译文:反向搜索Netflix的联合图

以云看科技 2024-09-07 02:19:08

自从我们之前发布关于内容工程在 Netflix 联合图谱中实现搜索功能的作用的文章(第一篇文章,我们在其中确定了问题并详细阐述了索引架构,第二篇文章,我们在其中详细介绍了如何促进查询)以来,我们已经取得了重大进展。我们向 Netflix 的整个工程部门开放了内容工程之外的 Studio Search,并将其重命名为 Graph Search。有超过 100 个应用程序与 Graph Search 集成,我们支持近 50 个索引。我们继续为该服务添加功能。正如上一篇文章中所承诺的,我们将分享我们如何与我们的其中一个 Studio 工程团队合作构建反向搜索。反向搜索颠倒了标准查询模式:它不是查找与查询匹配的文档,而是查找与文档匹配的查询。

简介

蒂芙尼是 Netflix 的后期制作协调员,负责监督近十几部处于前期制作、制作和后期制作不同阶段的电影。蒂芙尼和她的团队与各种跨职能合作伙伴(包括法律、创意和片头发行管理)合作,跟踪她的电影的进展和健康状况。

因此,蒂芙尼订阅了针对某些关注领域的通知和日历更新,例如“在墨西哥城拍摄的没有分配关键角色的电影”或“面临无法在上映日期前准备好的风险的电影”。

Tiffany 并没有订阅特定电影的更新,而是订阅了返回动态电影子集的查询。这给我们这些负责向她发送通知的人带来了问题。当电影发生变化时,我们不知道该通知谁,因为员工与他们感兴趣的电影之间没有任何关联。

我们可以保存这些搜索,然后反复查询每个搜索的结果,但由于我们属于一个大型联合图的一部分,这会对我们连接的每个服务造成很大的流量影响。我们必须决定是希望及时通知还是希望减少图上的负载。

如果我们可以回答“这部电影是否会通过此查询返回”这个问题,我们可以根据变化事件以激光精度重新查询,而不会影响更广泛的生态系统。

解决方案

Graph Search 建立在 Elasticsearch 之上,它具有我们所需的确切功能:

可用于索引 Elasticsearch 查询的过滤器字段渗透查询可用于确定哪些索引查询与输入文档匹配。

渗透查询不是进行搜索(例如“在墨西哥城拍摄的西班牙语电影”)并返回匹配的文档(一个用于罗马,一个用于家庭),而是获取一个文档(一个用于罗马)并返回与该文档匹配的搜索,例如“西班牙语电影”和“剧本剧”。

我们将此功能传达为保存搜索的能力,称为SavedSearches,它是现有索引上的持久过滤器。

type SavedSearch { id: ID! filter: String index: SearchIndex!}

该过滤器以 Graph Search DSL 编写,转换为 Elasticsearch 查询并在过滤器字段中编入索引。要了解有关 Graph Search DSL 的更多信息以及我们为什么创建它而不是直接使用 Elasticsearch 查询语言,请参阅“Netflix 内容工程如何使联合图可搜索(第 2 部分)”的查询语言部分。

我们将查找匹配的已保存搜索的过程称为ReverseSearch。这是此产品最直接的部分。我们为图谱搜索的域图谱服务(DGS) 添加了一个新的解析器。它获取感兴趣的索引和文档,并通过发出过滤查询返回与文档匹配的所有已保存搜索。

"""Query for retrieving all the registered saved searches, in a given index,based on a provided document. The document in this case is an ElasticSearchdocument that is generated based on the configuration of the index."""reverseSearch( after: String, document: JSON!, first: Int!, index: SearchIndex!): SavedSearchConnection

持久化SavedSearch是作为 Graph Search DGS 上的一项新突变实现的。这最终会触发在过滤器字段中对 Elasticsearch 查询进行索引。

"""Mutation for registering and updating a saved search. They need to be updatedany time a user adjusts their search criteria."""upsertSavedSearch(input: UpsertSavedSearchInput!): UpsertSavedSearchPayload

支持过滤器字段从根本上改变了我们为 Graph Search 配置索引管道的方式(请参阅 Netflix 内容工程如何使联合图可搜索的架构部分)。每个 Graph Search 索引不再只有一个索引管道,现在我们有两个:一个用于索引文档,另一个用于将已保存的搜索索引到过滤器索引。我们选择将过滤器字段添加到单独的索引中,以便分别调整两种查询类型的性能。

Elasticsearch 要求渗透索引具有与其存储的查询结构相匹配的映射,因此必须与文档索引的映射相匹配。索引模板定义在创建新索引时应用的​映射。通过使用索引模板的index_patterns功能,我们能够在两者之间共享文档索引的映射。index_patterns 还为我们提供了一种简单的方法来向我们创建的每个渗透索引添加渗透器字段。

文档索引映射示例

索引模式 — application_*

{ “order” : 1 , “index_patterns” : [ “application_*” ] , “mappings” : { “properties” : { “movieTitle” : { “type” : “keyword” } , “isArchived” : { “type” : “boolean” } } }

渗透液索引映射示例

索引模式——*_percolate

{ “order” : 2 , “index_patterns” : [ “*_percolate*” ] , “mappings” : { “properties” : { “percolate_query” : { “type” : “percolator” } } } }

生成的映射示例

Percolate 索引名称是 application_v1_percolate

{ “application_v1_percolate” : { “mappings” : { “_doc” : { “properties” : { “movieTitle” : { “type” : “keyword” } , “isArchived” : { “type” : “boolean” } , “percolate_query” : { “type” : “percolator” } } } } } }渗透索引管道

渗透索引并不像从 GraphQL 变异中获取输入、将其转换为 Elasticsearch 查询并对其进行索引那么简单。版本控制(我们稍后会详细讨论)出现了,并使事情变得更加复杂。以下是渗透索引管道的设置方式。

请参阅 Data Mesh — Netflix 的数据移动和处理平台,了解有关 Data Mesh 的更多信息。

当SavedSearches修改时,我们将它们存储在我们的 CockroachDB 中,并且 Cockroach 数据库的源连接器会发出 CDC 事件。单个表用于存储所有内容SavedSearches,因此下一步是使用过滤处理器过滤出仅针对*此*索引的内容。如前所述,数据库中存储的是我们的自定义 Graph Search 过滤器 DSL,它与 Elasticsearch DSL 不同,因此我们无法直接将事件索引到渗透索引中。相反,我们向 Graph Search DGS 发出突变。Graph Search DGS 将 DSL 转换为 Elasticsearch 查询。然后我们将 Elasticsearch 查询作为适当的渗透索引中的渗透字段进行索引。返回索引编制成功或失败的信息SavedSearch。如果失败,事件SavedSearch将发送到死信队列 (DLQ),该队列可用于解决任何故障,例如搜索查询中引用的字段从索引中删除。

现在来谈谈版本控制,以解释为什么上述内容是必要的。假设我们已经开始标记有动物的电影。如果我们希望用户能够创建“有动物的电影”视图,我们需要将这个新字段添加到现有的搜索索引中,以标记此类电影。但是,当前索引中的映射不包括它,因此我们无法对其进行过滤。为了解决这个问题,我们有索引版本。

系列《动物宝宝摄像机》中的达莉亚和福雷斯特

当对索引定义进行更改而需要新的映射时(例如当我们添加动物标签时),Graph Search 会创建新版本的 Elasticsearch 索引和新的管道来填充它。这个新管道从 Data Mesh 中经过日志压缩的 Kafka 主题中读取数据 — 这就是我们可以重新索引整个语料库而无需要求数据源重新发送所有旧事件的方法。新管道和旧管道并行运行,直到新管道处理完积压数据,此时 Graph Search 会切换到使用 Elasticsearch 索引别名的版本。

为我们的文档创建新索引意味着我们还需要为我们的查询创建新的渗透索引,以便它们具有一致的索引映射。当我们更改版本时,这个新的渗透索引也需要回填。这就是管道以这种方式工作的原因——SavedSearches当我们启动新的渗透索引管道时,我们可以再次利用数据网格中的日志压缩主题来重新索引语料库。

我们将用户提供的过滤器 DSL 保存到数据库,而不是立即将其转换为 Elasticsearch 查询语言。这使我们能够在将保存的搜索 DSL 转换为 Elasticsearch 查询时进行更改或修复。我们可以通过创建新版本的索引来部署这些更改,因为引导过程将重新翻译每个保存的搜索。

另一个用例

我们希望反向搜索功能最终能对其他工程团队有用。我们几乎立即就遇到了一个可以用反向搜索解决的问题。

根据电影的类型,制作电影的方式可能大不相同。一部电影可能会经历一系列不适用于另一部电影的阶段,或者可能需要安排另一部电影不需要的某些事件。我们应该能够定义电影分类的方法,并利用该方法自动将电影分配给工作流,而不是根据电影的分类手动配置工作流。但确定电影的分类具有挑战性:您可以仅根据类型来定义这些电影分类,例如“动作”或“喜剧”,但您可能需要更复杂的定义。可能是根据类型、地区、格式、语言或其中一些细微的组合来定义的。电影匹配服务提供了一种基于任何匹配条件组合对电影进行分类的方法。在底层,匹配条件存储为反向搜索,为了确定电影符合哪些条件,电影的文档将提交到反向搜索端点。

简而言之,反向搜索为外部化标准匹配器提供支持。它现在用于电影标准,但由于每个 Graph Search 索引现在都具有反向搜索功能,因此任何索引都可以使用此模式。

可能的未来:订阅

反向搜索看起来也是创建响应更快的 UI 的有前途的基础。搜索结果可以通过 GraphQL 订阅提供,而不是作为查询一次获取结果。这些订阅可以与一个相关联,SavedSearch并且当索引发生变化时,可以使用反向搜索来确定何时更新订阅返回的键集。

作者:Ricky Gardiner、Alex Hutter和Katie Lefevre

出处:https://netflixtechblog.com/reverse-searching-netflixs-federated-graph-222ac5d23576

0 阅读:3

以云看科技

简介:感谢大家的关注