elasticsearch - 使用聚合结果过滤另一个索引中的文档
问题描述
我有 2 个索引:
users
navigations
假设users
看起来像这样:
{
"properties": {
"cookies": {"type": "keyword"},
"name": {"type": "text"}
}
}
navigations
看起来像这样:
{
"properties": {
"url": {"type": "keyword"},
"cookie_id": {"type": "keyword"}
}
}
正如您所注意到的,users
并且navigations
可以通过cookie_id
andcookies
字段连接在一起。
实际上,我的索引有更多字段,但只有这些是证明我的问题所必需的。
我将users
and存储navigations
在 2 个不同的索引中,而不是使用join
映射或nested
映射,因为我将拥有navigations
比用户更多的内容,并且在我的大多数搜索用例中,我只会搜索users
,因此我不想维护navigations
per的列表users
。我更喜欢将它们分开(我还有一些其他限制因素促使我选择 2 个单独的索引,例如数据协调等......)。
我想做的是这样的查询/聚合:“给我所有name
Fabien
导航5次的用户url
http://example.com
”
到目前为止,我有以下查询/聚合(搜索查询是在我的 2 个索引上完成的):
发布 /users,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"
}
}
}
}
}
}
}
}
使用此查询,我可以过滤我users
的 with name = Fabien
,并且我可以从至少有 5 个文档 with 的地方获取cookie_id
值。navigations
url = http://example.com
但我不知道如何使用cookie_id
聚合中的 s 来过滤我的users
.
任何想法?
谢谢!
解决方案
具有两个独立索引的解决方案
由于 elasticsearch 不是关系数据库,因此您将无法在单个请求中检索结果。这是elasticsearch的一个很大的限制,但这也是它表现出色的一个主要原因。
基本上,elasticsearch 会将您的查询编译为 Lucene 查询,并使用 Lucene 查询执行索引扫描。没有机制使查询中的某些参数(例如user_id
字段的值)依赖于另一个查询的结果(例如,查找名称为“Fabien”的所有id
值)。users
您必须在外部执行连接:
users
首先,从name 为的索引中检索所有文档Fabien
。如果文档数量不受限制,则必须执行滚动搜索或使用search_after其次,从第一个请求返回的文档集中的索引
navigation
中检索所有文档,user_id
并且满足您的其他条件。
这种方法可能很慢,并且您无法保证在运行第二个查询时用户索引没有更新。
连接映射的解决方案
实际上,如果您使用连接类型映射,则不需要为您的用例使用聚合。
请注意,连接字段有几个限制,不建议将其作为建模一对多关系的默认解决方案。
这是一个可以满足您要求的工作示例。
映射:包含用户和导航字段以及连接字段。
PUT /user_navigation
{
"mappings": {
"properties": {
"cookies": {
"type": "keyword"
},
"name": {
"type": "keyword"
},
"join_field": {
"type": "join",
"relations": {
"user": "navigation"
}
}
}
}
}
添加一些测试文档。两个父文档有name: Fabien
,但只有一个有两个孩子cookies: http://example.com
。另一个文档有两个孩子,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 查询,将仅返回带有name: Fabien
和 的文档,这样它至少有两个带有 的子文档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"
参数允许更改必须满足请求的最小子文档数。
"inner_hits": {}
允许在响应中检索子文档。
推荐阅读
- java - Android - Paytm 支付网关集成问题:com.paytm.pg.AppTest 类型已定义多次
- spring-boot - Spring Boot RestTemplate WebClient - 响应正文 JSON 空
- javascript - 如何使用 CSS 创建运行文本(选取框)
- java - Java 中的枚举 VS C# 中的枚举
- .net - 替换 Tizen TV 上的 .NET WebView
- php - 如何将n个员工出勤数据存储在另一个表中作为任何其他列名
- api - 如何将搜索结果与数据库集成
- python - 让 python 与多个 android 模拟器交互
- javascript - Ajax 填充的选择元素在重新调整 json 的 ajax 数据库插入调用上相互冲突
- javascript - 使用 VSCode 和 Pupeteer 在 REPL 循环中评估 promise