下面的json是https://www.elastic.co/blog/top-hits-aggregation/
中的示例{
"query":{
"match":{
"body":"web"
}
},
"size":1,
"aggs":{
"top-programming-languages":{
"terms":{
"field":"tags",
"include":"java|javascript|python|php|python|ruby|perl|c#",
"size":10,
"order":{
"max_score":"desc"
}
},
"aggs":{
"top-questions":{
"top_hits":{
"size":1
}
},
"max_score":{
"max":{
"lang":"expression",
"script":"doc.score"
}
}
}
}
}
}
我只是不明白这里的逻辑。
首先执行以下查询,并且仅返回一个结果。
"query":{
"match":{
"body":"web"
}
},
"size":1,
然后接下来的top-programming-languages
聚合仅对这一条记录起作用吗?
在top-programming-languages -> term -> order
中,max_score
是必需的,但在top-programming-languages -> aggs -> max_score
聚合完成之前它不存在。那么怎么可能执行top-programming-languages -> term
?
似乎ES使用了反直觉的执行顺序,但我未能在其文档中找到解释。
有人可以帮我吗? 预先感谢!
答案 0 :(得分:0)
执行查询并匹配N个文档。
从这N个文档中,仅返回一个文档,即得分最高的文档(因为它是一个match
查询,因此排名最高)。
然后,对查询匹配的所有N个文档(即不仅在返回的文档上)运行汇总,并且从所有这些文档汇总tags
在索引的每个分片上,对标签进行存储和排序,使得包含最高doc.score
文档的标签排在第一位。对于每个存储段,最上面的文档位于top-questions
子聚合中。最后,每个分片的前10个存储桶将返回到协调节点。但是,由于您仅包含7个标记(python被列出两次),因此每个分片可能只有7个存储桶。
然后,协调节点将它们组合在一起,并根据max_score
对所有分片中的所有标签桶进行重新排序,并且仅获取前10个桶。
假设您的索引包含2个分片和以下文档(为了简化起见,我将使用size: 5
而不是10,因为您的include
子句与大小10冲突):< / p>
分片1:
{"body":"web", "score": 1, "tags": "java"}
{"body":"web", "score": 2, "tags": "c#"}
{"body":"web", "score": 10, "tags": "javascript"}
{"body":"web", "score": 3, "tags": "python"}
{"body":"web", "score": 2, "tags": "php"}
{"body":"web", "score": 4, "tags": "java"}
{"body":"web", "score": 6, "tags": "java"}
{"body":"web", "score": 10, "tags": "php"}
{"body":"web", "score": 15, "tags": "ruby"}
{"body":"web", "score": 8, "tags": "php"}
分片2:
{"body":"web", "score": 4, "tags": "php"}
{"body":"web", "score": 6, "tags": "php"}
{"body":"web", "score": 3, "tags": "python"}
{"body":"web", "score": 9, "tags": "c#"}
{"body":"web", "score": 10, "tags": "python"}
{"body":"web", "score": 3, "tags": "php"}
{"body":"web", "score": 5, "tags": "ruby"}
{"body":"web", "score": 12, "tags": "javascript"}
{"body":"web", "score": 1, "tags": "python"}
{"body":"web", "score": 3, "tags": "java"}
在每个分片上,标记存储桶将如下所示(按max(doc.score)
排序):
分片1:
ruby: 1 doc, max(doc.score) = 15
javascript: 1 doc, max(doc.score) = 10
php: 3 docs, max(doc.score) = 8
java: 3 docs, max(doc.score) = 6
python: 1 doc, max(doc.score) = 3
c#: 1 doc, max(doc.score) = 2
分片2:
javascript: 1 doc, max(doc.score) = 12
python: 3 docs, max(doc.score) = 10
c#: 1 doc, max(doc.score) = 9
php: 3 docs, max(doc.score) = 6
ruby: 1 doc, max(doc.score) = 5
java: 1 doc, max(doc.score) = 3
只有前5个将返回到协调节点,它将重新组合所有存储桶,并再次对其重新排序,并且仅取前5个,这将产生以下最终存储桶:
ruby: 2 docs, max(doc.score) = 15
javascript: 2 doc, max(doc.score) = 12
python: 4 docs, max(doc.score) = 10
php: 6 docs, max(doc.score) = 8
java: 3 docs, max(doc.score) = 6
因此,如您所见,执行顺序是交织在一起的。存储和排序在每个分片上发生一次,然后在协调节点上再次发生。但这并不是像先计算存储桶的顺序,然后计算存储桶等……这几乎是同时发生的,并且存储/排序也首先以分布式方式执行,最后所有内容都被集中化,类似于map / reduce的作用是什么,在ES中称为散点图/聚集。