Elasticsearch вложенная сортировка
Я пытаюсь выполнить вложенную сортировку в Elasticsearch, но пока не удалось.
Моя структура данных:
{ "_id" : 1,
"authorList" : [
{"lastName":"hawking", "firstName":"stephan"},
{"lastName":"frey", "firstName":"richard"}
]
}
{ "_id" : 2,
"authorList" : [
{"lastName":"roger", "firstName":"christina"},
{"lastName":"freud", "firstName":"damian"}
]
}
Я хочу отсортировать документы по фамилии первых авторов в документах.
Используемое картирование:
"authorList" : { "type" : "nested", "properties" : {"lastName":{"type":"keyword"}}}
Сортировка с использованием SearchRequestBuilder (JAVA):
searchRequestBuilder.addSort(
SortBuilders.fieldSort("authorList.lastName")
.order(SortOrder.ASC)
.sortMode(SortMode.MIN)
.setNestedPath("authorList")
)
Это работает, но не дает желаемого результата (например, сначала "хокинг", затем "роджер").
Я что-то пропустил? Есть ли способ указать Elasticsearch для доступа к index=0 массива authorList? Есть ли отображение / нормализатор для индексации первой записи массива отдельно?
1 ответ
Вложенные документы не сохраняются в виде простого массива или списка. Они управляются изнутри Elasticsearch:
Elasticsearch все еще принципиально плоский, но он управляет вложенными отношениями внутренне, чтобы создать видимость вложенной иерархии. Когда вы создаете вложенный документ, Elasticsearch фактически индексирует два отдельных документа (корневой объект и вложенный объект), а затем связывает их внутренне. (подробнее здесь)
Я думаю, что вам нужно предоставить некоторую дополнительную информацию для эластичного поиска, которая будет показателем того, какой автор является "первичным / первым". Достаточно поместить это дополнительное поле только одному автору во вложенном объекте (ваше отображение может остаться прежним), примерно так:
{
"authorList" : [
{"lastName":"roger", "firstName":"christina", "authorOrder": 1},
{"lastName":"freud", "firstName":"damian"}
]
},
{
"authorList" : [
{"lastName":"hawking", "firstName":"stephan", "authorOrder": 1},
{"lastName":"adams", "firstName": "mark" }
{"lastName":"frey", "firstName":"richard"}
]
},
{
"authorList" : [
{"lastName":"adams", "firstName":"monica", "authorOrder": 1},
{"lastName":"adams", "firstName":"richard"}
]
}
Тогда запрос может быть:
{
"query" : {
"nested" : {
"query" : {
"bool" : {
"must" : [
{
"match" : {
"authorList.authorOrder" : 1
}
}
]
}
},
"path" : "authorList"
}
},
"sort" : [
{
"authorList.lastName" : {
"order" : "asc",
"nested_filter" : {
"bool" : {
"must" : [
{
"match" : {
"authorList.authorOrder" : 1
}
}
]
}
},
"nested_path" : "authorList"
}
}
]
}
И с Java API:
QueryBuilder matchFirst = QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("authorList.authorOrder", 1));
QueryBuilder mainQuery = QueryBuilders.nestedQuery("authorList", matchFirst, ScoreMode.None);
SortBuilder sb = SortBuilders.fieldSort("authorList.lastName")
.order(SortOrder.ASC)
.setNestedPath("authorList")
.setNestedFilter(matchFirst);
SearchRequestBuilder builder = client.prepareSearch("test")
.setSize(50)
.setQuery(mainQuery)
.addSort(sb);
Обратите внимание, что SortBuilder
имеет .setNestedFilter(matchAll)
Это означает, что сортировка основана на authorList.lastName
поле, но только из ваших "первичных / первых" вложенных элементов. Без него asticsearch сначала отсортировал бы все вложенные документы, выбрал первый элемент из отсортированного по возрастанию списка и на основании этого отсортировал бы родительские документы. Таким образом, документ с "Хокингом" может быть первым, поскольку он имеет фамилию "Адамс".
Конечный результат:
"authorList" : [
{"lastName":"adams", "firstName":"monica", "authorOrder": 1},
{"lastName":"adams", "firstName":"richard"}
],
}
"authorList" : [
{"lastName":"hawking", "firstName":"stephan", "authorOrder": 1},
{"lastName":"adams", "firstName":"mark"},
{"lastName":"frey", "firstName":"richard"}
]
},
{
"authorList" : [
{"lastName":"roger", "firstName":"christina", "authorOrder": 1},
{"lastName":"freud", "firstName":"damian"}
]
}