トップヒット集約

top_hits メトリック集約器は、集約される最も関連性の高いドキュメントを追跡します。この集約器はサブ集約器として使用されることを意図しており、バケットごとに最も一致するドキュメントを集約できます。

top_hits をトップレベルの集約として使用することはお勧めしません。検索ヒットをグループ化したい場合は、代わりに collapse パラメータを使用してください。

top_hits 集約器は、バケット集約器を介して特定のフィールドによって結果セットを効果的にグループ化するために使用できます。1つ以上のバケット集約器が、結果セットがどのプロパティによってスライスされるかを決定します。

オプション

  • from - 取得したい最初の結果からのオフセット。
  • size - バケットごとに返す最大の一致するヒット数。デフォルトでは、最初の3つの一致するヒットが返されます。
  • sort - 最も一致するヒットのソート方法。デフォルトでは、ヒットはメインクエリのスコアによってソートされます。

サポートされるヒットごとの機能

トップヒット集約は通常の検索ヒットを返すため、多くのヒットごとの機能がサポートされます:

docvalue_fieldssizesort のみが必要な場合は、トップメトリクス の方がトップヒット集約よりも効率的な選択かもしれません。

top_hitsrescore パラメータをサポートしていません。クエリの再スコアリングは検索ヒットにのみ適用され、集約結果には適用されません。集約で使用されるスコアを変更するには、function_score または script_score クエリを使用してください。

以下の例では、タイプごとに売上をグループ化し、各タイプの最後の売上を表示します。各売上について、日付と価格フィールドのみがソースに含まれます。

Python

  1. resp = client.search(
  2. index="sales",
  3. size="0",
  4. aggs={
  5. "top_tags": {
  6. "terms": {
  7. "field": "type",
  8. "size": 3
  9. },
  10. "aggs": {
  11. "top_sales_hits": {
  12. "top_hits": {
  13. "sort": [
  14. {
  15. "date": {
  16. "order": "desc"
  17. }
  18. }
  19. ],
  20. "_source": {
  21. "includes": [
  22. "date",
  23. "price"
  24. ]
  25. },
  26. "size": 1
  27. }
  28. }
  29. }
  30. }
  31. },
  32. )
  33. print(resp)

Ruby

  1. response = client.search(
  2. index: 'sales',
  3. size: 0,
  4. body: {
  5. aggregations: {
  6. top_tags: {
  7. terms: {
  8. field: 'type',
  9. size: 3
  10. },
  11. aggregations: {
  12. top_sales_hits: {
  13. top_hits: {
  14. sort: [
  15. {
  16. date: {
  17. order: 'desc'
  18. }
  19. }
  20. ],
  21. _source: {
  22. includes: [
  23. 'date',
  24. 'price'
  25. ]
  26. },
  27. size: 1
  28. }
  29. }
  30. }
  31. }
  32. }
  33. }
  34. )
  35. puts response

Js

  1. const response = await client.search({
  2. index: "sales",
  3. size: 0,
  4. aggs: {
  5. top_tags: {
  6. terms: {
  7. field: "type",
  8. size: 3,
  9. },
  10. aggs: {
  11. top_sales_hits: {
  12. top_hits: {
  13. sort: [
  14. {
  15. date: {
  16. order: "desc",
  17. },
  18. },
  19. ],
  20. _source: {
  21. includes: ["date", "price"],
  22. },
  23. size: 1,
  24. },
  25. },
  26. },
  27. },
  28. },
  29. });
  30. console.log(response);

コンソール

  1. POST /sales/_search?size=0
  2. {
  3. "aggs": {
  4. "top_tags": {
  5. "terms": {
  6. "field": "type",
  7. "size": 3
  8. },
  9. "aggs": {
  10. "top_sales_hits": {
  11. "top_hits": {
  12. "sort": [
  13. {
  14. "date": {
  15. "order": "desc"
  16. }
  17. }
  18. ],
  19. "_source": {
  20. "includes": [ "date", "price" ]
  21. },
  22. "size": 1
  23. }
  24. }
  25. }
  26. }
  27. }
  28. }

可能な応答:

コンソール-結果

  1. {
  2. ...
  3. "aggregations": {
  4. "top_tags": {
  5. "doc_count_error_upper_bound": 0,
  6. "sum_other_doc_count": 0,
  7. "buckets": [
  8. {
  9. "key": "hat",
  10. "doc_count": 3,
  11. "top_sales_hits": {
  12. "hits": {
  13. "total" : {
  14. "value": 3,
  15. "relation": "eq"
  16. },
  17. "max_score": null,
  18. "hits": [
  19. {
  20. "_index": "sales",
  21. "_id": "AVnNBmauCQpcRyxw6ChK",
  22. "_source": {
  23. "date": "2015/03/01 00:00:00",
  24. "price": 200
  25. },
  26. "sort": [
  27. 1425168000000
  28. ],
  29. "_score": null
  30. }
  31. ]
  32. }
  33. }
  34. },
  35. {
  36. "key": "t-shirt",
  37. "doc_count": 3,
  38. "top_sales_hits": {
  39. "hits": {
  40. "total" : {
  41. "value": 3,
  42. "relation": "eq"
  43. },
  44. "max_score": null,
  45. "hits": [
  46. {
  47. "_index": "sales",
  48. "_id": "AVnNBmauCQpcRyxw6ChL",
  49. "_source": {
  50. "date": "2015/03/01 00:00:00",
  51. "price": 175
  52. },
  53. "sort": [
  54. 1425168000000
  55. ],
  56. "_score": null
  57. }
  58. ]
  59. }
  60. }
  61. },
  62. {
  63. "key": "bag",
  64. "doc_count": 1,
  65. "top_sales_hits": {
  66. "hits": {
  67. "total" : {
  68. "value": 1,
  69. "relation": "eq"
  70. },
  71. "max_score": null,
  72. "hits": [
  73. {
  74. "_index": "sales",
  75. "_id": "AVnNBmatCQpcRyxw6ChH",
  76. "_source": {
  77. "date": "2015/01/01 00:00:00",
  78. "price": 150
  79. },
  80. "sort": [
  81. 1420070400000
  82. ],
  83. "_score": null
  84. }
  85. ]
  86. }
  87. }
  88. }
  89. ]
  90. }
  91. }
  92. }

フィールド崩壊の例

フィールド崩壊または結果グループ化は、結果セットを論理的にグループに分け、各グループごとにトップドキュメントを返す機能です。グループの順序は、グループ内の最初のドキュメントの関連性によって決まります。Elasticsearchでは、top_hits 集約器をサブ集約器としてラップするバケット集約器を介してこれを実装できます。

以下の例では、クロールされたウェブページを検索します。各ウェブページについて、本文とそのウェブページが属するドメインを保存します。terms 集約器を domain フィールドに定義することで、ウェブページの結果セットをドメインでグループ化します。top_hits 集約器はサブ集約器として定義され、バケットごとに最も一致するヒットが収集されます。

また、max 集約器が定義されており、terms 集約器の順序機能によって、バケットを最も関連性の高いドキュメントの関連性順に返すために使用されます。

Python

  1. resp = client.search(
  2. index="sales",
  3. query={
  4. "match": {
  5. "body": "elections"
  6. }
  7. },
  8. aggs={
  9. "top_sites": {
  10. "terms": {
  11. "field": "domain",
  12. "order": {
  13. "top_hit": "desc"
  14. }
  15. },
  16. "aggs": {
  17. "top_tags_hits": {
  18. "top_hits": {}
  19. },
  20. "top_hit": {
  21. "max": {
  22. "script": {
  23. "source": "_score"
  24. }
  25. }
  26. }
  27. }
  28. }
  29. },
  30. )
  31. print(resp)

Ruby

  1. response = client.search(
  2. index: 'sales',
  3. body: {
  4. query: {
  5. match: {
  6. body: 'elections'
  7. }
  8. },
  9. aggregations: {
  10. top_sites: {
  11. terms: {
  12. field: 'domain',
  13. order: {
  14. top_hit: 'desc'
  15. }
  16. },
  17. aggregations: {
  18. top_tags_hits: {
  19. top_hits: {}
  20. },
  21. top_hit: {
  22. max: {
  23. script: {
  24. source: '_score'
  25. }
  26. }
  27. }
  28. }
  29. }
  30. }
  31. }
  32. )
  33. puts response

Js

  1. const response = await client.search({
  2. index: "sales",
  3. query: {
  4. match: {
  5. body: "elections",
  6. },
  7. },
  8. aggs: {
  9. top_sites: {
  10. terms: {
  11. field: "domain",
  12. order: {
  13. top_hit: "desc",
  14. },
  15. },
  16. aggs: {
  17. top_tags_hits: {
  18. top_hits: {},
  19. },
  20. top_hit: {
  21. max: {
  22. script: {
  23. source: "_score",
  24. },
  25. },
  26. },
  27. },
  28. },
  29. },
  30. });
  31. console.log(response);

コンソール

  1. POST /sales/_search
  2. {
  3. "query": {
  4. "match": {
  5. "body": "elections"
  6. }
  7. },
  8. "aggs": {
  9. "top_sites": {
  10. "terms": {
  11. "field": "domain",
  12. "order": {
  13. "top_hit": "desc"
  14. }
  15. },
  16. "aggs": {
  17. "top_tags_hits": {
  18. "top_hits": {}
  19. },
  20. "top_hit" : {
  21. "max": {
  22. "script": {
  23. "source": "_score"
  24. }
  25. }
  26. }
  27. }
  28. }
  29. }
  30. }

現在、max (または min) 集約器が必要で、terms 集約器からのバケットが各ドメインの最も関連性の高いウェブページのスコアに従って順序付けられることを確認します。残念ながら、top_hits 集約器はまだ order オプションの terms 集約器で使用できません。

ネストされたまたは逆ネストされた集約器における top_hits サポート

top_hits 集約器が nested または reverse_nested 集約器にラップされている場合、ネストされたヒットが返されます。ネストされたヒットは、通常のドキュメントの一部である隠れたミニドキュメントであり、マッピングでネストされたフィールドタイプが構成されています。top_hits 集約器は、nested または reverse_nested 集約器にラップされている場合、これらのドキュメントを表示する能力を持っています。ネストされたについての詳細は、ネストされたタイプのマッピングを参照してください。

ネストされたタイプが構成されている場合、単一のドキュメントは実際には複数のLuceneドキュメントとしてインデックスされ、同じIDを共有します。ネストされたヒットのアイデンティティを決定するには、IDだけでは不十分であるため、ネストされたヒットにはネストされたアイデンティティも含まれます。ネストされたアイデンティティは、検索ヒットの _nested フィールドに保持され、配列フィールドとネストされたヒットが属する配列フィールド内のオフセットを含みます。オフセットはゼロベースです。

実際のサンプルでどのように機能するかを見てみましょう。以下のマッピングを考慮してください:

Python

  1. resp = client.indices.create(
  2. index="sales",
  3. mappings={
  4. "properties": {
  5. "tags": {
  6. "type": "keyword"
  7. },
  8. "comments": {
  9. "type": "nested",
  10. "properties": {
  11. "username": {
  12. "type": "keyword"
  13. },
  14. "comment": {
  15. "type": "text"
  16. }
  17. }
  18. }
  19. }
  20. },
  21. )
  22. print(resp)

Ruby

  1. response = client.indices.create(
  2. index: 'sales',
  3. body: {
  4. mappings: {
  5. properties: {
  6. tags: {
  7. type: 'keyword'
  8. },
  9. comments: {
  10. type: 'nested',
  11. properties: {
  12. username: {
  13. type: 'keyword'
  14. },
  15. comment: {
  16. type: 'text'
  17. }
  18. }
  19. }
  20. }
  21. }
  22. }
  23. )
  24. puts response

Js

  1. const response = await client.indices.create({
  2. index: "sales",
  3. mappings: {
  4. properties: {
  5. tags: {
  6. type: "keyword",
  7. },
  8. comments: {
  9. type: "nested",
  10. properties: {
  11. username: {
  12. type: "keyword",
  13. },
  14. comment: {
  15. type: "text",
  16. },
  17. },
  18. },
  19. },
  20. },
  21. });
  22. console.log(response);

コンソール

  1. PUT /sales
  2. {
  3. "mappings": {
  4. "properties": {
  5. "tags": { "type": "keyword" },
  6. "comments": {
  7. "type": "nested",
  8. "properties": {
  9. "username": { "type": "keyword" },
  10. "comment": { "type": "text" }
  11. }
  12. }
  13. }
  14. }
  15. }
comments は、product オブジェクトの下にネストされたドキュメントを保持する配列です。

いくつかのドキュメント:

Python

  1. resp = client.index(
  2. index="sales",
  3. id="1",
  4. refresh=True,
  5. document={
  6. "tags": [
  7. "car",
  8. "auto"
  9. ],
  10. "comments": [
  11. {
  12. "username": "baddriver007",
  13. "comment": "This car could have better brakes"
  14. },
  15. {
  16. "username": "dr_who",
  17. "comment": "Where's the autopilot? Can't find it"
  18. },
  19. {
  20. "username": "ilovemotorbikes",
  21. "comment": "This car has two extra wheels"
  22. }
  23. ]
  24. },
  25. )
  26. print(resp)

Ruby

  1. response = client.index(
  2. index: 'sales',
  3. id: 1,
  4. refresh: true,
  5. body: {
  6. tags: [
  7. 'car',
  8. 'auto'
  9. ],
  10. comments: [
  11. {
  12. username: 'baddriver007',
  13. comment: 'This car could have better brakes'
  14. },
  15. {
  16. username: 'dr_who',
  17. comment: "Where's the autopilot? Can't find it"
  18. },
  19. {
  20. username: 'ilovemotorbikes',
  21. comment: 'This car has two extra wheels'
  22. }
  23. ]
  24. }
  25. )
  26. puts response

Js

  1. const response = await client.index({
  2. index: "sales",
  3. id: 1,
  4. refresh: "true",
  5. document: {
  6. tags: ["car", "auto"],
  7. comments: [
  8. {
  9. username: "baddriver007",
  10. comment: "This car could have better brakes",
  11. },
  12. {
  13. username: "dr_who",
  14. comment: "Where's the autopilot? Can't find it",
  15. },
  16. {
  17. username: "ilovemotorbikes",
  18. comment: "This car has two extra wheels",
  19. },
  20. ],
  21. },
  22. });
  23. console.log(response);

コンソール

  1. PUT /sales/_doc/1?refresh
  2. {
  3. "tags": [ "car", "auto" ],
  4. "comments": [
  5. { "username": "baddriver007", "comment": "This car could have better brakes" },
  6. { "username": "dr_who", "comment": "Where's the autopilot? Can't find it" },
  7. { "username": "ilovemotorbikes", "comment": "This car has two extra wheels" }
  8. ]
  9. }

次の top_hits 集約 (nested 集約にラップされた) を実行することが可能になりました:

Python

  1. resp = client.search(
  2. index="sales",
  3. query={
  4. "term": {
  5. "tags": "car"
  6. }
  7. },
  8. aggs={
  9. "by_sale": {
  10. "nested": {
  11. "path": "comments"
  12. },
  13. "aggs": {
  14. "by_user": {
  15. "terms": {
  16. "field": "comments.username",
  17. "size": 1
  18. },
  19. "aggs": {
  20. "by_nested": {
  21. "top_hits": {}
  22. }
  23. }
  24. }
  25. }
  26. }
  27. },
  28. )
  29. print(resp)

Ruby

  1. response = client.search(
  2. index: 'sales',
  3. body: {
  4. query: {
  5. term: {
  6. tags: 'car'
  7. }
  8. },
  9. aggregations: {
  10. by_sale: {
  11. nested: {
  12. path: 'comments'
  13. },
  14. aggregations: {
  15. by_user: {
  16. terms: {
  17. field: 'comments.username',
  18. size: 1
  19. },
  20. aggregations: {
  21. by_nested: {
  22. top_hits: {}
  23. }
  24. }
  25. }
  26. }
  27. }
  28. }
  29. }
  30. )
  31. puts response

Js

  1. const response = await client.search({
  2. index: "sales",
  3. query: {
  4. term: {
  5. tags: "car",
  6. },
  7. },
  8. aggs: {
  9. by_sale: {
  10. nested: {
  11. path: "comments",
  12. },
  13. aggs: {
  14. by_user: {
  15. terms: {
  16. field: "comments.username",
  17. size: 1,
  18. },
  19. aggs: {
  20. by_nested: {
  21. top_hits: {},
  22. },
  23. },
  24. },
  25. },
  26. },
  27. },
  28. });
  29. console.log(response);

コンソール

  1. POST /sales/_search
  2. {
  3. "query": {
  4. "term": { "tags": "car" }
  5. },
  6. "aggs": {
  7. "by_sale": {
  8. "nested": {
  9. "path": "comments"
  10. },
  11. "aggs": {
  12. "by_user": {
  13. "terms": {
  14. "field": "comments.username",
  15. "size": 1
  16. },
  17. "aggs": {
  18. "by_nested": {
  19. "top_hits": {}
  20. }
  21. }
  22. }
  23. }
  24. }
  25. }
  26. }

ネストされたヒットを含むトップヒット応答スニペットは、配列フィールド comments の最初のスロットに存在します:

コンソール-結果

  1. {
  2. ...
  3. "aggregations": {
  4. "by_sale": {
  5. "by_user": {
  6. "buckets": [
  7. {
  8. "key": "baddriver007",
  9. "doc_count": 1,
  10. "by_nested": {
  11. "hits": {
  12. "total" : {
  13. "value": 1,
  14. "relation": "eq"
  15. },
  16. "max_score": 0.3616575,
  17. "hits": [
  18. {
  19. "_index": "sales",
  20. "_id": "1",
  21. "_nested": {
  22. "field": "comments",
  23. "offset": 0
  24. },
  25. "_score": 0.3616575,
  26. "_source": {
  27. "comment": "This car could have better brakes",
  28. "username": "baddriver007"
  29. }
  30. }
  31. ]
  32. }
  33. }
  34. }
  35. ...
  36. ]
  37. }
  38. }
  39. }
  40. }
ネストされたヒットを含む配列フィールドの名前
含まれる配列内のネストされたヒットの位置
ネストされたヒットのソース

_source が要求されると、ネストされたオブジェクトのソースの一部のみが返され、ドキュメント全体のソースは返されません。また、ネストされた内部オブジェクトレベルの保存されたフィールドは、top_hits 集約器を介して nested または reverse_nested 集約器に存在することができます。

ネストされたヒットのみがヒット内に _nested フィールドを持ち、非ネストされた(通常の)ヒットは _nested フィールドを持ちません。

_nested の情報は、_source が有効でない場合に、元のソースを他の場所で解析するためにも使用できます。

マッピングで複数のレベルのネストされたオブジェクトタイプが定義されている場合、_nested の情報は、2層以上のネストされたヒットのアイデンティティを表現するために階層的である可能性があります。

以下の例では、ネストされたヒットがフィールド nested_grand_child_field の最初のスロットに存在し、次に nested_child_field フィールドの2番目のスロットに存在します:

Js

  1. ...
  2. "hits": {
  3. "total" : {
  4. "value": 2565,
  5. "relation": "eq"
  6. },
  7. "max_score": 1,
  8. "hits": [
  9. {
  10. "_index": "a",
  11. "_id": "1",
  12. "_score": 1,
  13. "_nested" : {
  14. "field" : "nested_child_field",
  15. "offset" : 1,
  16. "_nested" : {
  17. "field" : "nested_grand_child_field",
  18. "offset" : 0
  19. }
  20. }
  21. "_source": ...
  22. },
  23. ...
  24. ]
  25. }
  26. ...

パイプライン集約での使用

top_hits は、バケットごとに単一の値を消費するパイプライン集約で使用できます。たとえば、bucket_selector はバケットフィルタリングを適用し、SQLのHAVING句を使用するのと似ています。これには、size を1に設定し、ラッピング集約器に渡す値の正しいパスを指定する必要があります。後者は、_source_sort、または _score の値である可能性があります。例えば:

Python

  1. resp = client.search(
  2. index="sales",
  3. size="0",
  4. aggs={
  5. "top_tags": {
  6. "terms": {
  7. "field": "type",
  8. "size": 3
  9. },
  10. "aggs": {
  11. "top_sales_hits": {
  12. "top_hits": {
  13. "sort": [
  14. {
  15. "date": {
  16. "order": "desc"
  17. }
  18. }
  19. ],
  20. "_source": {
  21. "includes": [
  22. "date",
  23. "price"
  24. ]
  25. },
  26. "size": 1
  27. }
  28. },
  29. "having.top_salary": {
  30. "bucket_selector": {
  31. "buckets_path": {
  32. "tp": "top_sales_hits[_source.price]"
  33. },
  34. "script": "params.tp < 180"
  35. }
  36. }
  37. }
  38. }
  39. },
  40. )
  41. print(resp)

Js

  1. const response = await client.search({
  2. index: "sales",
  3. size: 0,
  4. aggs: {
  5. top_tags: {
  6. terms: {
  7. field: "type",
  8. size: 3,
  9. },
  10. aggs: {
  11. top_sales_hits: {
  12. top_hits: {
  13. sort: [
  14. {
  15. date: {
  16. order: "desc",
  17. },
  18. },
  19. ],
  20. _source: {
  21. includes: ["date", "price"],
  22. },
  23. size: 1,
  24. },
  25. },
  26. "having.top_salary": {
  27. bucket_selector: {
  28. buckets_path: {
  29. tp: "top_sales_hits[_source.price]",
  30. },
  31. script: "params.tp < 180",
  32. },
  33. },
  34. },
  35. },
  36. },
  37. });
  38. console.log(response);

コンソール

  1. POST /sales/_search?size=0
  2. {
  3. "aggs": {
  4. "top_tags": {
  5. "terms": {
  6. "field": "type",
  7. "size": 3
  8. },
  9. "aggs": {
  10. "top_sales_hits": {
  11. "top_hits": {
  12. "sort": [
  13. {
  14. "date": {
  15. "order": "desc"
  16. }
  17. }
  18. ],
  19. "_source": {
  20. "includes": [ "date", "price" ]
  21. },
  22. "size": 1
  23. }
  24. },
  25. "having.top_salary": {
  26. "bucket_selector": {
  27. "buckets_path": {
  28. "tp": "top_sales_hits[_source.price]"
  29. },
  30. "script": "params.tp < 180"
  31. }
  32. }
  33. }
  34. }
  35. }
  36. }

bucket_path は、top_hits 名称 top_sales_hits と、集約値を提供するフィールドのキーワード、すなわち上記の _source フィールド price を使用します。他のオプションには、上記のソート値 top_sales_hits[_sort] にフィルタリングするためのものや、トップヒットのスコアにフィルタリングするための top_sales_hits[_score] があります。