Elasticsearch: сортировка по различным полям в зависимости от типа



У меня есть два типа в моем индексе (Event и City), и я пытаюсь отсортировать их все вместе по дате. Однако имя поля даты отличается для каждого типа:
для Event значение находится в поле updated_at, а для City Дата находится в поле update_at в одном из вложенных объектов его city_events вложенного массива объектов (обратите внимание на фильтрацию по region_id).



Я попытался указать каждое поле в массиве сортировки следующим образом:



  "sort": [
{
"city_events.updated_at": {
"order": "desc",
"nested_path": "city_events",
"nested_filter": {
"term": {
"city_events.region_id": 1
}
}
}
},
{
"updated_at": "desc"
}
]


Но, к сожалению, это не смешивает два типа вместе. Вместо этого он сначала сортирует все Cities по их вложенному city_events.updated_at полю, а затем добавляет все Events внизу, отсортированное по их полю updated_at. Как мне смешать и отсортировать их вместе?



В качестве альтернативного решения я попытался отсортировать только по вложенному полю city_events.updated_at и указать "missing": "updated_at", однако это вызвало ошибку "number_format_exception", несмотря на то, что оба поля имеют одинаковый формат:



{
"error": {
"root_cause": [
{
"type": "number_format_exception",
"reason": "For input string: "updated_at""
}
],
"type": "search_phase_execution_exception",
"reason": "all shards failed",
"phase": "query_fetch",
"grouped": true,
"failed_shards": [
{
"shard": 0,
"index": "events_1461095196252",
"node": "sYQstSw_SN62ojmXgGjPlg",
"reason": {
"type": "number_format_exception",
"reason": "For input string: "updated_at""
}
}
]
},
"status": 400
}


Обновление 1: на основе ответа Андрея Стефана ниже я попытался разработать заводной скрипт, который зацикливался на city_events для каждого City документа, выбирая тот, который соответствует region_id, а затем возвращая это city_event значение updated_at для оценки, но имел проблемы с доступом к вложенным полям внутри скрипта: https://stackoverflow.com/questions/36781476/elasticsearch-access-fields-inside-array-of-nested-objects-in-a-groovy-script

907   2  

2 ответов:

Попробуйте script сортировку на основе, и вам понадобится, чтобы ваше поле nested имело include_in_parent: true, чтобы быть доступным в скрипте:

    "city_events": {
      "type": "nested",
      "include_in_parent": true, 
      "properties": {
        "updated_at": {
          "type": "date"
        }
      }
    }

И сортировочная часть:

  "sort": {
    "_script": {
      "type": "number",
      "script": {
        "inline": "if (doc['_type'].value=='Event') return doc['updated_at'].date.getMillis(); else if (doc['_type'].value=='City') return doc['city_events.updated_at'].date.getMillis()",
        "lang": "groovy"
      },
      "order": "desc"
    }
  }

ПОЗЖЕ РЕДАКТИРОВАТЬ

Даже если я добавлю условие city_events.region_id==1 к скрипту Groovy, который не будет чувствовать Elasticsearch, это будет чистое Программирование Groovy, а не сила Elasticsearch.

Я пробовал другие подходы (все в ES 2.3.1):

  • copy_to из регулярного поля updated_at к полю nested внутри Event, так что регулярная сортировка nested выполняется по всем типам. Это не сработало.
  • даже если бы copy_to сработало, Elasticsearch не соответствовал бы "term": {"city_events.region_id": 1} (Поскольку region_id не существует в Event) из части sort в типе Event и для этих значений использовал бы -9223372036854776000 вместо фактической даты (эти значения получены из тестов, которые я выполнил).
  • используйте также поле nested в Event и во время индексации поместите это поле updated_at в это вложенное поле. Это не будет работать по той же причине, что и попытка №2 выше: в Event также должен быть region_id, чтобы фильтр nested из части sort применялся для обоих типов.

Что я бы предложил, какправильный способ справиться с этим, это немного переосмыслить структуру данных так, чтобы Сортировочная часть (по крайней мере) следовала способу Elasticsearch. Ваши типы называются City и Event, а внутри City у вас есть список (вложенных) city_events. Не можете ли вы включить Event в City и дублировать детали событий в каждом городе? Это не обязательно должна быть нормализованная структура данных RDB. Напротив, ES более счастлив с ненормализованными данными.


Для полноты картины, но я не рекомендую это :

  "sort": {
    "_script": {
      "type": "number",
      "script": {
        "inline": "if (doc['_type'].value=='Event') return doc['updated_at'].date.getMillis(); else if (doc['_type'].value=='City') {for(nestedObj in _source.city_events) {if(nestedObj.region_id==1) return nestedObj.updated_at.toLong();}}",
        "lang": "groovy"
      },
      "order": "desc"
    }
  }

обратите внимание, что я не сделал все необходимые проверки в скрипте Groovy выше (например, проверяя, действительно ли в документе есть вложенные объекты).

Данные Elasticsearch должны быть оптимизированы для чтения. Лучшим решением было бы добавить общее поле для обоих типов, хранящее соответствующее значение сортировки.

Что касается нескольких вложенных объектов в пределах города: я бы все равно хранил наиболее релевантное (последнее) значение на уровне города.

Comments

    Ничего не найдено.