内部ヒットの取得

parent-join および nested 機能は、異なるスコープに一致するドキュメントを返すことを可能にします。親/子のケースでは、親ドキュメントは子ドキュメントの一致に基づいて返されるか、子ドキュメントは親ドキュメントの一致に基づいて返されます。ネストされたケースでは、ネストされた内部オブジェクトの一致に基づいてドキュメントが返されます。

どちらの場合も、ドキュメントが返される原因となった異なるスコープ内の実際の一致は隠されています。多くの場合、特定の情報が返される原因となった内部ネストされたオブジェクト(ネストされた場合)や子/親ドキュメント(親/子の場合)を知ることは非常に有用です。内部ヒット機能はこれに使用できます。この機能は、検索応答内の各検索ヒットに対して、異なるスコープで検索ヒットが一致する原因となった追加のネストされたヒットを返します。

内部ヒットは、inner_hits 定義を nestedhas_child または has_parent クエリおよびフィルターに定義することで使用できます。構造は次のようになります:

Js

  1. "<query>" : {
  2. "inner_hits" : {
  3. <inner_hits_options>
  4. }
  5. }

もし inner_hits がそれをサポートするクエリに定義されている場合、各検索ヒットには次の構造を持つ inner_hits JSON オブジェクトが含まれます:

Js

  1. "hits": [
  2. {
  3. "_index": ...,
  4. "_type": ...,
  5. "_id": ...,
  6. "inner_hits": {
  7. "<inner_hits_name>": {
  8. "hits": {
  9. "total": ...,
  10. "hits": [
  11. {
  12. "_id": ...,
  13. ...
  14. },
  15. ...
  16. ]
  17. }
  18. }
  19. },
  20. ...
  21. },
  22. ...
  23. ]

オプション

内部ヒットは次のオプションをサポートします:

from 返される通常の検索ヒットの各 inner_hits に対して取得する最初のヒットのオフセット。
size inner_hits に対して返す最大ヒット数。デフォルトでは、上位3つの一致するヒットが返されます。
sort inner_hits に対して内部ヒットをどのようにソートするか。デフォルトでは、ヒットはスコアによってソートされます。
name 応答内の特定の内部ヒット定義に使用される名前。単一の検索リクエストで複数の内部ヒットが定義されている場合に便利です。デフォルトは、内部ヒットが定義されているクエリによって異なります。has_child クエリおよびフィルターの場合、これは子タイプであり、has_parent クエリおよびフィルターの場合、これは親タイプであり、ネストされたクエリおよびフィルターの場合、これはネストされたパスです。

内部ヒットは、次のドキュメントごとの機能もサポートします:

ネストされた内部ヒット

ネストされた inner_hits は、検索ヒットに対する内部ヒットとしてネストされた内部オブジェクトを含めるために使用できます。

Python

  1. resp = client.indices.create(
  2. index="test",
  3. mappings={
  4. "properties": {
  5. "comments": {
  6. "type": "nested"
  7. }
  8. }
  9. },
  10. )
  11. print(resp)
  12. resp1 = client.index(
  13. index="test",
  14. id="1",
  15. refresh=True,
  16. document={
  17. "title": "Test title",
  18. "comments": [
  19. {
  20. "author": "kimchy",
  21. "number": 1
  22. },
  23. {
  24. "author": "nik9000",
  25. "number": 2
  26. }
  27. ]
  28. },
  29. )
  30. print(resp1)
  31. resp2 = client.search(
  32. index="test",
  33. query={
  34. "nested": {
  35. "path": "comments",
  36. "query": {
  37. "match": {
  38. "comments.number": 2
  39. }
  40. },
  41. "inner_hits": {}
  42. }
  43. },
  44. )
  45. print(resp2)

Ruby

  1. response = client.indices.create(
  2. index: 'test',
  3. body: {
  4. mappings: {
  5. properties: {
  6. comments: {
  7. type: 'nested'
  8. }
  9. }
  10. }
  11. }
  12. )
  13. puts response
  14. response = client.index(
  15. index: 'test',
  16. id: 1,
  17. refresh: true,
  18. body: {
  19. title: 'Test title',
  20. comments: [
  21. {
  22. author: 'kimchy',
  23. number: 1
  24. },
  25. {
  26. author: 'nik9000',
  27. number: 2
  28. }
  29. ]
  30. }
  31. )
  32. puts response
  33. response = client.search(
  34. index: 'test',
  35. body: {
  36. query: {
  37. nested: {
  38. path: 'comments',
  39. query: {
  40. match: {
  41. 'comments.number' => 2
  42. }
  43. },
  44. inner_hits: {}
  45. }
  46. }
  47. }
  48. )
  49. puts response

Js

  1. const response = await client.indices.create({
  2. index: "test",
  3. mappings: {
  4. properties: {
  5. comments: {
  6. type: "nested",
  7. },
  8. },
  9. },
  10. });
  11. console.log(response);
  12. const response1 = await client.index({
  13. index: "test",
  14. id: 1,
  15. refresh: "true",
  16. document: {
  17. title: "Test title",
  18. comments: [
  19. {
  20. author: "kimchy",
  21. number: 1,
  22. },
  23. {
  24. author: "nik9000",
  25. number: 2,
  26. },
  27. ],
  28. },
  29. });
  30. console.log(response1);
  31. const response2 = await client.search({
  32. index: "test",
  33. query: {
  34. nested: {
  35. path: "comments",
  36. query: {
  37. match: {
  38. "comments.number": 2,
  39. },
  40. },
  41. inner_hits: {},
  42. },
  43. },
  44. });
  45. console.log(response2);

コンソール

  1. PUT test
  2. {
  3. "mappings": {
  4. "properties": {
  5. "comments": {
  6. "type": "nested"
  7. }
  8. }
  9. }
  10. }
  11. PUT test/_doc/1?refresh
  12. {
  13. "title": "Test title",
  14. "comments": [
  15. {
  16. "author": "kimchy",
  17. "number": 1
  18. },
  19. {
  20. "author": "nik9000",
  21. "number": 2
  22. }
  23. ]
  24. }
  25. POST test/_search
  26. {
  27. "query": {
  28. "nested": {
  29. "path": "comments",
  30. "query": {
  31. "match": {"comments.number" : 2}
  32. },
  33. "inner_hits": {}
  34. }
  35. }
  36. }
ネストされたクエリ内の内部ヒット定義。他のオプションは定義する必要はありません。

上記の検索リクエストから生成される可能性のある応答スニペットの例:

コンソール-結果

  1. {
  2. ...,
  3. "hits": {
  4. "total" : {
  5. "value": 1,
  6. "relation": "eq"
  7. },
  8. "max_score": 1.0,
  9. "hits": [
  10. {
  11. "_index": "test",
  12. "_id": "1",
  13. "_score": 1.0,
  14. "_source": ...,
  15. "inner_hits": {
  16. "comments": {
  17. "hits": {
  18. "total" : {
  19. "value": 1,
  20. "relation": "eq"
  21. },
  22. "max_score": 1.0,
  23. "hits": [
  24. {
  25. "_index": "test",
  26. "_id": "1",
  27. "_nested": {
  28. "field": "comments",
  29. "offset": 1
  30. },
  31. "_score": 1.0,
  32. "_source": {
  33. "author": "nik9000",
  34. "number": 2
  35. }
  36. }
  37. ]
  38. }
  39. }
  40. }
  41. }
  42. ]
  43. }
  44. }
検索リクエスト内の内部ヒット定義で使用される名前。カスタムキーは name オプションを介して使用できます。

上記の例では、_nested メタデータは重要です。なぜなら、どの内部ネストされたオブジェクトからこの内部ヒットが来たのかを定義するからです。field は、ネストされたヒットがどのオブジェクト配列フィールドから来ているかを定義し、offset_source 内の位置に対して相対的です。ソートとスコアリングのため、inner_hits 内のヒットオブジェクトの実際の位置は、ネストされた内部オブジェクトが定義された位置とは通常異なります。

デフォルトでは、_sourceinner_hits 内のヒットオブジェクトにも返されますが、これは変更可能です。_source フィルタリング機能を介して、ソースの一部を返すか無効にすることができます。ネストされたレベルでストレージフィールドが定義されている場合、これらも fields 機能を介して返すことができます。

重要なデフォルトは、_sourceinner_hits 内のヒットで返されるのは _nested メタデータに対して相対的であるということです。したがって、上記の例では、ネストされたヒットごとにコメント部分のみが返され、コメントを含むトップレベルドキュメントの全体のソースは返されません。

ネストされた内部ヒットと _source

ネストされたドキュメントには _source フィールドがありません。なぜなら、ドキュメントの全体のソースがその _source フィールドの下にルートドキュメントと共に保存されているからです。ネストされたドキュメントのソースのみを含めるために、ルートドキュメントのソースが解析され、ネストされたドキュメントに関連する部分のみが内部ヒットのソースとして含まれます。各一致するネストされたドキュメントに対してこれを行うことは、特に size および内部ヒットの size がデフォルトよりも高く設定されている場合、全体の検索リクエストを実行するのにかかる時間に影響を与えます。ネストされた内部ヒットのために比較的高価なソース抽出を避けるために、ソースを含めることを無効にし、ドキュメント値フィールドのみに依存することができます。次のように:

Python

  1. resp = client.indices.create(
  2. index="test",
  3. mappings={
  4. "properties": {
  5. "comments": {
  6. "type": "nested"
  7. }
  8. }
  9. },
  10. )
  11. print(resp)
  12. resp1 = client.index(
  13. index="test",
  14. id="1",
  15. refresh=True,
  16. document={
  17. "title": "Test title",
  18. "comments": [
  19. {
  20. "author": "kimchy",
  21. "text": "comment text"
  22. },
  23. {
  24. "author": "nik9000",
  25. "text": "words words words"
  26. }
  27. ]
  28. },
  29. )
  30. print(resp1)
  31. resp2 = client.search(
  32. index="test",
  33. query={
  34. "nested": {
  35. "path": "comments",
  36. "query": {
  37. "match": {
  38. "comments.text": "words"
  39. }
  40. },
  41. "inner_hits": {
  42. "_source": False,
  43. "docvalue_fields": [
  44. "comments.text.keyword"
  45. ]
  46. }
  47. }
  48. },
  49. )
  50. print(resp2)

Ruby

  1. response = client.indices.create(
  2. index: 'test',
  3. body: {
  4. mappings: {
  5. properties: {
  6. comments: {
  7. type: 'nested'
  8. }
  9. }
  10. }
  11. }
  12. )
  13. puts response
  14. response = client.index(
  15. index: 'test',
  16. id: 1,
  17. refresh: true,
  18. body: {
  19. title: 'Test title',
  20. comments: [
  21. {
  22. author: 'kimchy',
  23. text: 'comment text'
  24. },
  25. {
  26. author: 'nik9000',
  27. text: 'words words words'
  28. }
  29. ]
  30. }
  31. )
  32. puts response
  33. response = client.search(
  34. index: 'test',
  35. body: {
  36. query: {
  37. nested: {
  38. path: 'comments',
  39. query: {
  40. match: {
  41. 'comments.text' => 'words'
  42. }
  43. },
  44. inner_hits: {
  45. _source: false,
  46. docvalue_fields: [
  47. 'comments.text.keyword'
  48. ]
  49. }
  50. }
  51. }
  52. }
  53. )
  54. puts response

Js

  1. const response = await client.indices.create({
  2. index: "test",
  3. mappings: {
  4. properties: {
  5. comments: {
  6. type: "nested",
  7. },
  8. },
  9. },
  10. });
  11. console.log(response);
  12. const response1 = await client.index({
  13. index: "test",
  14. id: 1,
  15. refresh: "true",
  16. document: {
  17. title: "Test title",
  18. comments: [
  19. {
  20. author: "kimchy",
  21. text: "comment text",
  22. },
  23. {
  24. author: "nik9000",
  25. text: "words words words",
  26. },
  27. ],
  28. },
  29. });
  30. console.log(response1);
  31. const response2 = await client.search({
  32. index: "test",
  33. query: {
  34. nested: {
  35. path: "comments",
  36. query: {
  37. match: {
  38. "comments.text": "words",
  39. },
  40. },
  41. inner_hits: {
  42. _source: false,
  43. docvalue_fields: ["comments.text.keyword"],
  44. },
  45. },
  46. },
  47. });
  48. console.log(response2);

コンソール

  1. PUT test
  2. {
  3. "mappings": {
  4. "properties": {
  5. "comments": {
  6. "type": "nested"
  7. }
  8. }
  9. }
  10. }
  11. PUT test/_doc/1?refresh
  12. {
  13. "title": "Test title",
  14. "comments": [
  15. {
  16. "author": "kimchy",
  17. "text": "comment text"
  18. },
  19. {
  20. "author": "nik9000",
  21. "text": "words words words"
  22. }
  23. ]
  24. }
  25. POST test/_search
  26. {
  27. "query": {
  28. "nested": {
  29. "path": "comments",
  30. "query": {
  31. "match": {"comments.text" : "words"}
  32. },
  33. "inner_hits": {
  34. "_source" : false,
  35. "docvalue_fields" : [
  36. "comments.text.keyword"
  37. ]
  38. }
  39. }
  40. }
  41. }

ネストされたオブジェクトフィールドと内部ヒットの階層レベル。

マッピングに複数の階層ネストされたオブジェクトフィールドがある場合、各レベルはドット表記のパスを介してアクセスできます。たとえば、comments ネストフィールドが votes ネストフィールドを含み、投票がルートヒットと直接返されるべき場合、次のパスを定義できます:

Python

  1. resp = client.indices.create(
  2. index="test",
  3. mappings={
  4. "properties": {
  5. "comments": {
  6. "type": "nested",
  7. "properties": {
  8. "votes": {
  9. "type": "nested"
  10. }
  11. }
  12. }
  13. }
  14. },
  15. )
  16. print(resp)
  17. resp1 = client.index(
  18. index="test",
  19. id="1",
  20. refresh=True,
  21. document={
  22. "title": "Test title",
  23. "comments": [
  24. {
  25. "author": "kimchy",
  26. "text": "comment text",
  27. "votes": []
  28. },
  29. {
  30. "author": "nik9000",
  31. "text": "words words words",
  32. "votes": [
  33. {
  34. "value": 1,
  35. "voter": "kimchy"
  36. },
  37. {
  38. "value": -1,
  39. "voter": "other"
  40. }
  41. ]
  42. }
  43. ]
  44. },
  45. )
  46. print(resp1)
  47. resp2 = client.search(
  48. index="test",
  49. query={
  50. "nested": {
  51. "path": "comments.votes",
  52. "query": {
  53. "match": {
  54. "comments.votes.voter": "kimchy"
  55. }
  56. },
  57. "inner_hits": {}
  58. }
  59. },
  60. )
  61. print(resp2)

Ruby

  1. response = client.indices.create(
  2. index: 'test',
  3. body: {
  4. mappings: {
  5. properties: {
  6. comments: {
  7. type: 'nested',
  8. properties: {
  9. votes: {
  10. type: 'nested'
  11. }
  12. }
  13. }
  14. }
  15. }
  16. }
  17. )
  18. puts response
  19. response = client.index(
  20. index: 'test',
  21. id: 1,
  22. refresh: true,
  23. body: {
  24. title: 'Test title',
  25. comments: [
  26. {
  27. author: 'kimchy',
  28. text: 'comment text',
  29. votes: []
  30. },
  31. {
  32. author: 'nik9000',
  33. text: 'words words words',
  34. votes: [
  35. {
  36. value: 1,
  37. voter: 'kimchy'
  38. },
  39. {
  40. value: -1,
  41. voter: 'other'
  42. }
  43. ]
  44. }
  45. ]
  46. }
  47. )
  48. puts response
  49. response = client.search(
  50. index: 'test',
  51. body: {
  52. query: {
  53. nested: {
  54. path: 'comments.votes',
  55. query: {
  56. match: {
  57. 'comments.votes.voter' => 'kimchy'
  58. }
  59. },
  60. inner_hits: {}
  61. }
  62. }
  63. }
  64. )
  65. puts response

Js

  1. const response = await client.indices.create({
  2. index: "test",
  3. mappings: {
  4. properties: {
  5. comments: {
  6. type: "nested",
  7. properties: {
  8. votes: {
  9. type: "nested",
  10. },
  11. },
  12. },
  13. },
  14. },
  15. });
  16. console.log(response);
  17. const response1 = await client.index({
  18. index: "test",
  19. id: 1,
  20. refresh: "true",
  21. document: {
  22. title: "Test title",
  23. comments: [
  24. {
  25. author: "kimchy",
  26. text: "comment text",
  27. votes: [],
  28. },
  29. {
  30. author: "nik9000",
  31. text: "words words words",
  32. votes: [
  33. {
  34. value: 1,
  35. voter: "kimchy",
  36. },
  37. {
  38. value: -1,
  39. voter: "other",
  40. },
  41. ],
  42. },
  43. ],
  44. },
  45. });
  46. console.log(response1);
  47. const response2 = await client.search({
  48. index: "test",
  49. query: {
  50. nested: {
  51. path: "comments.votes",
  52. query: {
  53. match: {
  54. "comments.votes.voter": "kimchy",
  55. },
  56. },
  57. inner_hits: {},
  58. },
  59. },
  60. });
  61. console.log(response2);

コンソール

  1. PUT test
  2. {
  3. "mappings": {
  4. "properties": {
  5. "comments": {
  6. "type": "nested",
  7. "properties": {
  8. "votes": {
  9. "type": "nested"
  10. }
  11. }
  12. }
  13. }
  14. }
  15. }
  16. PUT test/_doc/1?refresh
  17. {
  18. "title": "Test title",
  19. "comments": [
  20. {
  21. "author": "kimchy",
  22. "text": "comment text",
  23. "votes": []
  24. },
  25. {
  26. "author": "nik9000",
  27. "text": "words words words",
  28. "votes": [
  29. {"value": 1 , "voter": "kimchy"},
  30. {"value": -1, "voter": "other"}
  31. ]
  32. }
  33. ]
  34. }
  35. POST test/_search
  36. {
  37. "query": {
  38. "nested": {
  39. "path": "comments.votes",
  40. "query": {
  41. "match": {
  42. "comments.votes.voter": "kimchy"
  43. }
  44. },
  45. "inner_hits" : {}
  46. }
  47. }
  48. }

このように見えます:

コンソール-結果

  1. {
  2. ...,
  3. "hits": {
  4. "total" : {
  5. "value": 1,
  6. "relation": "eq"
  7. },
  8. "max_score": 0.6931471,
  9. "hits": [
  10. {
  11. "_index": "test",
  12. "_id": "1",
  13. "_score": 0.6931471,
  14. "_source": ...,
  15. "inner_hits": {
  16. "comments.votes": {
  17. "hits": {
  18. "total" : {
  19. "value": 1,
  20. "relation": "eq"
  21. },
  22. "max_score": 0.6931471,
  23. "hits": [
  24. {
  25. "_index": "test",
  26. "_id": "1",
  27. "_nested": {
  28. "field": "comments",
  29. "offset": 1,
  30. "_nested": {
  31. "field": "votes",
  32. "offset": 0
  33. }
  34. },
  35. "_score": 0.6931471,
  36. "_source": {
  37. "value": 1,
  38. "voter": "kimchy"
  39. }
  40. }
  41. ]
  42. }
  43. }
  44. }
  45. }
  46. ]
  47. }
  48. }

この間接参照は、ネストされた内部ヒットにのみサポートされています。

親/子内部ヒット

親/子 inner_hits は、親または子を含めるために使用できます:

Python

  1. resp = client.indices.create(
  2. index="test",
  3. mappings={
  4. "properties": {
  5. "my_join_field": {
  6. "type": "join",
  7. "relations": {
  8. "my_parent": "my_child"
  9. }
  10. }
  11. }
  12. },
  13. )
  14. print(resp)
  15. resp1 = client.index(
  16. index="test",
  17. id="1",
  18. refresh=True,
  19. document={
  20. "number": 1,
  21. "my_join_field": "my_parent"
  22. },
  23. )
  24. print(resp1)
  25. resp2 = client.index(
  26. index="test",
  27. id="2",
  28. routing="1",
  29. refresh=True,
  30. document={
  31. "number": 1,
  32. "my_join_field": {
  33. "name": "my_child",
  34. "parent": "1"
  35. }
  36. },
  37. )
  38. print(resp2)
  39. resp3 = client.search(
  40. index="test",
  41. query={
  42. "has_child": {
  43. "type": "my_child",
  44. "query": {
  45. "match": {
  46. "number": 1
  47. }
  48. },
  49. "inner_hits": {}
  50. }
  51. },
  52. )
  53. print(resp3)

Ruby

  1. response = client.indices.create(
  2. index: 'test',
  3. body: {
  4. mappings: {
  5. properties: {
  6. my_join_field: {
  7. type: 'join',
  8. relations: {
  9. my_parent: 'my_child'
  10. }
  11. }
  12. }
  13. }
  14. }
  15. )
  16. puts response
  17. response = client.index(
  18. index: 'test',
  19. id: 1,
  20. refresh: true,
  21. body: {
  22. number: 1,
  23. my_join_field: 'my_parent'
  24. }
  25. )
  26. puts response
  27. response = client.index(
  28. index: 'test',
  29. id: 2,
  30. routing: 1,
  31. refresh: true,
  32. body: {
  33. number: 1,
  34. my_join_field: {
  35. name: 'my_child',
  36. parent: '1'
  37. }
  38. }
  39. )
  40. puts response
  41. response = client.search(
  42. index: 'test',
  43. body: {
  44. query: {
  45. has_child: {
  46. type: 'my_child',
  47. query: {
  48. match: {
  49. number: 1
  50. }
  51. },
  52. inner_hits: {}
  53. }
  54. }
  55. }
  56. )
  57. puts response

Js

  1. const response = await client.indices.create({
  2. index: "test",
  3. mappings: {
  4. properties: {
  5. my_join_field: {
  6. type: "join",
  7. relations: {
  8. my_parent: "my_child",
  9. },
  10. },
  11. },
  12. },
  13. });
  14. console.log(response);
  15. const response1 = await client.index({
  16. index: "test",
  17. id: 1,
  18. refresh: "true",
  19. document: {
  20. number: 1,
  21. my_join_field: "my_parent",
  22. },
  23. });
  24. console.log(response1);
  25. const response2 = await client.index({
  26. index: "test",
  27. id: 2,
  28. routing: 1,
  29. refresh: "true",
  30. document: {
  31. number: 1,
  32. my_join_field: {
  33. name: "my_child",
  34. parent: "1",
  35. },
  36. },
  37. });
  38. console.log(response2);
  39. const response3 = await client.search({
  40. index: "test",
  41. query: {
  42. has_child: {
  43. type: "my_child",
  44. query: {
  45. match: {
  46. number: 1,
  47. },
  48. },
  49. inner_hits: {},
  50. },
  51. },
  52. });
  53. console.log(response3);

コンソール

  1. PUT test
  2. {
  3. "mappings": {
  4. "properties": {
  5. "my_join_field": {
  6. "type": "join",
  7. "relations": {
  8. "my_parent": "my_child"
  9. }
  10. }
  11. }
  12. }
  13. }
  14. PUT test/_doc/1?refresh
  15. {
  16. "number": 1,
  17. "my_join_field": "my_parent"
  18. }
  19. PUT test/_doc/2?routing=1&refresh
  20. {
  21. "number": 1,
  22. "my_join_field": {
  23. "name": "my_child",
  24. "parent": "1"
  25. }
  26. }
  27. POST test/_search
  28. {
  29. "query": {
  30. "has_child": {
  31. "type": "my_child",
  32. "query": {
  33. "match": {
  34. "number": 1
  35. }
  36. },
  37. "inner_hits": {}
  38. }
  39. }
  40. }
ネストされた例のような内部ヒット定義。

上記の検索リクエストから生成される可能性のある応答スニペットの例:

コンソール-結果

  1. {
  2. ...,
  3. "hits": {
  4. "total": {
  5. "value": 1,
  6. "relation": "eq"
  7. },
  8. "max_score": 1.0,
  9. "hits": [
  10. {
  11. "_index": "test",
  12. "_id": "1",
  13. "_score": 1.0,
  14. "_source": {
  15. "number": 1,
  16. "my_join_field": "my_parent"
  17. },
  18. "inner_hits": {
  19. "my_child": {
  20. "hits": {
  21. "total": {
  22. "value": 1,
  23. "relation": "eq"
  24. },
  25. "max_score": 1.0,
  26. "hits": [
  27. {
  28. "_index": "test",
  29. "_id": "2",
  30. "_score": 1.0,
  31. "_routing": "1",
  32. "_source": {
  33. "number": 1,
  34. "my_join_field": {
  35. "name": "my_child",
  36. "parent": "1"
  37. }
  38. }
  39. }
  40. ]
  41. }
  42. }
  43. }
  44. }
  45. ]
  46. }
  47. }