Перекладывание SQL-запроса на эластик без изменения формата данных (трансформирования перед индексацией)
Вводные:
- Документы с коллекциями nested.
- Коллекция представляет собой историю одной и той же сущности. Одна версия является актуальной и отличается отсутствием флага archived.
- Актуальная версия может как присутствовать, так и нет. В случае отсутствия поиск производится по архивированным версиям.
- Nested ожидаемо содержит прочие данные, которые участвуют в запросе.
Решение состоит из композиции логических запросов:
or:
- and:
# проверка что актуальная версия вообще существует
- nested:
not:
exists: archived # alt. archived = false
- nested:
and:
# проверка что конкретная версия (из всех доступных)
# является актуальной
- not:
exists: archived
- <стандартный запрос>
- and:
# проверка несуществования nested без archived,
# i.e. что актуальной версии попросту нет
# два not нельзя схлопнуть из-за наличия nested
# (выступающего экзистенциальным квантификатором)
# между ними. Если бы в эластике был универсальный
# for-all, тогда бы это свелось к for all ... exists: archived
- not:
nested:
not:
exists: archived
# and на archived = true не требуется, т.к. он подразумевается
# выполнением предыдущего подзапроса
- nested:
<стандартный запрос>
см. query.json
Данный подход найдет необходимые совпадения, но не даст верное количество нестедов: эластик высчитывает совпадение только в рамках одного документа (включая обработку вложенного документа), поэтому самим запросом невозможно добиться ситуации, когда в исходном документе есть несколько архивных версий, а inner_hits возвращает только один единственный результат. Однако количество совпадений в этом случае соответствует количеству родительских документов, что может быть получено как напрямую, так и за счет агрегации:
aggs:
immerse:
nested:
path: items
aggs:
counter:
reverse_nested: {}
Результат:
{
"aggregations" : {
"immerse" : {
"doc_count" : 5,
"counter" : {
"doc_count" : 3 // << искомое значение
}
}
}
}
ADDRESS=localhost:9200
curl -XPUT -H 'Content-Type: application/json' "$ADDRESS/playground?pretty" -d @index.json
for document in $(find . -name "document.*"); do
local id=$(basename "$document" .json)
curl -XPUT -H 'Content-Type: application/json' "$ADDRESS/playground/_doc/$id?pretty" -d "@$document"
done
curl -XPOST -H 'Content-Type: application/json' "$ADDRESS/playground/_refresh?pretty"
curl -XPOST -H 'Content-Type: application/json' "$ADDRESS/playground/_search?pretty" -d @query.json