使用聚合结果过滤另一个索引中的文档
Using results of an aggregation to filter documents in another index
我有 2 个索引:
users
navigations
假设 users
看起来像这样:
{
"properties": {
"cookies": {"type": "keyword"},
"name": {"type": "text"}
}
}
而 navigations
看起来像这样:
{
"properties": {
"url": {"type": "keyword"},
"cookie_id": {"type": "keyword"}
}
}
如您所见,users
和 navigations
可以通过 cookie_id
和 cookies
字段连接在一起。
实际上我的索引有更多的字段,但只有这些是证明我的问题所必需的。
我将 users
和 navigations
存储在 2 个不同的索引中,而不是使用 join
映射或 nested
映射,因为我会有更多navigations
比用户多,在我的大多数搜索用例中,我只会搜索 users
,因此我不想维护每个 users
的 navigations
列表.我更喜欢将它们分开(我还有一些其他限制因素促使我选择 2 个单独的索引,例如数据协调等...)。
我想做的是 query/aggregation 这样的:"give me all users with name
Fabien
that navigated 5 times on url
http://example.com
"
到目前为止,我有以下 query/aggregation(搜索查询是在我的 2 个索引上完成的):
POST /用户,navigations/_search
{
"query": {
"bool": {
"must": [
{"match": {"name": "Fabien"}}
]
}
},
"aggregations": {
"all_navs": {
"global": {},
"aggregations": {
"cookies": {
"terms": {"field": "cookie_id"},
"aggregations": {
"page_visited": {
"filter": {
"bool": {
"must": [
{"term": {"url": "http://example.com"} }
]
}
},
"aggregations": {
"number_page_visited": {
"value_count": {"field": "type"}
}
}
},
"count_filter": {
"bucket_selector": {
"buckets_path": {
"count": "page_visited>number_page_visited"
},
"script": "params.count > 5"
}
}
}
}
}
}
}
}
通过此查询,我可以使用 name = Fabien
过滤我的 users
,并且我可以从 navigations
中获取 cookie_id
值,其中至少有 5 个文档具有 url = http://example.com
.
但我不知道如何使用聚合中的 cookie_id
来过滤我的 users
。
有什么想法吗?
谢谢!
具有两个独立索引的解决方案
因为 elasticsearch 不是关系数据库,您将无法在单个请求中检索结果。这是elasticsearch的一个很大的局限性,但也是它表现出色的一个重要原因。
基本上,elasticsearch 会将您的查询编译成 Lucene 查询,并使用 Lucene 查询执行索引扫描。没有机制使查询中的某些参数(例如 user_id
字段的值)依赖于另一个查询的结果(例如,从 users
中找到所有 id
值,其中名字是 "Fabien").
您必须在外部执行连接:
首先,从名称为 Fabien
的索引 users
中检索所有文档。如果文档数量不受限制,则必须执行 scroll search or use search_after
其次,从索引 navigation
检索所有文档,其中 user_id
在第一个请求 return 的文档集中,您的其他条件是满意。
这种方法可能很慢,而且您无法保证在 运行 第二次查询时用户索引没有更新。
连接映射的解决方案
实际上,如果您使用 join type mapping,则不需要为您的用例使用聚合。
请注意,连接字段有 several restriction,不推荐作为一对多关系建模的默认解决方案。
这是一个可以满足您要求的工作示例。
映射:包含用户和导航字段以及一个连接字段。
PUT /user_navigation
{
"mappings": {
"properties": {
"cookies": {
"type": "keyword"
},
"name": {
"type": "keyword"
},
"join_field": {
"type": "join",
"relations": {
"user": "navigation"
}
}
}
}
}
添加一些测试文件。两个 parent 文档有 name: Fabien
,但只有一个有两个 children 和 cookies: http://example.com
。另一个文件有两个 children 和 cookies: http://example.com
但没有用 Fabien
.
命名
POST user_navigation/_doc/_bulk
{ "index" : { "_index" : "user_navigation", "_id" : "1" } }
{ "name" : "Fabien", "join_field": "user" }
{ "index" : { "_index" : "user_navigation", "_id" : "2" } }
{ "name" : "Fabien", "join_field": "user" }
{ "index" : { "_index" : "user_navigation", "_id" : "3" } }
{ "name" : "Autre", "join_field": "user" }
{ "index" : { "_index" : "user_navigation", "routing": "1" } }
{ "cookies": "http://example.com", "join_field": { "name": "navigation", "parent": "1" }}
{ "index" : { "_index" : "user_navigation", "routing": "1"} }
{ "cookies": "http://example.com", "join_field": { "name": "navigation", "parent": "1" }}
{ "index" : { "_index" : "user_navigation", "routing": "2"} }
{ "cookies": "http://example.com", "join_field": { "name": "navigation", "parent": "2" }}
{ "index" : { "_index" : "user_navigation", "routing": "2"} }
{ "cookies": "other_url", "join_field": { "name": "navigation", "parent": "3" }}
{ "index" : { "_index" : "user_navigation", "routing": "3"} }
{ "cookies": "http://example.com", "join_field": { "name": "navigation", "parent": "3" }}
{ "index" : { "_index" : "user_navigation", "routing": "3"} }
{ "cookies": "http://example.com", "join_field": { "name": "navigation", "parent": "3" }}
以下请求使用 has_child query 并将 return 仅包含 name: Fabien
的文档,并且它至少有两个 children 包含 cookies: http://example.com
的文档.
GET user_navigation/_doc/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"name": "Fabien"
}
},
{
"has_child": {
"type": "navigation",
"query": {
"term": {
"cookies": "http://example.com"
}
},
"min_children": 2,
"inner_hits": {}
}
}
]
}
}
}
响应将仅包含 ID 为 1 的文档。
"min_children"
参数允许更改必须满足请求的最小 children 文档数。
"inner_hits": {}
允许在响应中检索 children 文档。
我有 2 个索引:
users
navigations
假设 users
看起来像这样:
{
"properties": {
"cookies": {"type": "keyword"},
"name": {"type": "text"}
}
}
而 navigations
看起来像这样:
{
"properties": {
"url": {"type": "keyword"},
"cookie_id": {"type": "keyword"}
}
}
如您所见,users
和 navigations
可以通过 cookie_id
和 cookies
字段连接在一起。
实际上我的索引有更多的字段,但只有这些是证明我的问题所必需的。
我将 users
和 navigations
存储在 2 个不同的索引中,而不是使用 join
映射或 nested
映射,因为我会有更多navigations
比用户多,在我的大多数搜索用例中,我只会搜索 users
,因此我不想维护每个 users
的 navigations
列表.我更喜欢将它们分开(我还有一些其他限制因素促使我选择 2 个单独的索引,例如数据协调等...)。
我想做的是 query/aggregation 这样的:"give me all users with name
Fabien
that navigated 5 times on url
http://example.com
"
到目前为止,我有以下 query/aggregation(搜索查询是在我的 2 个索引上完成的):
POST /用户,navigations/_search
{
"query": {
"bool": {
"must": [
{"match": {"name": "Fabien"}}
]
}
},
"aggregations": {
"all_navs": {
"global": {},
"aggregations": {
"cookies": {
"terms": {"field": "cookie_id"},
"aggregations": {
"page_visited": {
"filter": {
"bool": {
"must": [
{"term": {"url": "http://example.com"} }
]
}
},
"aggregations": {
"number_page_visited": {
"value_count": {"field": "type"}
}
}
},
"count_filter": {
"bucket_selector": {
"buckets_path": {
"count": "page_visited>number_page_visited"
},
"script": "params.count > 5"
}
}
}
}
}
}
}
}
通过此查询,我可以使用 name = Fabien
过滤我的 users
,并且我可以从 navigations
中获取 cookie_id
值,其中至少有 5 个文档具有 url = http://example.com
.
但我不知道如何使用聚合中的 cookie_id
来过滤我的 users
。
有什么想法吗?
谢谢!
具有两个独立索引的解决方案
因为 elasticsearch 不是关系数据库,您将无法在单个请求中检索结果。这是elasticsearch的一个很大的局限性,但也是它表现出色的一个重要原因。
基本上,elasticsearch 会将您的查询编译成 Lucene 查询,并使用 Lucene 查询执行索引扫描。没有机制使查询中的某些参数(例如 user_id
字段的值)依赖于另一个查询的结果(例如,从 users
中找到所有 id
值,其中名字是 "Fabien").
您必须在外部执行连接:
首先,从名称为
Fabien
的索引users
中检索所有文档。如果文档数量不受限制,则必须执行 scroll search or use search_after其次,从索引
navigation
检索所有文档,其中user_id
在第一个请求 return 的文档集中,您的其他条件是满意。
这种方法可能很慢,而且您无法保证在 运行 第二次查询时用户索引没有更新。
连接映射的解决方案
实际上,如果您使用 join type mapping,则不需要为您的用例使用聚合。
请注意,连接字段有 several restriction,不推荐作为一对多关系建模的默认解决方案。
这是一个可以满足您要求的工作示例。
映射:包含用户和导航字段以及一个连接字段。
PUT /user_navigation
{
"mappings": {
"properties": {
"cookies": {
"type": "keyword"
},
"name": {
"type": "keyword"
},
"join_field": {
"type": "join",
"relations": {
"user": "navigation"
}
}
}
}
}
添加一些测试文件。两个 parent 文档有 name: Fabien
,但只有一个有两个 children 和 cookies: http://example.com
。另一个文件有两个 children 和 cookies: http://example.com
但没有用 Fabien
.
POST user_navigation/_doc/_bulk
{ "index" : { "_index" : "user_navigation", "_id" : "1" } }
{ "name" : "Fabien", "join_field": "user" }
{ "index" : { "_index" : "user_navigation", "_id" : "2" } }
{ "name" : "Fabien", "join_field": "user" }
{ "index" : { "_index" : "user_navigation", "_id" : "3" } }
{ "name" : "Autre", "join_field": "user" }
{ "index" : { "_index" : "user_navigation", "routing": "1" } }
{ "cookies": "http://example.com", "join_field": { "name": "navigation", "parent": "1" }}
{ "index" : { "_index" : "user_navigation", "routing": "1"} }
{ "cookies": "http://example.com", "join_field": { "name": "navigation", "parent": "1" }}
{ "index" : { "_index" : "user_navigation", "routing": "2"} }
{ "cookies": "http://example.com", "join_field": { "name": "navigation", "parent": "2" }}
{ "index" : { "_index" : "user_navigation", "routing": "2"} }
{ "cookies": "other_url", "join_field": { "name": "navigation", "parent": "3" }}
{ "index" : { "_index" : "user_navigation", "routing": "3"} }
{ "cookies": "http://example.com", "join_field": { "name": "navigation", "parent": "3" }}
{ "index" : { "_index" : "user_navigation", "routing": "3"} }
{ "cookies": "http://example.com", "join_field": { "name": "navigation", "parent": "3" }}
以下请求使用 has_child query 并将 return 仅包含 name: Fabien
的文档,并且它至少有两个 children 包含 cookies: http://example.com
的文档.
GET user_navigation/_doc/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"name": "Fabien"
}
},
{
"has_child": {
"type": "navigation",
"query": {
"term": {
"cookies": "http://example.com"
}
},
"min_children": 2,
"inner_hits": {}
}
}
]
}
}
}
响应将仅包含 ID 为 1 的文档。
"min_children"
参数允许更改必须满足请求的最小 children 文档数。
"inner_hits": {}
允许在响应中检索 children 文档。