#elasticsearch #elasticsearch-aggregation #elasticsearch-dsl
#elasticsearch #elasticsearch-агрегирование #elasticsearch-dsl
Вопрос:
У меня есть следующий запрос, который работает нормально (это может быть не фактический запрос):
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "location",
"query": {
"geo_distance": {
"distance": "16090km",
"distance_type": "arc",
"location.point": {
"lat": "51.794177",
"lon": "-0.063055"
}
}
}
}
},
{
"geo_distance": {
"distance": "16090km",
"distance_type": "arc",
"location.point": {
"lat": "51.794177",
"lon": "-0.063055"
}
}
}
]
}
}
}
Хотя я хочу сделать следующее (как часть запроса, но не влияющее на существующий запрос):
- Найти все документы, которые имеют
field_name
=1
- Для всех документов, которые имеют
field_name
=1
выполнить упорядочение по geo_distance - Удалите дубликаты с
field_name
=1
и тем же значением вfield_name_2
=2
и оставьте ближайший элемент в результатах документов, но удалите остальные
Обновление (дальнейшее объяснение):
Агрегирование не может быть использовано, поскольку мы хотим манипулировать документами в результате.
Сохраняя при этом порядок в документах; значение:
Если у меня есть 20 документов, отсортированных по полю; и у меня есть 5 из которых имеют field_name = 1, я хотел бы отсортировать 5 по расстоянию и исключить 4 из них; при этом сохраняя первую сортировку. (возможно, выполнение сортировки и удаления по геодезическим расстояниям перед фактическим запросом?)
Не совсем уверен, как это сделать, любая помощь приветствуется — в настоящее время я использую ElasticSearch DSL DRF — но я могу легко преобразовать запрос в ElasticSearch DSL.
Примеры документов (до манипулирования):
[{
"field_name": 1,
"field_name_2": 2,
"location": ....
},
{
"field_name": 1,
"field_name_2": 2,
"location": ....
},
{
"field_name": 55,
"field_name_5": 22,
"location": ....
}]
Вывод (желаемый):
[{
"field_name": 1,
"field_name_2": 2,
"location": .... <- closest
},
{
"field_name": 55,
"field_name_5": 22,
"location": ....
}]
Комментарии:
1. Итак, ваш запрос выше возвращает все документы в пределах заданного расстояния от заданной точки, но они не упорядочены по расстоянию… Однако я не уверен, как ваше другое условие
field_name = 1
должно сочетаться с этим начальным запросом. Можете ли вы более подробно объяснить, что вы имеете в видуas part of the query but not affecting the existing query
?2. Извините, если я не объяснил себя должным образом — итак, запрос, который у меня есть на данный момент, сортирует и фильтрует по расстоянию (как показано в запросе выше) — хотя я также хочу иметь отдельную часть (потому что не все запросы сортируются по расстоянию) для сортировки всех документов, которые имеют field_name = 1, ипросто выберите первый документ (ближайший), т.е. Найдите ближайший документ с field_name = 1 и удалите остальные, но также оставьте те, у которых нет field_name = 1, как есть.
3. Хорошо, потому что я не видел никакого предложения sort в вашем запросе, так что вы просто полагаетесь на
_score
сортировку по умолчанию, верно?4. Да, @Val, мы просто полагаемся на сортировку по умолчанию
Ответ №1:
Один из способов добиться желаемого — сохранить часть запроса в том виде, в каком она есть сейчас (чтобы вы по-прежнему получали нужные вам хиты), и добавить часть агрегирования, чтобы получить ближайший документ с включенным дополнительным условием filed_name
. Часть агрегации будет состоять из:
filter
агрегирование для рассмотрения только документов сfield_name = 1
geo_distance
агрегация с очень маленьким расстояниемtop_hits
агрегирование для возврата документа с наименьшим расстоянием
Часть агрегации будет выглядеть следующим образом:
{
"query": {
...same as you have now...
},
"aggs": {
"field_name": {
"filter": {
"term": {
"field_name": 1 <--- only select desired documents
}
},
"aggs": {
"geo_distance": {
"field": "location.point",
"unit": "km",
"distance_type": "arc",
"origin": {
"lat": "51.794177",
"lon": "-0.063055"
},
"ranges": [
{
"to": 1 <---- single bucket for docs < 1km (change as needed)
}
]
},
"aggs": {
"closest": {
"top_hits": {
"size": 1, <---- closest document
"sort": [
{
"_geo_distance": {
"location.point": {
"lat": "51.794177",
"lon": "-0.063055"
},
"order": "asc",
"unit": "km",
"mode": "min",
"distance_type": "arc",
"ignore_unmapped": true
}
}
]
}
}
}
}
}
}
}
Комментарии:
1. Выглядит великолепно, спасибо @Val, могу я спросить, что
ranges
именно делает? (одно ведро)2.
geo_distance
Агрегация позволяет создавать группы документов, расположенных между расстояниями от заданной точки (что-то вроде концентрических окружностей). Здесь нас интересует только наименьшее поле в центре круга. Вы можете настроитьto
значение по своему усмотрению, чтобы сделать его как можно меньше3. Ах, круто! спасибо за объяснение! 🙂 Если у меня есть фильтр geo_distance в
query
, нужно ли мне делать это снова здесь? — будет ли достаточно сортировки по расстоянию?4. будет ли результат отличаться от обычного результата запроса? есть ли другой способ сделать это, если да?
5. Другим решением также может быть выполнение 2 запросов (с использованием
_msearch
конечной точки)
Ответ №2:
Это можно сделать с помощью Field Collapsing
— что эквивалентно группировке. — Ниже приведен пример того, как этого можно достичь:
{"collapse": {"field": "vin",
"inner_hits": {
"name": "closest_dealer",
"size": 1,
"sort": [
{
"_geo_distance": {
"location.point": {
"lat": "latitude",
"lon": "longitude"
},
"order": "desc",
"unit": "km",
"distance_type": "arc",
"nested_path": "location"
}
}
]
}
}
}
Свертывание выполняется в поле vin
— и inner_hits
используется для сортировки сгруппированных элементов и получения ближайшего. (размер = 1)