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"}
    ]
}
Другие вопросы по тегам