Что вам нужно сделать с точки зрения Elasticsearch:
- фильтрация «родительских» документов по требуемым критериям (например, наличие
GPU
в title
или упоминание Nvidia
в списке companies
);
- группирует «вложенные» документы по определенным критериям: корзина (например,
company_id
);
- подсчитайте, сколько "вложенных" документов существует в каждом сегменте.
Каждый из nested
объектов в массиве индексируется как отдельный скрытый документ , что немного усложняет жизнь. Посмотрим, как их агрегировать.
Итак, как агрегировать и считать nested
документы?
Этого можно достичь с помощью комбинации вложенных , терминов и top_hits агрегации:
POST my_index/doc/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "GPU"
}
},
{
"nested": {
"path": "companies",
"query": {
"match_all": {}
}
}
}
]
}
},
"aggs": {
"Extract nested": {
"nested": {
"path": "companies"
},
"aggs": {
"By company id": {
"terms": {
"field": "companies.company_id"
},
"aggs": {
"Examples of such company_id": {
"top_hits": {
"size": 1
}
}
}
}
}
}
}
}
Это даст следующий вывод:
{
...
"hits": { ... },
"aggregations": {
"Extract nested": {
"doc_count": 4, <== How many "nested" documents there were?
"By company id": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": 3, <== this bucket's key: "company_id": 3
"doc_count": 2, <== how many "nested" documents there were with such company_id?
"Examples of such company_id": {
"hits": {
"total": 2,
"max_score": 1.5897496,
"hits": [ <== an example, "top hit" for such company_id
{
"_nested": {
"field": "companies",
"offset": 1
},
"_score": 1.5897496,
"_source": {
"company_id": 3,
"name": "Nvidia"
}
}
]
}
}
},
{
"key": 1,
"doc_count": 1,
"Examples of such company_id": {
"hits": {
"total": 1,
"max_score": 1.5897496,
"hits": [
{
"_nested": {
"field": "companies",
"offset": 0
},
"_score": 1.5897496,
"_source": {
"company_id": 1,
"name": "AMD"
}
}
]
}
}
}
]
}
}
}
}
Обратите внимание, что для Nvidia
у нас есть "doc_count": 2
.
Но что, если мы хотим посчитать количество "родительских" объектов, у которых есть Nvidia
против Intel
?
Что если мы хотим подсчитать родительские объекты на основе nested
сегмента?
Этого можно достичь с помощью агрегации reverse_nested
.
Нам нужно немного изменить наш запрос:
POST my_index/doc/_search
{
"query": { ... },
"aggs": {
"Extract nested": {
"nested": {
"path": "companies"
},
"aggs": {
"By company id": {
"terms": {
"field": "companies.company_id"
},
"aggs": {
"Examples of such company_id": {
"top_hits": {
"size": 1
}
},
"original doc count": { <== we ask ES to count how many there are parent docs
"reverse_nested": {}
}
}
}
}
}
}
}
Результат будет выглядеть так:
{
...
"hits": { ... },
"aggregations": {
"Extract nested": {
"doc_count": 3,
"By company id": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": 3,
"doc_count": 2,
"original doc count": {
"doc_count": 2 <== how many "parent" documents have such company_id
},
"Examples of such company_id": {
"hits": {
"total": 2,
"max_score": 1.5897496,
"hits": [
{
"_nested": {
"field": "companies",
"offset": 1
},
"_score": 1.5897496,
"_source": {
"company_id": 3,
"name": "Nvidia"
}
}
]
}
}
},
{
"key": 1,
"doc_count": 1,
"original doc count": {
"doc_count": 1
},
"Examples of such company_id": {
"hits": {
"total": 1,
"max_score": 1.5897496,
"hits": [
{
"_nested": {
"field": "companies",
"offset": 0
},
"_score": 1.5897496,
"_source": {
"company_id": 1,
"name": "AMD"
}
}
]
}
}
}
]
}
}
}
}
Как я могу определить разницу?
Чтобы сделать разницу очевидной, давайте немного изменим данные и добавим еще один элемент Nvidia
в список документов:
PUT my_index/doc/2
{
"title" : "GPU release 2018-01-10",
"companies" : [
{ "company_id" : 1, "name" : "AMD" },
{ "company_id" : 3, "name" : "Nvidia" },
{ "company_id" : 3, "name" : "Nvidia" }
]
}
Последний запрос (с reverse_nested
) даст нам следующее:
"By company id": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": 3,
"doc_count": 3, <== 3 "nested" documents with Nvidia
"original doc count": {
"doc_count": 2 <== but only 2 "parent" documents
},
"Examples of such company_id": {
"hits": {
"total": 3,
"max_score": 1.5897496,
"hits": [
{
"_nested": {
"field": "companies",
"offset": 2
},
"_score": 1.5897496,
"_source": {
"company_id": 3,
"name": "Nvidia"
}
}
]
}
}
},
Как видите, это тонкое различие, которое трудно понять, но оно полностью меняет семантику.
А как насчет производительности?
Хотя для большинства случаев производительности nested
запросов и агрегаций должно быть достаточно, конечно, это сопряжено с определенными затратами. Поэтому рекомендуется избегать использования nested
или родительских и дочерних типов при настройке скорости поиска .
В Elasticsearch лучшая производительность часто достигается с помощью денормализации , хотя единого рецепта не существует, и вам следует выбирать модель данных в зависимости от ваших потребностей.
Надеюсь, это прояснит вам эту вещь nested
!