Потребность во вложенном типе (Nested Type)

Для того, чтобы понять зачем это нужно рассмотрим пример.

Добавляем водителей, которые находятся в разных городах и работают в разных компаниях:

POST denormalized_cars/_bulk
{"index":{"_id":1}}
{"id":"1","title":"True","drivers":[{"name":"Egor Hdasda","company":{"country":{"name":"Shymkent"},"name":"Ebobo"}},{"name":"Gabdi Popopo","company":{"country":{"name":"Astana"},"name":"Olx"}}]}
{"index":{"_id":2}}
{"id":"2","title":"False","drivers":[{"name":"Gabdi Overton","company":{"country":{"name":"Shymkent"},"name":"Globex"}},{"name":"Egor Fghgfhgfh","company":{"country":{"name":"Almaty"},"name":"Olx"}}]}
{"index":{"_id":3}}
{"id":"3","title":"False","drivers":[{"name":"Egor Hdasda","company":{"country":{"name":"Shymkent"},"name":"Ebobo"}},{"name":"Gabdi Popopo","company":{"country":{"name":"Astana"},"name":"Olx"}},{"name":"Tim Roes","company":{"country":{"name":"Japan"},"name":"Kiwi"}}]}

Делаем запрос для выборки водителя Egor, работающего в Olx:

GET denormalized_cars/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "drivers.name": "egor"
          }
        },
        {
          "match": {
            "drivers.company.name.keyword": "Olx"
          }
        }
      ]
    }
  }
}

А в итоге получаем ответ, в котором все водители (у правильного в title прописано true).

Теперь разберёмся почему собственно так вышло. Поля name и company.name являются внутренними объектами JSON поля массива drivers. Когда вы индексируете документ в Elasticsearch, объект JSON разделяется в соответствии с внутренними требованиями Lucene. Ожидаемая связь между name и company.name теряется. Т.е они разбиваются на 2 несвязанных поля.

Для того, чтобы не было таких ситуаций как раз и используется вложенной тип.

Вложенный тип данных

Создаём индекс используя nested:

PUT nested_cars
{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "title": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword"
          }
        }
      },
      "drivers": {
        "type": "nested",
        "properties": {
          "name": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword"
              }
            }
          },
          "company": {
            "type": "object",
            "properties": {
              "name": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword"
                  }
                }
              },
              "country": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "text",
                    "fields": {
                      "keyword": {
                        "type": "keyword"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

POST nested_cars/_bulk
{"index":{"_id":1}}
{"id":"1","title":"False","drivers":[{"name":"Egor Hdasda","company":{"country":{"name":"Shymkent"},"name":"Ebobo"}},{"name":"Gabdi Popopo","company":{"country":{"name":"Astana"},"name":"Olx"}}]}
{"index":{"_id":2}}
{"id":"2","title":"True","drivers":[{"name":"Gabdi Overton","company":{"country":{"name":"Shymkent"},"name":"Globex"}},{"name":"Egor Fghgfhgfh","company":{"country":{"name":"Almaty"},"name":"Olx"}}]}
{"index":{"_id":3}}
{"id":"3","title":"False","drivers":[{"name":"Egor Hdasda","company":{"country":{"name":"Shymkent"},"name":"Ebobo"}},{"name":"Gabdi Popopo","company":{"country":{"name":"Astana"},"name":"Olx"}},{"name":"Tim Roes","company":{"country":{"name":"Japan"},"name":"Kiwi"}}]}

И делаем запрос используя nested:

GET nested_cars/_search
{
  "query": {
    "nested": {
      "path": "drivers",
      "query": {
        "bool": {
          "must": [
            {
              "match": {
                "drivers.name": "egor"
              }
            },
            {
              "match": {
                "drivers.company.name.keyword": "Olx"
              }
            }
          ]
        }
      }
    }
  }
}

И получаем в итоге только один документ с правильным водителем:

          "title" : "True",
          "drivers" : [
            {
              "name" : "Gabdi Overton",
              "company" : {
                "country" : {
                  "name" : "Shymkent"
                },
                "name" : "Globex"
              }
            },
            {
              "name" : "Egor Fghgfhgfh",
              "company" : {
                "country" : {
                  "name" : "Almaty"
                },
                "name" : "Olx"
              }
            }
          ]

В итоге

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