Получите вложенные документы с фильтром на Elasticsearch 5

У меня есть следующий документ сопоставлен в ES 5:

{
"appName" : {
"mappings" : {
"market_audit" : {
"properties" : {
"generation_date": {
"type": "date"},
"customers" : {
"type" : "nested",
"properties" : {
"customer_id" : {
"type" : "integer"},
[... other properties ...]
}

Несколько записей в узле «клиенты» могут иметь один и тот же customer_id, и я пытаюсь получить только записи, имеющие определенный customer_id (т. Е. «1»), вместе с «generation_date» документа верхнего уровня (только последний документ должен быть обработан).

Я смог придумать следующий запрос:

{
"query": {},
"sort": [
{ "generation_date": "desc" }
],
"size": 1,
"aggregations": {
"nested": {
"nested": {
"path": "customers"},
"aggregations": {
"filter": {
"filter": {
"match": {
"customers.customer_id": {
"query": "1"}
}
},
"aggregations": {
"tophits_agg": {
"top_hits": {}
}
}
}
}
}
}
}

Этот запрос возвращает мне интересующие меня данные, расположенные в массиве «агрегаты» (вместе с «попаданиями», которые содержат весь документ). Проблема здесь заключается в том, что используемая мной среда (пакет ElasticSearch ONGR вместе с пакетом DSL, использующий Symfony3) жалуется каждый раз, когда я пытаюсь получить доступ к фактическим данным, на которые нет доступных блоков.

Я прочитал документацию ES, но не смог придумать рабочий запрос, который добавил сегменты. Я уверен, что я что-то упустил, небольшая помощь будет более чем приветствоваться. Если у вас есть идея о том, как соответствующим образом изменить запрос, я думаю, что могу придумать код PHP для его создания.

РЕДАКТИРОВАТЬ: так как этот вопрос получил несколько просмотров и ответа нет (и я все еще застрял), я бы согласился на любой запрос, который позволяет мне получить информацию о конкретном «клиенте» (используя customer_id) из последнего сгенерированного документа (в соответствии с поле «generation_date»). Вопрос, который я дал, это то, что я смог придумать, и я уверен, что есть гораздо лучший способ сделать это. Предложения, может быть?

РЕДАКТИРОВАТЬ 2:
Вот данные, отправленные в ES:

{
"index": {
"_type": "market_data_audit_document"}
}
{
"customers": [
{
"customer_id": 1,
"colocation_name": "colo1",
"colocation_id": 26,
"device_name": "device 1",
"channels": [
{
"name": "channel1-5",
"multicast":"1.2.1.5",
"sugar_state":4,
"network_state":1
}
]
},
{
"customer_id":2,
"colocation_name":"colo2",
"colocation_id":27,
"device_name":"device 2",
"channels": [
{
"name":"channel2-5",
"multicast":"1.2.2.5",
"sugar_state":4,
"network_state":1
}
]
},
{
"customer_id":3,
"colocation_name":"colo3",
"colocation_id":28,
"device_name":"device 3",
"channels": [
{
"name":"channel3-5",
"multicast":"1.2.3.5",
"sugar_state":4,
"network_state":1
}
]
},
{
"customer_id":4,
"colocation_name":"colo4",
"colocation_id":29,
"device_name":"device 4","channels": [
{
"name":"channel4-5",
"multicast":"1.2.4.5",
"sugar_state":4,
"network_state":1
}
]
},
{
"customer_id":5,
"colocation_name":"colo5",
"colocation_id":30,
"device_name":"device 5",
"channels": [
{
"name":"channel5-5",
"multicast":"1.2.5.5",
"sugar_state":4,
"network_state":1
}
]
}
],
"generation_date":"2017-02-27T10:55:45+0100"}

К сожалению, когда я попытался отправить запрос, указанный в этом посте, я обнаружил, что агрегация не делает то, что я ожидал: она возвращает «хорошие» данные, но из ВСЕ сохраненные документы! Вот пример вывода:

{
"timed_out" : false,
"took" : 60,
"hits" : {
"total" : 2,
"hits" : [
{
"_source" : {
"customers" : [
{
"colocation_id" : 26,
"channels" : [
{
"name" : "channel1-5",
"sugar_state" : 4,
"network_state" : 1,
"multicast" : "1.2.1.5"}
],
"customer_id" : 1,
"colocation_name" : "colo1",
"device_name" : "device 1"},
{
"colocation_id" : 27,
"channels" : [
{
"multicast" : "1.2.2.5",
"network_state" : 1,
"name" : "channel2-5",
"sugar_state" : 4
}
],
"customer_id" : 2,
"device_name" : "device 2",
"colocation_name" : "colo2"},
{
"device_name" : "device 3",
"colocation_name" : "colo3",
"customer_id" : 3,
"channels" : [
{
"multicast" : "1.2.3.5",
"network_state" : 1,
"sugar_state" : 4,
"name" : "channel3-5"}
],
"colocation_id" : 28
},
{
"channels" : [
{
"sugar_state" : 4,
"name" : "channel4-5",
"multicast" : "1.2.4.5",
"network_state" : 1
}
],
"customer_id" : 4,
"colocation_id" : 29,
"colocation_name" : "colo4",
"device_name" : "device 4"},
{
"device_name" : "device 5",
"colocation_name" : "colo5",
"colocation_id" : 30,
"channels" : [
{
"sugar_state" : 4,
"name" : "channel5-5",
"multicast" : "1.2.5.5",
"network_state" : 1
}
],
"customer_id" : 5
}
],
"generation_date" : "2017-02-27T11:45:37+0100"},
"_type" : "market_data_audit_document",
"sort" : [
1488192337000
],
"_index" : "mars",
"_score" : null,
"_id" : "AVp_LPeJdrvi0cWb8CrL"}
],
"max_score" : null
},
"aggregations" : {
"nested" : {
"doc_count" : 10,
"filter" : {
"doc_count" : 2,
"tophits_agg" : {
"hits" : {
"max_score" : 1,
"total" : 2,
"hits" : [
{
"_nested" : {
"offset" : 0,
"field" : "customers"},
"_score" : 1,
"_source" : {
"channels" : [
{
"name" : "channel1-5",
"sugar_state" : 4,
"multicast" : "1.2.1.5",
"network_state" : 1
}
],
"customer_id" : 1,
"colocation_id" : 26,
"colocation_name" : "colo1",
"device_name" : "device 1"}
},
{
"_source" : {
"colocation_id" : 26,
"customer_id" : 1,
"channels" : [
{
"multicast" : "1.2.1.5",
"network_state" : 1,
"name" : "channel1-5",
"sugar_state" : 4
}
],
"device_name" : "device 1",
"colocation_name" : "colo1"},
"_nested" : {
"offset" : 0,
"field" : "customers"},
"_score" : 1
}
]
}
}
}
}
},
"_shards" : {
"total" : 13,
"successful" : 1,
"failures" : [
{
"reason" : {
"index" : ".kibana",
"index_uuid" : "bTkwoysSQ0y8Tt9yYFRStg",
"type" : "query_shard_exception",
"reason" : "No mapping found for [generation_date] in order to sort on"},
"shard" : 0,
"node" : "4ZUgOm4VRry6EtUK15UH3Q",
"index" : ".kibana"},
{
"reason" : {
"index_uuid" : "lN2mVF9bRjuDtiBF2qACfA",
"index" : "archiv1_log",
"type" : "query_shard_exception",
"reason" : "No mapping found for [generation_date] in order to sort on"},
"shard" : 0,
"node" : "4ZUgOm4VRry6EtUK15UH3Q",
"index" : "archiv1_log"},
{
"index" : "archiv1_session",
"shard" : 0,
"node" : "4ZUgOm4VRry6EtUK15UH3Q",
"reason" : {
"type" : "query_shard_exception",
"index" : "archiv1_session",
"index_uuid" : "cmMAW04YTtCb0khEqHpNyA",
"reason" : "No mapping found for [generation_date] in order to sort on"}
},
{
"shard" : 0,
"node" : "4ZUgOm4VRry6EtUK15UH3Q",
"reason" : {
"reason" : "No mapping found for [generation_date] in order to sort on",
"index" : "archiv1_users_dev",
"index_uuid" : "AH48gIf5T0CXSQaE7uvVRg",
"type" : "query_shard_exception"},
"index" : "archiv1_users_dev"}
],
"failed" : 12
}
}

2

Решение

На основании вашего описания:

  • вы храните документы на эластичный поиск с кучей свойств
  • каждый документ содержит список клиентов в массиве (вложенные документы)
  • Вы хотите извлечь только вложенный документ, связанный с customer.id
  • ваша библиотека не управляет ответом Elasticsearch без сегментов
  • вы ожидаете, что Elasticsearch вернет вложенные документы

проблема

Существует 2 вида скоплений:

  • ковши
  • метрика

В вашем случае у вас есть 2 Агрегации под вложенными Agg: Фильтр и Метрика.
Фильтр:

Обходной путь:

Я сомневаюсь, что ваша PHP-библиотека будет правильно обрабатывать результаты вложенного агрегирования, но вы могли бы использовать фильтрs вместо фильтров агрегации, чтобы получить список сегментов

{
"aggregations": {
"nested": {
"nested": {
"path": "customers"},
"aggregations": {
"filters_customer": {
"filters": {
"filters": [
{
"match": {
"customers.customer_id": "1"}
}
]
},
"aggregations": {
"top_hits_customer": {
"top_hits": {}
}
}
}
}
}
}
}

Обеспечит что-то вроде:

{
"aggregations": {
"nested": {
"doc_count": 15,
"filters_customer": {
"buckets": [
{
"doc_count": 3,
"top_hits_customer": {
"hits": {
"total": 3,
"max_score": 1,
"hits": [
{
"_nested": {
"field": "customers",
"offset": 0
},
"_score": 1,
"_source": {
"customer_id": 1,
"foo": "bar"}
},
{
"_nested": {
"field": "customers",
"offset": 0
},
"_score": 1,
"_source": {
"customer_id": 1,
"foo": "bar"}
},
{
"_nested": {
"field": "customers",
"offset": 0
},
"_score": 1,
"_source": {
"customer_id": 1,
"foo": "bar"}
}
]
}
}
}
]
}
}
}
}

Обратите внимание на ваш EDIT 2

Elasticsearch выполнит поиск по всем документам, а не по документу «TOP 1», основываясь на дате вашего отчета. Чтобы разделить результаты по отчетам, используйте группу терминов на дату отчета:

{
"query": {},
"size": 0,
"aggregations": {
"grp_report": {
"terms": {
"field": "generation_date"},
"aggregations": {
"nested_customers": {
"nested": {
"path": "customers"},
"aggregations": {
"filters_customer": {
"filters": {
"filters": [
{
"match": {
"customers.customer_id": "1"}
}
]
},
"aggregations": {
"top_hits_customer": {
"top_hits": {}
}
}
}
}
}
}
}
}
}

Советы:

Избегайте сложных документов, предпочитайте разбивать отчет на небольшие документы со связанным ключом (например, reportId). Вы сможете легко фильтровать и агрегировать без вложенного документа. Добавьте в документ клиента информацию о том, что вы будете фильтровать по всем типам (избыточность не является проблемой в этом случае).

Примеры использования:

  • список отчетов
  • показывать информацию о клиентах по отчетам
  • показать историю для клиента в нескольких отчетах

Пример текущего документа: / indexName / market_audit

{
"generation_date": "...",
"customers": [
{
"id": 1,
"foo": "bar 1"},
{
"id": 2,
"foo": "bar 2"},
{
"id": 3,
"foo": "bar 3"}
]
}

Переформатированный документ:

/ IndexName / market_audit_report

{
"report_id" : "123456""generation_date": "...",
"foo":"bar"}

/ indexName / market_audit_customer Documents

{
"report_id" : "123456""customer_id": 1,
"foo": "bar 1"}{
"report_id" : "123456""customer_id": 2,
"foo": "bar 2"}{
"report_id" : "123456""customer_id": 3,
"foo": "bar 3"}

Если вам известен идентификатор вашего отчета, вы сможете получить все свои данные за один запрос:

  • фильтр по идентификатору отчета
  • термин агрегация по типу
    • фильтр по типу отчета
      • агрегация top_hit для получения отчета
    • фильтр агрегации, чтобы получить только тип клиента и идентификатор клиента 1
      • агрегация top_hit для клиента 1

Или же

  • фильтр по идентификатору отчета
  • термин агрегация по типу
    • фильтр по типу отчета
      • агрегация top_hit для получения отчета
    • агрегация терминов по идентификатору клиента
      • агрегация top_hit для получения информации о клиенте

Размер агрегации лучших хитов

Не забудьте предоставить size в ваших top_hits в противном случае вы получите только топ 3

1

Другие решения

Чтение эластичного поиска первой строки определение агрегатов Я думаю, что вы не очень хорошо понимаете, как это работает:

Структура агрегирования помогает предоставлять агрегированные данные на основе
поисковый запрос

Так как ваш запрос не имеет никакого фильтра, возврате ВСЕ сохраненные документы в hits.hits Объекты — это ожидаемый результат.
Тогда вы используете filter агрегация, которая поможет вам получить нужные документы, но они находятся в aggs собственность возвращена dict
Если я прав, я бы рекомендовал вам сделать это как можно проще, поэтому вот мой угаданный вопрос

{
"query": {
"filtered": {
"filter": {
"nested": {
"path" : "customers",
"filter": {
"bool": {
"must" : [
"term": {"customer_id" : "1"}
]
}
}
}
}
}
},
"aggregations": {
"tophits_agg": {
"top_hits": {}
}
}
}
0

По вопросам рекламы [email protected]