Spec-Zone.ru › Elasticsearch 8
›Elasticsearch Guide [8.17] ›Поиск данных ›API поиска

Получение вложенных совпадений

Функции parent-join и nested позволяют возвращать документы, которые имеют совпадения в другом контексте. В случае parent/child, родительские документы возвращаются на основе совпадений в дочерних документах, или дочерние документы возвращаются на основе совпадений в родительских документах. В случае nested, документы возвращаются на основе совпадений во вложенных внутренних объектах.

В обоих случаях фактические совпадения в различных контекстах, которые привели к возврату документа, скрыты. Во многих случаях очень полезно знать, какие внутренние вложенные объекты (в случае nested) или дочерние/родительские документы (в случае parent/child) вызвали возврат определенной информации. Для этого можно использовать функцию inner hits. Эта функция возвращает дополнительные вложенные совпадения для каждого результата поиска в ответе, которые привели к совпадению поиска в другом контексте.

Вложенные совпадения могут быть использованы путем определения определения inner_hits для запроса и фильтра nested, has_child или has_parent. Структура выглядит так:

"<query>" : {
    "inner_hits" : {
        <inner_hits_options>
    }
}

Если inner_hits определено для запроса, который его поддерживает, то каждый результат поиска будет содержать объект json inner_hits со следующей структурой:

"hits": [
     {
        "_index": ...,
        "_type": ...,
        "_id": ...,
        "inner_hits": {
           "<inner_hits_name>": {
              "hits": {
                 "total": ...,
                 "hits": [
                    {
                       "_id": ...,
                       ...
                    },
                    ...
                 ]
              }
           }
        },
        ...
     },
     ...
]

Параметры

Вложенные совпадения поддерживают следующие параметры:

from

Смещение от начала, с которого извлекать первый результат для каждого inner_hits во возвращаемых обычных результатах поиска.

size

Максимальное количество результатов, которые нужно вернуть для каждого inner_hits. По умолчанию возвращаются три лучших совпадающих результата.

sort

Способ сортировки вложенных совпадений по каждому inner_hits. По умолчанию результаты сортируются по оценке.

name

Имя, которое будет использоваться для конкретного определения вложенного совпадения в ответе. Полезно, когда в одном запросе определено несколько вложенных совпадений. Значение по умолчанию зависит от того, в каком запросе определено вложенное совпадение. Для запроса и фильтра has_child это тип дочернего элемента, для запроса и фильтра has_parent это тип родительского элемента, а для запроса и фильтра nested это путь к вложенному элементу.

Вложенные совпадения также поддерживают следующие функции на уровне документа:

  • Выделение
  • Объяснение
  • Поля поиска
  • Фильтрация _source
  • Скриптовые поля
  • Поля значений документа
  • Включить версии
  • Включить последовательные номера и первичные термины

Вложенные вложенные совпадения

Вложенные inner_hits могут быть использованы для включения вложенных внутренних объектов в качестве вложенных совпадений результата поиска.

resp = client.indices.create(
    index="test",
    mappings={
        "properties": {
            "comments": {
                "type": "nested"
            }
        }
    },
)
print(resp)

resp1 = client.index(
    index="test",
    id="1",
    refresh=True,
    document={
        "title": "Test title",
        "comments": [
            {
                "author": "kimchy",
                "number": 1
            },
            {
                "author": "nik9000",
                "number": 2
            }
        ]
    },
)
print(resp1)

resp2 = client.search(
    index="test",
    query={
        "nested": {
            "path": "comments",
            "query": {
                "match": {
                    "comments.number": 2
                }
            },
            "inner_hits": {}
        }
    },
)
print(resp2)
response = client.indices.create(
  index: 'test',
  body: {
    mappings: {
      properties: {
        comments: {
          type: 'nested'
        }
      }
    }
  }
)
puts response

response = client.index(
  index: 'test',
  id: 1,
  refresh: true,
  body: {
    title: 'Test title',
    comments: [
      {
        author: 'kimchy',
        number: 1
      },
      {
        author: 'nik9000',
        number: 2
      }
    ]
  }
)
puts response

response = client.search(
  index: 'test',
  body: {
    query: {
      nested: {
        path: 'comments',
        query: {
          match: {
            'comments.number' => 2
          }
        },
        inner_hits: {}
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "test",
  mappings: {
    properties: {
      comments: {
        type: "nested",
      },
    },
  },
});
console.log(response);

const response1 = await client.index({
  index: "test",
  id: 1,
  refresh: "true",
  document: {
    title: "Test title",
    comments: [
      {
        author: "kimchy",
        number: 1,
      },
      {
        author: "nik9000",
        number: 2,
      },
    ],
  },
});
console.log(response1);

const response2 = await client.search({
  index: "test",
  query: {
    nested: {
      path: "comments",
      query: {
        match: {
          "comments.number": 2,
        },
      },
      inner_hits: {},
    },
  },
});
console.log(response2);
PUT test
{
  "mappings": {
    "properties": {
      "comments": {
        "type": "nested"
      }
    }
  }
}

PUT test/_doc/1?refresh
{
  "title": "Test title",
  "comments": [
    {
      "author": "kimchy",
      "number": 1
    },
    {
      "author": "nik9000",
      "number": 2
    }
  ]
}

POST test/_search
{
  "query": {
    "nested": {
      "path": "comments",
      "query": {
        "match": {"comments.number" : 2}
      },
      "inner_hits": {} 
    }
  }
}

Определение вложенных совпадений вложенном запросе. Больше никаких опций определять не нужно.

Пример фрагмента ответа, который может быть сгенерирован из вышеупомянутого запроса поиска:

{
  ...,
  "hits": {
    "total" : {
        "value": 1,
        "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
      {
        "_index": "test",
        "_id": "1",
        "_score": 1.0,
        "_source": ...,
        "inner_hits": {
          "comments": { 
            "hits": {
              "total" : {
                  "value": 1,
                  "relation": "eq"
              },
              "max_score": 1.0,
              "hits": [
                {
                  "_index": "test",
                  "_id": "1",
                  "_nested": {
                    "field": "comments",
                    "offset": 1
                  },
                  "_score": 1.0,
                  "_source": {
                    "author": "nik9000",
                    "number": 2
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

Имя, используемое в определении вложенного совпадения в запросе поиска. Можно использовать пользовательский ключ с помощью опции name.

Метаданные _nested имеют решающее значение в приведенном выше примере, поскольку они определяют, откуда вложенное совпадение взято из вложенного вложенного объекта. field определяет поле массива объектов, из которого взято вложенное совпадение, а offset относительно его расположения в _source. Из-за сортировки и оценки фактическое расположение объектов совпадения в inner_hits обычно отличается от расположения вложенного внутреннего объекта.

По умолчанию _source также возвращается для объектов совпадения во вложенных inner_hits, но это можно изменить. С помощью фильтрации _source можно возвращать или отключать часть источника. Если на уровне вложенности определены сохраненные поля, то они также могут быть возвращены с помощью функции fields.

Важно, что _source, возвращаемые в результатах поиска внутри inner_hits, относятся к метаданным _nested. Таким образом, в приведенном выше примере возвращается только часть комментария для каждого вложенного совпадения, а не весь источник исходного документа, содержащего комментарий.

Вложенные внутренние совпадения и _source

Вложенные документы не имеют поля _source, потому что весь источник документа хранится с корневым документом в его поле _source. Чтобы включить источник только вложенного документа, источник корневого документа анализируется, и только соответствующая часть для вложенного документа включается в качестве источника в вложенное совпадение. Выполнение этого для каждого совпадающего вложенного документа влияет на время выполнения всего запроса поиска, особенно когда size и size вложенных совпадений устанавливаются выше по умолчанию. Чтобы избежать относительно дорогостоящей извлечения источника для вложенных внутренних совпадений, можно отключить включение источника и полагаться только на поля значений документа. Например:

resp = client.indices.create(
    index="test",
    mappings={
        "properties": {
            "comments": {
                "type": "nested"
            }
        }
    },
)
print(resp)

resp1 = client.index(
    index="test",
    id="1",
    refresh=True,
    document={
        "title": "Test title",
        "comments": [
            {
                "author": "kimchy",
                "text": "comment text"
            },
            {
                "author": "nik9000",
                "text": "words words words"
            }
        ]
    },
)
print(resp1)

resp2 = client.search(
    index="test",
    query={
        "nested": {
            "path": "comments",
            "query": {
                "match": {
                    "comments.text": "words"
                }
            },
            "inner_hits": {
                "_source": False,
                "docvalue_fields": [
                    "comments.text.keyword"
                ]
            }
        }
    },
)
print(resp2)
response = client.indices.create(
  index: 'test',
  body: {
    mappings: {
      properties: {
        comments: {
          type: 'nested'
        }
      }
    }
  }
)
puts response

response = client.index(
  index: 'test',
  id: 1,
  refresh: true,
  body: {
    title: 'Test title',
    comments: [
      {
        author: 'kimchy',
        text: 'comment text'
      },
      {
        author: 'nik9000',
        text: 'words words words'
      }
    ]
  }
)
puts response

response = client.search(
  index: 'test',
  body: {
    query: {
      nested: {
        path: 'comments',
        query: {
          match: {
            'comments.text' => 'words'
          }
        },
        inner_hits: {
          _source: false,
          docvalue_fields: [
            'comments.text.keyword'
          ]
        }
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "test",
  mappings: {
    properties: {
      comments: {
        type: "nested",
      },
    },
  },
});
console.log(response);

const response1 = await client.index({
  index: "test",
  id: 1,
  refresh: "true",
  document: {
    title: "Test title",
    comments: [
      {
        author: "kimchy",
        text: "comment text",
      },
      {
        author: "nik9000",
        text: "words words words",
      },
    ],
  },
});
console.log(response1);

const response2 = await client.search({
  index: "test",
  query: {
    nested: {
      path: "comments",
      query: {
        match: {
          "comments.text": "words",
        },
      },
      inner_hits: {
        _source: false,
        docvalue_fields: ["comments.text.keyword"],
      },
    },
  },
});
console.log(response2);
PUT test
{
  "mappings": {
    "properties": {
      "comments": {
        "type": "nested"
      }
    }
  }
}

PUT test/_doc/1?refresh
{
  "title": "Test title",
  "comments": [
    {
      "author": "kimchy",
      "text": "comment text"
    },
    {
      "author": "nik9000",
      "text": "words words words"
    }
  ]
}

POST test/_search
{
  "query": {
    "nested": {
      "path": "comments",
      "query": {
        "match": {"comments.text" : "words"}
      },
      "inner_hits": {
        "_source" : false,
        "docvalue_fields" : [
          "comments.text.keyword"
        ]
      }
    }
  }
}

Иерархические уровни полей вложенных объектов и вложенных совпадений.

Если отображение имеет несколько уровней иерархических вложенных полей объектов, каждый уровень можно получить через обозначение пути точкой. Например, если есть вложенное поле comments, которое содержит вложенное поле votes, и голоса должны быть возвращены непосредственно с корневыми совпадениями, то можно определить следующий путь:

resp = client.indices.create(
    index="test",
    mappings={
        "properties": {
            "comments": {
                "type": "nested",
                "properties": {
                    "votes": {
                        "type": "nested"
                    }
                }
            }
        }
    },
)
print(resp)

resp1 = client.index(
    index="test",
    id="1",
    refresh=True,
    document={
        "title": "Test title",
        "comments": [
            {
                "author": "kimchy",
                "text": "comment text",
                "votes": []
            },
            {
                "author": "nik9000",
                "text": "words words words",
                "votes": [
                    {
                        "value": 1,
                        "voter": "kimchy"
                    },
                    {
                        "value": -1,
                        "voter": "other"
                    }
                ]
            }
        ]
    },
)
print(resp1)

resp2 = client.search(
    index="test",
    query={
        "nested": {
            "path": "comments.votes",
            "query": {
                "match": {
                    "comments.votes.voter": "kimchy"
                }
            },
            "inner_hits": {}
        }
    },
)
print(resp2)
response = client.indices.create(
  index: 'test',
  body: {
    mappings: {
      properties: {
        comments: {
          type: 'nested',
          properties: {
            votes: {
              type: 'nested'
            }
          }
        }
      }
    }
  }
)
puts response

response = client.index(
  index: 'test',
  id: 1,
  refresh: true,
  body: {
    title: 'Test title',
    comments: [
      {
        author: 'kimchy',
        text: 'comment text',
        votes: []
      },
      {
        author: 'nik9000',
        text: 'words words words',
        votes: [
          {
            value: 1,
            voter: 'kimchy'
          },
          {
            value: -1,
            voter: 'other'
          }
        ]
      }
    ]
  }
)
puts response

response = client.search(
  index: 'test',
  body: {
    query: {
      nested: {
        path: 'comments.votes',
        query: {
          match: {
            'comments.votes.voter' => 'kimchy'
          }
        },
        inner_hits: {}
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "test",
  mappings: {
    properties: {
      comments: {
        type: "nested",
        properties: {
          votes: {
            type: "nested",
          },
        },
      },
    },
  },
});
console.log(response);

const response1 = await client.index({
  index: "test",
  id: 1,
  refresh: "true",
  document: {
    title: "Test title",
    comments: [
      {
        author: "kimchy",
        text: "comment text",
        votes: [],
      },
      {
        author: "nik9000",
        text: "words words words",
        votes: [
          {
            value: 1,
            voter: "kimchy",
          },
          {
            value: -1,
            voter: "other",
          },
        ],
      },
    ],
  },
});
console.log(response1);

const response2 = await client.search({
  index: "test",
  query: {
    nested: {
      path: "comments.votes",
      query: {
        match: {
          "comments.votes.voter": "kimchy",
        },
      },
      inner_hits: {},
    },
  },
});
console.log(response2);
PUT test
{
  "mappings": {
    "properties": {
      "comments": {
        "type": "nested",
        "properties": {
          "votes": {
            "type": "nested"
          }
        }
      }
    }
  }
}

PUT test/_doc/1?refresh
{
  "title": "Test title",
  "comments": [
    {
      "author": "kimchy",
      "text": "comment text",
      "votes": []
    },
    {
      "author": "nik9000",
      "text": "words words words",
      "votes": [
        {"value": 1 , "voter": "kimchy"},
        {"value": -1, "voter": "other"}
      ]
    }
  ]
}

POST test/_search
{
  "query": {
    "nested": {
      "path": "comments.votes",
        "query": {
          "match": {
            "comments.votes.voter": "kimchy"
          }
        },
        "inner_hits" : {}
    }
  }
}

Что выглядело бы так:

{
  ...,
  "hits": {
    "total" : {
        "value": 1,
        "relation": "eq"
    },
    "max_score": 0.6931471,
    "hits": [
      {
        "_index": "test",
        "_id": "1",
        "_score": 0.6931471,
        "_source": ...,
        "inner_hits": {
          "comments.votes": { 
            "hits": {
              "total" : {
                  "value": 1,
                  "relation": "eq"
              },
              "max_score": 0.6931471,
              "hits": [
                {
                  "_index": "test",
                  "_id": "1",
                  "_nested": {
                    "field": "comments",
                    "offset": 1,
                    "_nested": {
                      "field": "votes",
                      "offset": 0
                    }
                  },
                  "_score": 0.6931471,
                  "_source": {
                    "value": 1,
                    "voter": "kimchy"
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

Эта косвенная ссылка поддерживается только для вложенных внутренних совпадений.

Вложенные совпадения родитель/потомок

Вложенные совпадения родитель/потомок inner_hits могут быть использованы для включения родительских или дочерних:

resp = client.indices.create(
    index="test",
    mappings={
        "properties": {
            "my_join_field": {
                "type": "join",
                "relations": {
                    "my_parent": "my_child"
                }
            }
        }
    },
)
print(resp)

resp1 = client.index(
    index="test",
    id="1",
    refresh=True,
    document={
        "number": 1,
        "my_join_field": "my_parent"
    },
)
print(resp1)

resp2 = client.index(
    index="test",
    id="2",
    routing="1",
    refresh=True,
    document={
        "number": 1,
        "my_join_field": {
            "name": "my_child",
            "parent": "1"
        }
    },
)
print(resp2)

resp3 = client.search(
    index="test",
    query={
        "has_child": {
            "type": "my_child",
            "query": {
                "match": {
                    "number": 1
                }
            },
            "inner_hits": {}
        }
    },
)
print(resp3)
response = client.indices.create(
  index: 'test',
  body: {
    mappings: {
      properties: {
        my_join_field: {
          type: 'join',
          relations: {
            my_parent: 'my_child'
          }
        }
      }
    }
  }
)
puts response

response = client.index(
  index: 'test',
  id: 1,
  refresh: true,
  body: {
    number: 1,
    my_join_field: 'my_parent'
  }
)
puts response

response = client.index(
  index: 'test',
  id: 2,
  routing: 1,
  refresh: true,
  body: {
    number: 1,
    my_join_field: {
      name: 'my_child',
      parent: '1'
    }
  }
)
puts response

response = client.search(
  index: 'test',
  body: {
    query: {
      has_child: {
        type: 'my_child',
        query: {
          match: {
            number: 1
          }
        },
        inner_hits: {}
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "test",
  mappings: {
    properties: {
      my_join_field: {
        type: "join",
        relations: {
          my_parent: "my_child",
        },
      },
    },
  },
});
console.log(response);

const response1 = await client.index({
  index: "test",
  id: 1,
  refresh: "true",
  document: {
    number: 1,
    my_join_field: "my_parent",
  },
});
console.log(response1);

const response2 = await client.index({
  index: "test",
  id: 2,
  routing: 1,
  refresh: "true",
  document: {
    number: 1,
    my_join_field: {
      name: "my_child",
      parent: "1",
    },
  },
});
console.log(response2);

const response3 = await client.search({
  index: "test",
  query: {
    has_child: {
      type: "my_child",
      query: {
        match: {
          number: 1,
        },
      },
      inner_hits: {},
    },
  },
});
console.log(response3);
PUT test
{
  "mappings": {
    "properties": {
      "my_join_field": {
        "type": "join",
        "relations": {
          "my_parent": "my_child"
        }
      }
    }
  }
}

PUT test/_doc/1?refresh
{
  "number": 1,
  "my_join_field": "my_parent"
}

PUT test/_doc/2?routing=1&refresh
{
  "number": 1,
  "my_join_field": {
    "name": "my_child",
    "parent": "1"
  }
}

POST test/_search
{
  "query": {
    "has_child": {
      "type": "my_child",
      "query": {
        "match": {
          "number": 1
        }
      },
      "inner_hits": {}    
    }
  }
}

Определение вложенного совпадения, как и в примере с вложенными объектами.

Пример фрагмента ответа, который может быть сгенерирован из вышеупомянутого запроса поиска:

{
  ...,
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
      {
        "_index": "test",
        "_id": "1",
        "_score": 1.0,
        "_source": {
          "number": 1,
          "my_join_field": "my_parent"
        },
        "inner_hits": {
          "my_child": {
            "hits": {
              "total": {
                "value": 1,
                "relation": "eq"
              },
              "max_score": 1.0,
              "hits": [
                {
                  "_index": "test",
                  "_id": "2",
                  "_score": 1.0,
                  "_routing": "1",
                  "_source": {
                    "number": 1,
                    "my_join_field": {
                      "name": "my_child",
                      "parent": "1"
                    }
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/inner-hits.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API