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
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