Я пытаюсь отфильтровать товары по их цене, и я совершенно озадачен тем, как поступить. Надеясь, что кто-то может пролить свет на это, и, возможно, указать мне правильное направление.
Концепция
Каждый продукт имеет несколько цен. Эти цены действительны в течение определенного диапазона дат. Фактическая цена товара на определенную дату является самой низкой ценой, действующей на эту дату.
Цель
Я хочу иметь возможность:
- получить самую низкую и самую высокую цену за определенную дату
- отфильтровать товары по максимальной / минимальной цене на определенную дату
предостережение : я упростил ограничения для цен в этом примере, но я не могу консолидировать даты, поэтому на каждый диапазон дат действует только 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
записи будут действовать следующие цены, правильные цены выделены жирным шрифтом:
Решение
Мин / Макс
Я выяснил, как получить минимальное и максимальное значения действующих цен. Это было довольно выполнимо с помощью агрегации:
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 для получения наименьшего), и это не вариант.
Надеюсь, что вы можете указать мне в правильном направлении