对分析工作熟练的人,想必都用过 Excel 中的 VLOOKUP 函数,参考我的另一篇博客:VLOOKUP 函数跨工作表跨文件使用方式 ,但是目前已经被官方取消【2019 年末宣布】,转而使用 XLOOKUP。此外,可能很少有人知道,在 Elasticsearch 中也存在这样一个类似的查询接口:terms lookup,可以跨索引查询数据,看起来很是方便,本文简单介绍一下。开发环境基于 Elasticsearch v5.6.8。 在 Elasticsearch 中,是存在父子文档这种设置的,即在父索引中嵌套一个子索引,例如在主帖中嵌套用户的信息。这样查询主帖时,不仅可以同时指定用户的筛选条件,而且返回数据时可以连同用户信息一起返回,在使用层面很方便。 但是,这种存储方式显然冗余了大量的用户信息,如果数据量级很大,浪费了大量的存储空间,不可取。随着业务的数据增长,这种设计方式肯定要被淘汰掉,转而选择把父子文档拆分,此时如果还想使用类似父子文档的查询特性,可以选择跨索引查询,即 terms lookup。 例如查询某个用户关注列表中所有用户发表的主帖,如果是拆开查询,需要两步:先查出关注列表,再查询发表的主帖,而使用 terms lookup 查询只需要一步即可。但是这种方式有诸多的限制,下面会举例说明,我想这也是为了性能考虑。 其实,terms lookup 是一个查询过滤器【filter,从 v0.90 开始引进】,只不过不需要用户指定 filter、filtered 等关键字,所以用户使用起来也无感知。 参考官方文档:query-dsl-terms-query 。 读者在继续往下阅读之前可以先去了解一下与 缓存 相关的知识点,例如:如何开启、如何关闭、删除缓存、自定义命名、什么场景应该使用、哪些查询不会被缓存。 我在这里使用两个典型的场景进行演示,一个可以支持,另一个不能支持。假如有 2 个索引:用户 my-index-user、主帖 my-index-post,它们之间是通过用户的 id 来进行关联的【在用户索引中字段为 item_id,在主帖索引中字段为 user_item_id】,即用户可以任意发表主帖。 查询某个用户的关注列表中所有用户发表的主帖。【可以支持】 查询思路分两个步骤,一是利用 item_id 查询用户索引,返回关注列表 friends 中的所有 item_id;二是利用一步骤中返回的 itemt_id 列表,去匹配主帖的 user_item_id 字段,从而查询所有的主帖。 转换为 terms lookup 查询为: 可以看到查询出来 2743 条主帖,这样的查询方式是不是很方便呢。 下面可以拆分步骤简单验证一下,先查询用户索引,把关注列表查出来: 查询出生日期是 1994 年的所有用户发表的主帖。【不可以支持】 查询思路分两个步骤,一是利用 birth_year 查询用户索引,返回满足条件的所有 item_id;二是利用一步骤中返回的 itemt_id 列表,去匹配主帖的 user_item_id 字段,从而查询所有的主帖。 转换为 terms lookup 查询为: 可以看到,报错了:[terms] query does not support [birth_year] within lookup element,想象很美好,现实很残酷,其实 Elasticsearch 并不支持对用户索引使用非 id 字段条件,也就是指定内层的索引条件,只能是和 id 有关的,这也是一种限制。 terms 个数限制,对于场景一来说,如果关注列表中的 item_id 过多,也会导致查询主帖的 terms 匹配失败,因为 terms 查询是有个数限制的。可以通过配置更改,设置 terms 最大个数:index.max_terms_count,默认最大个数为 65535,可以根据集群情况降低,例如设置为 10000,为了集群稳定,一般不需要设置那么大。 内层返回字段需要存储,对于场景一来说,如果关注列表 friends 字段没有存储【stored_fields 属性】,只是做了索引,也是无法支持的,会报错:[terms] query does not support [friends] within lookup element。 https://www.playpi.org/2019060601.html接口介绍
演示示例
场景一
POST my-index-post/_search
{
"query": {
"bool": {
"must": [
{
"terms": {
"user_item_id": {
"index": "my-index-user",
"type": "user",
"id": "0f42d65be1f5287e1c9c26e3728814aa",
"path": "friends"
}
}
}
]
}
}
}
POST my-index-user/user/_search
{
"query": {
"bool": {
"must": [
{
"terms": {
"id": [
"0f42d65be1f5287e1c9c26e3728814aa"
]
}
}
]
}
},
"_source": [
"item_id",
"friends",
"birth_year"
]
}
场景二
POST my-index-post/_search
{
"query": {
"bool": {
"must": [
{
"terms": {
"user_item_id": {
"index": "my-index-user",
"type": "user",
"birth_year": 1994,
"path": "item_id"
}
}
}
]
}
}
}
引申说明
对于场景二来说,表现的就是 terms lookup 无法支持复杂的查询条件,只能是和 id 字段有关的,这样就降低了 Elasticsearch 的计算量。原链接