Join 查询解析器
Join 查询解析器允许用户运行查询,以规范化文档之间的关系。
Solr 运行用户选择的子查询(v
参数),识别所有匹配文档在感兴趣的字段(from
参数)中具有的所有值,然后返回这些值包含在第二个感兴趣的字段(to
参数)中的文档。
在实践中,这些语义非常类似于 SQL 引擎中的“内部查询”。例如,考虑下面的 Solr 查询
/solr/techproducts/select?q={!join from=manu_id_s to=id}title:ipod
此查询返回每个制造商的文档,这些制造商生产标题中包含 "ipod" 的产品,其语义与下面的 SQL 查询相同
SELECT *
FROM techproducts
WHERE id IN (
SELECT manu_id_s
FROM techproducts
WHERE title='ipod'
)
join 操作是在词项基础上完成的,因此 from
和 to
字段必须使用兼容的字段类型。例如:在 StrField
和 IntPointField
之间进行连接将不起作用。同样,在 StrField
和使用 LowerCaseFilterFactory
的 TextField
之间进行连接,仅适用于字符串字段中已转换为小写的值。
参数
此查询解析器采用以下参数
from
-
必需
默认值:无
包含要在
to
字段中查找的值的字段的名称。可以是单值或多值,但必须具有与to
字段中表示的字段兼容的字段类型。 to
-
必需
默认值:无
要检查其值以匹配
from
字段中找到的值的字段的名称。可以是单值或多值,但必须具有与from
字段兼容的字段类型。 fromIndex
-
可选
默认值:请参阅描述
score
-
可选
默认值:请参阅描述
指示 Solr 返回有关 "from" 查询分值的信息。此参数的值控制返回的聚合信息类型。选项包括
avg
(平均值)、max
(最大值)、min
(最小值)、total
(总计)或none
(无)。如果未指定
method
但指定了score
,则使用dvWithScore
方法。如果指定了method
并且不是dvWithScore
,则忽略score
值。有关更多详细信息,请参阅下面的method
参数文档。 method
-
可选
默认值:请参阅描述
确定 Solr 应使用哪种查询实现。选项仅限于:
index
、dvWithScore
和topLevelDV
。如果未指定,则默认值为
index
,除非存在score
参数,否则会将其覆盖为dvWithScore
。每种实现都有其自身的性能特征,建议用户进行试验,以确定哪种实现最适合其用例。下面提供了详细信息和性能启发式方法。index
-
除非指定了
score
参数,否则默认的method
。它使用术语索引结构来处理请求。性能随 "from" 字段的基数和帖子数(术语出现次数)而变化。当 "from" 字段的基数较低、"to" 侧返回大量文档或者无法容忍零星的提交后减速时(这是index
避免的其他方法的缺点),请考虑此方法。 dvWithScore
-
返回结果文档旁边的可选 "score" 统计信息。如果可用,它会使用 docValues 结构,但在必要时会回退到字段缓存。首次访问字段缓存会减慢提交后初始请求的速度,并占用 JVM 堆上的额外空间,因此在大多数情况下建议使用 docValues。性能与 "from" 字段中匹配的值的数量线性缩放。如果需要分值信息,则必须使用此方法,并且当 "from" 查询匹配的文档很少时,也应考虑使用此方法,而不管返回的 "to" 侧文档数量如何。
dvWithScore 和单值数字dvWithScore
方法不支持单值数字字段。鼓励从 7.0 之前的版本迁移的用户在迁移期间将字段类型更改为字符串并重建索引。 topLevelDV
-
只能在
to
和from
字段具有 docValues 数据时使用,并且目前不支持数字字段。它使用顶级 docValues 数据结构来查找结果。随着from
字段中匹配的值的数量的增加,这些数据结构的性能优于其他方法。但是,它们的构建成本也很高,并且需要在每次提交后延迟填充,从而导致每次提交后第一次使用它们的查询出现有时明显的减速。如果频繁提交并且您的用例可以容忍静态预热查询,请考虑在solrconfig.xml
中添加一个,以便此工作作为提交本身的一部分完成,而不是直接附加到用户请求。当 "from" 查询匹配大量文档并且 "to" 结果集的大小为小到中等时,请考虑此方法,但前提是偶尔的提交后缓慢是可以容忍的。
跨单分片集合连接
您还可以指定 fromIndex
参数以与另一个核心或单分片集合的字段连接。如果在 SolrCloud 模式下运行,则 fromIndex
参数中指定的集合必须具有单个分片,并且在要加入的集合具有副本的所有 Solr 节点上都有一个副本。
让我们考虑一个示例,其中您想使用 Solr 连接查询来按获得奥斯卡奖的导演过滤电影。具体来说,假设我们有两个具有以下字段的集合
movies:id、title、director_id、…
movie_directors:id、name、has_oscar、…
要使用 movie_directors 集合上的 Solr 连接按获得奥斯卡奖的导演过滤电影,您可以向 movies 集合发送以下过滤器查询
fq={!join from=id fromIndex=movie_directors to=director_id}has_oscar:true
请注意,过滤器 (has_oscar:true
) 的查询条件基于使用 fromIndex
指定的集合中的字段。请记住,您不能使用连接查询从 fromIndex
集合返回字段,您只能使用这些字段来过滤 "to" 集合(电影)中的结果。
接下来,让我们了解如何在集群中部署这些集合。假设 movies 集合部署到四个节点的 SolrCloud 集群,并且有两个分片,复制因子为 2。具体来说,movies 集合在以下四个节点上具有副本
节点 1:movies_shard1_replica1
节点 2:movies_shard1_replica2
节点 3:movies_shard2_replica1
节点 4:movies_shard2_replica2
要在 Solr 连接查询中将 movie_directors 集合与 movies 集合一起使用,它需要在四个节点中的每一个节点上都有一个副本。换句话说,movie_directors 必须有一个分片,复制因子为 4
节点 1:movie_directors_shard1_replica1
节点 2:movie_directors_shard1_replica2
节点 3:movie_directors_shard1_replica3
节点 4:movie_directors_shard1_replica4
在查询时,JoinQParser
将访问 movie_directors 集合的本地副本以执行连接。如果本地副本不可用或未激活,则查询将失败。此时,应该很清楚,由于您仅限于单个分片,并且数据必须在需要它的所有节点上复制,因此这种方法在 "from" 集合和 "to" 集合之间存在一对多关系的情况下,更适合较小的数据集。此外,如果向 "to" 集合添加副本,则还需要为 "from" 集合添加副本。
有关更多信息,Erick Erickson 撰写了一篇关于连接性能的博客文章,标题为Solr 和连接。
连接多个分片集合
也可以连接具有
-
相同数量的分片
-
两个集合的
router.name
相同 -
router.field
或uniqueKey
应与to
、from
参数中使用的字段相对应(请参阅下面的详细信息和例外情况) -
集合的分片是并置的(请参阅下面的示例)
为了简化,我们使用 "from" "多" 连接 "to" "一" 的示例。但是,相同的方法也可以在其他情况下使用。
"to" 集合 | "from" 集合 | |
---|---|---|
节点 1 |
products_shard1_replica1 |
skus_shard1_replica2 |
节点 2 |
products_shard1_replica2 |
skus_shard1_replica1 |
节点 3 |
products_shard2_replica1 |
skus_shard2_replica1 |
节点 4 |
products_shard2_replica2 |
skus_shard2_replica2 |
请注意 shardN 如何对应于来自另一侧集合的对应项。使用 AffinityPlacementPlugin.withCollectionShards 对齐集合分片。
以下是支持的集合路由选项
router.name |
字段约束 |
---|---|
|
|
|
|
|
可以通过 |
注意:第三种情况假设索引器在此情况下分配 sku.id=<product.id>!<sku_id>
,compositeId
逻辑将产品的 sku 放入与其产品同名的分片中。此外,您可以关闭 checkRouterField=false
并通过 _route_
伪字段手动放置每个文档。
例如,如果 skus
集合具有 router.field=product_id
,我们可以通过 q={!join to=id from=product_id fromIndex=skus}color:red
找到红色 sku 的产品。
跨集合连接
跨集合连接过滤器是一种用于连接解析器的方法,它将针对远程 Solr 集合执行查询,以取回一组将用作针对本地 Solr 集合的过滤器查询的连接键。
跨集合连接查询将创建一个 CrossCollectionQuery
对象。CrossCollectionQuery
将首先查询远程 Solr 集合,并取回连接键的流式表达式结果。当连接键流式传输到节点时,将建立本地索引中匹配文档的位集。这避免了在任何给定时间将所有连接键保存在内存中。然后,与 Solr 过滤器缓存的正常行为一样,在成功执行后,此位集将插入到过滤器缓存中。
如果本地索引是根据连接键字段进行分片的,则跨集合连接可以利用称为哈希范围查询解析器的辅助查询解析器。哈希范围查询解析器负责仅返回哈希到给定值范围的文档。这允许 CrossCollectionQuery
查询远程 Solr 集合,并且仅返回将与本地 Solr 集合中的特定分片匹配的连接键。这样做的好处是确保网络流量不会随着分片数量的增加而增加,并且可以实现更大的可伸缩性。
跨集合连接查询适用于字符串和点类型的字段。用于连接键的字段必须是单值的,并且启用 docValues。
建议按连接键对本地集合进行分片,因为这允许利用上面提到的优化。
跨集合连接查询通常不应作为 q
参数的一部分使用。它旨在用作过滤器查询 (fq
参数),以确保正确缓存。
被查询的远程 Solr 集合应具有一个单值字段,用于启用 docValues 的连接键。
远程 Solr 集合没有任何特定的分片要求。
solrconfig.xml 中的连接查询解析器定义
跨集合连接具有一些可以在 solrconfig.xml
中指定的配置选项。
routerField
-
可选
默认值:无
如果文档是通过连接字段使用 CompositeID 路由器路由到分片的,则应在此处的配置中指定该字段名称。这将允许解析器优化生成的 HashRange 查询。
allowSolrUrls
-
可选
默认值:无
如果指定,则此字符串数组指定允许传递给
solrUrl
查询参数的允许列表 Solr URL。如果没有此配置,则无法使用solrUrl
参数。此限制是必要的,以防止攻击者使用 Solr 来探索网络。
<queryParser name="join" class="org.apache.solr.search.JoinQParserPlugin">
<str name="routerField">product_id_s</str>
<arr name="allowSolrUrls">
<str>http://othersolr.example.com:8983/solr</str>
</arr>
</queryParser>
跨集合连接查询参数
fromIndex
-
必需
默认值:无
要查询以检索一组连接键值的外部 Solr 集合的名称。
zkHost
-
可选
默认值:无
用于连接 ZooKeeper 的连接字符串。
zkHost
和solrUrl
都是可选参数,最多只能指定其中一个。 如果既没有指定zkHost
也没有指定solrUrl
,则将使用本地 ZooKeeper 集群。 solrUrl
-
可选
默认值:无
要查询的外部 Solr 节点的 URL。 必须与
solrconfig.xml
中的allowSolrUrls
参数中列出的允许列表中的 URL 完全匹配(字符对字符)。 如果 URL 不匹配,此参数将实际上被禁用。 from
-
必需
默认值:无
外部集合中的连接键字段名称。
to
-
可选
默认值:无
本地集合中的连接键字段名称。
v
-
可选
默认值:无
作为本地参数替换的查询。 这是将匹配远程集合中文档的查询字符串。
routed
-
可选
默认值:
false
如果为
true
,跨集合连接查询将使用每个分片的分片哈希范围来确定要为该分片检索的连接键集合。 此参数提高了跨集合连接的性能,但它依赖于本地集合按to
字段进行路由。 如果未指定此参数,跨集合连接查询将尝试自动确定正确的值。 ttl
-
可选
默认值:
3600
秒缓存中跨集合连接查询被认为有效的时长,以秒为单位。 跨集合连接查询不会感知到远程集合的更改,因此如果远程集合被更新,缓存的跨集合查询可能会给出不准确的结果。 在
ttl
期限到期后,跨集合连接查询将针对远程集合重新执行连接。 - 其他参数
-
任何普通的 Solr 查询参数也可以被指定/作为本地参数传递。