ElasticSearch — фильтрация результата и работа с документами

#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 . Часть агрегации будет состоять из:

Часть агрегации будет выглядеть следующим образом:

 {
  "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)