Поиск документов по минимальному значению поля - PullRequest
1 голос
/ 21 февраля 2020

Я пытаюсь отфильтровать товары по их цене, и я совершенно озадачен тем, как поступить. Надеясь, что кто-то может пролить свет на это, и, возможно, указать мне правильное направление.

Концепция

Каждый продукт имеет несколько цен. Эти цены действительны в течение определенного диапазона дат. Фактическая цена товара на определенную дату является самой низкой ценой, действующей на эту дату.

Цель

Я хочу иметь возможность:

  • получить самую низкую и самую высокую цену за определенную дату
  • отфильтровать товары по максимальной / минимальной цене на определенную дату

предостережение : я упростил ограничения для цен в этом примере, но я не могу консолидировать даты, поэтому на каждый диапазон дат действует только 1

Пример

Отображение:

curl -XPUT 'http://localhost:9200/price-filter-test'

curl -XPUT 'http://localhost:9200/price-filter-test/_mapping/_doc' -H 'Content-Type: application/json' -d '{
    "properties": {
        "id": {"type": "integer"},
        "name": {"type": "text"},
        "prices": {
            "type": "nested",
            "properties": {
                "price": {"type": "integer"},
                "from": {"type": "date"},
                "untill": {"type": "date"}
            }
        }
    }
}'

Тестовые записи:

curl -XPUT 'http://localhost:9200/price-filter-test/_doc/1' -H 'Content-Type: application/json' -d '{
    "id": 1,
    "name": "Product A",
    "prices": [
        {
            "price": 10,
            "from": "2020-02-01",
            "untill": "2020-03-01"
        },
        {
            "price": 8,
            "from": "2020-02-20",
            "untill": "2020-02-21"
        },
        {
            "price": 12,
            "from": "2020-02-22",
            "untill": "2020-02-23"
        }
    ]
}'

curl -XPUT 'http://localhost:9200/price-filter-test/_doc/2' -H 'Content-Type: application/json' -d '{
    "id": 2,
    "name": "Product B",
    "prices": [
        {
            "price": 20,
            "from": "2020-02-01",
            "untill": "2020-03-01"
        },
        {
            "price": 18,
            "from": "2020-02-20",
            "untill": "2020-02-21"
        },
        {
            "price": 22,
            "from": "2020-02-22",
            "untill": "2020-02-23"
        }
    ]
}'

При 2020-02-20 записи будут действовать следующие цены, правильные цены выделены жирным шрифтом:

  • Продукт A:
    • 10
    • 8
  • Продукт B:
    • 20
    • 18

Решение

Мин / Макс

Я выяснил, как получить минимальное и максимальное значения действующих цен. Это было довольно выполнимо с помощью агрегации:

curl -XGET 'http://localhost:9200/price-filter-test/_search?pretty=true' -H 'Content-Type: application/json' -d '{
    "query": {"match_all": {}},
    "size": 0,
    "aggs": {
        "product_ids": {
            "terms": {"field": "id"},
            "aggs": {
                "nested_prices": {
                    "nested": {"path": "prices"},
                    "aggs": {
                        "applicable_prices": {
                            "filter": {
                                "bool": {
                                    "must": [
                                        {"range": {"prices.from": {"lte": "2020-02-20"}}},
                                        {"range": {"prices.untill": {"gte": "2020-02-20"}}}
                                    ]
                                }
                            },
                            "aggs": {
                                "min_price": {
                                    "min": {"field": "prices.price"}
                                }
                            }
                        }
                    }
                }
            }
        },
        "stats_min_prices": {
            "stats_bucket": {
                "buckets_path": "product_ids>nested_prices>applicable_prices>min_price"
            }
        }
    }
}'

Здесь я сначала агрегирую по разным идентификаторам, чтобы убедиться, что цены проверяются по каждому продукту, затем я фильтрую по применимым датам, а затем получаю минимальные цены для каждого. Используя агрегацию stats_bucket , я смогу получить минимальное и максимальное значения этих минимальных цен.

{
  // ...
  "aggregations" : {
    // ...
    "stats_min_prices" : {
      "count" : 2,
      "min" : 8.0,
      "max" : 18.0,
      "avg" : 13.0,
      "sum" : 26.0
    }
  }
}

Здесь мы видим правильный минимум (8 для продукта А) и max (18 для продукта B)

Фильтрация

Для фильтрации мне нужно иметь возможность исключать продукты на основе их самой низкой цены. Например, если я ищу продукты, которые стоят по крайней мере 19, я не должен найти их, так как самая низкая цена Продукта B 18

curl -X GET "localhost:9200/price-filter-test/_search?pretty" -H 'Content-Type: application/json' -d '{
    "query": {
        "nested": {
            "path": "prices",
            "query": {
                "bool": {
                    "must": [
                        {
                            "range" : {
                                "prices.price" : {"gte" : 19}
                            }
                        },
                        {"range": {"prices.from": {"lte": "2020-02-20"}}},
                        {"range": {"prices.untill": {"gte": "2020-02-20"}}}
                    ]
                }
            }
        }
    }
}'

Однако эта попытка по-прежнему выдает «Продукт B» как совпадение, так как одна из цен в этом диапазоне дат выше 19. Однако, поскольку это не самая низкая цена в этом диапазоне дат, она не является «правильной» ценой.

Я совершенно ошарашен относительно того, как это сделать. Я думал об использовании скриптовых полей, но думаю, что мне нужно объединить 2 (1 для рассчитанных применимых цен, 1 для получения наименьшего), и это не вариант.

Надеюсь, что вы можете указать мне в правильном направлении

1 Ответ

1 голос
/ 24 февраля 2020

Ну, если я прав, вы ищете inner_hits: https://www.elastic.co/guide/en/elasticsearch/reference/6.8/search-request-inner-hits.html

Я не был уверен в агрегации (вы не можете вставить inner_hits в агрегацию), поэтому я не опубликовал при запуске.

Надеюсь, это именно то, что вам нужно.

{
  "query": {
    "nested": {
      "path": "prices",
      "query": {
        "range": {
          "prices.price": {
            "gte": 10,
            "lte": 20
          }
        }
      },
      "inner_hits": {}
    }
  }
}

=> сохранит только вложенные значения c с диапазоном в части inner_hits:

"inner_hits":{
   "prices":{
      "hits":{
         "total":2,
         "max_score":1,
         "hits":[
            {
               "_nested":{
                  "field":"prices",
                  "offset":1
               },
               "_score":1,
               "_source":{
                  "price":18,
                  "from":"2020-02-20",
                  "untill":"2020-02-21"
               }
            },
            {
               "_nested":{
                  "field":"prices",
                  "offset":0
               },
               "_score":1,
               "_source":{
                  "price":20,
                  "from":"2020-02-01",
                  "untill":"2020-03-01"
               }
            }
         ]
      }
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...