字段折叠

一个普遍的需求是需要通过特定字段进行分组。例如我们需要按照用户名称 分组 返回最相关的博客文章。按照用户名分组意味着进行 terms 聚合。为能够按照用户 整体 名称进行分组,名称字段应保持 not_analyzed 的形式,具体说明参考 aggregations-and-analysis

  1. PUT /my_index/_mapping/blogpost
  2. {
  3. "properties": {
  4. "user": {
  5. "properties": {
  6. "name": { (1)
  7. "type": "string",
  8. "fields": {
  9. "raw": { (2)
  10. "type": "string",
  11. "index": "not_analyzed"
  12. }
  13. }
  14. }
  15. }
  16. }
  17. }
  18. }

<1> user.name 字段将用来进行全文检索。

<2> user.name.raw 字段将用来通过 terms 聚合进行分组。

然后添加一些数据:

  1. PUT /my_index/user/1
  2. {
  3. "name": "John Smith",
  4. "email": "john@smith.com",
  5. "dob": "1970/10/24"
  6. }
  7. PUT /my_index/blogpost/2
  8. {
  9. "title": "Relationships",
  10. "body": "It's complicated...",
  11. "user": {
  12. "id": 1,
  13. "name": "John Smith"
  14. }
  15. }
  16. PUT /my_index/user/3
  17. {
  18. "name": "Alice John",
  19. "email": "alice@john.com",
  20. "dob": "1979/01/04"
  21. }
  22. PUT /my_index/blogpost/4
  23. {
  24. "title": "Relationships are cool",
  25. "body": "It's not complicated at all...",
  26. "user": {
  27. "id": 3,
  28. "name": "Alice John"
  29. }
  30. }

现在我们来查询标题包含 relationships 并且作者名包含 John 的博客,查询结果再按作者名分组,感谢 {ref}/search-aggregations-metrics-top-hits-aggregation.html[top_hits aggregation] 提供了按照用户进行分组的功能:

  1. GET /my_index/blogpost/_search
  2. {
  3. "size" : 0, (1)
  4. "query": { (2)
  5. "bool": {
  6. "must": [
  7. { "match": { "title": "relationships" }},
  8. { "match": { "user.name": "John" }}
  9. ]
  10. }
  11. },
  12. "aggs": {
  13. "users": {
  14. "terms": {
  15. "field": "user.name.raw", (3)
  16. "order": { "top_score": "desc" } (4)
  17. },
  18. "aggs": {
  19. "top_score": { "max": { "script": "_score" }}, (4)
  20. "blogposts": { "top_hits": { "_source": "title", "size": 5 }} (5)
  21. }
  22. }
  23. }
  24. }

<1> 我们感兴趣的博客文章是通过 blogposts 聚合返回的,所以我们可以通过将 size 设置成 0 来禁止 hits 常规搜索。

<2> query 返回通过 relationships 查找名称为 John 的用户的博客文章。

<3> terms 聚合为每一个 user.name.raw 创建一个桶。

<4> top_score 聚合对通过 users 聚合得到的每一个桶按照文档评分对词项进行排序。

<5> top_hits 聚合仅为每个用户返回五个最相关的博客文章的 title 字段。

这里显示简短响应结果:

  1. ...
  2. "hits": {
  3. "total": 2,
  4. "max_score": 0,
  5. "hits": [] <1>
  6. },
  7. "aggregations": {
  8. "users": {
  9. "buckets": [
  10. {
  11. "key": "John Smith", <2>
  12. "doc_count": 1,
  13. "blogposts": {
  14. "hits": { <3>
  15. "total": 1,
  16. "max_score": 0.35258877,
  17. "hits": [
  18. {
  19. "_index": "my_index",
  20. "_type": "blogpost",
  21. "_id": "2",
  22. "_score": 0.35258877,
  23. "_source": {
  24. "title": "Relationships"
  25. }
  26. }
  27. ]
  28. }
  29. },
  30. "top_score": { <4>
  31. "value": 0.3525887727737427
  32. }
  33. },
  34. ...

<1> 因为我们设置 size 为 0 ,所以 hits 数组是空的。

<2> 在顶层查询结果中出现的每一个用户都会有一个对应的桶。

<3> 在每个用户桶下面都会有一个 blogposts.hits 数组包含针对这个用户的顶层查询结果。

<4> 用户桶按照每个用户最相关的博客文章进行排序。

使用 top_hits 聚合等效执行一个查询返回这些用户的名字和他们最相关的博客文章,然后为每一个用户执行相同的查询,以获得最好的博客。但前者的效率要好很多。

每一个桶返回的顶层查询命中结果是基于最初主查询进行的一个轻量 迷你查询 结果集。这个迷你查询提供了一些你期望的常用特性,例如高亮显示以及分页功能。