Как добавить фильтр между вложенными script_score?
Я динамически фильтрую цены по заданным курсам валют и сортирую их по баллам, которые генерируются скриптом. Но есть одна вещь, которую я не мог понять, как это сделать, - это фильтр диапазона.
Например, я хочу получить только результат соответствия product_platforms от 10 до 100.
Запрос индекса.
PUT /test_products
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 0,
"analysis": {
"filter": {
"autocomplete_filter": {
"type": "edge_ngram",
"min_gram": "2",
"max_gram": "15"
}
},
"analyzer": {
"autocomplete": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"autocomplete_filter"
]
}
}
}
},
"mappings": {
"properties": {
"id": {
"type": "keyword",
"doc_values": true
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
},
"raw": {
"type": "keyword"
}
},
"analyzer": "autocomplete",
"search_analyzer": "standard"
},
"product_platforms": {
"type": "nested",
"properties": {
"id": {
"type": "long"
},
"platform_id": {
"type": "long"
},
"price": {
"type": "float"
},
"currency_id": {
"type": "long"
},
"currency_code": {
"enabled": false
},
"sku": {
"type": "keyword"
},
"quantity": {
"type": "long"
}
}
}
}
}
}
Вставьте тестовые документы:
POST /test_products/_bulk?pretty&refresh
{"index":{"_id": 1}}
{"id": 1, "name": "1. Product", "product_platforms": [{"id": 11, "platform_id": 3, "price": 100, "currency_id": 1, "currency_code": "TRY", "sku": "URN_1_1", "quantity": 1},{"id": 12, "platform_id": 3, "price": 75, "currency_id": 2, "currency_code": "USD", "sku": "URN_1_2", "quantity": 1},{"id": 13, "platform_id": 2, "price": 15, "currency_id": 2, "currency_code": "USD", "sku": "URN_1_3", "quantity": 1}]}
{"index":{"_id": 2}}
{"id": 2, "name": "2. Product", "product_platforms": [{"id": 21, "platform_id": 3, "price": 50, "currency_id": 1, "currency_code": "TRY", "sku": "URN_2_1", "quantity": 1},{"id": 22, "platform_id": 3, "price": 25, "currency_id": 2, "currency_code": "USD", "sku": "URN_2_2", "quantity": 1},{"id": 23, "platform_id": 3, "price": 75, "currency_id": 1, "currency_code": "TRY", "sku": "URN_2_3", "quantity": 1}, {"id": 24, "platform_id": 3, "price": 20, "currency_id": 2, "currency_code": "USD", "sku": "URN_2_4", "quantity": 1}]}
И вот мой поисковый запрос:
GET /test_products/_search
{
"query": {
"nested": {
"path": "product_platforms",
"score_mode": "max",
"query": {
"function_score": {
"query": {
"bool": {
"must": [
{
"term": {
"product_platforms.platform_id": {
"value": "3"
}
}
}
]
}
},
"boost_mode": "replace",
"script_score": {
"script": {
"source": """
doc['product_platforms.price'].value * (doc['product_platforms.currency_id'].value == 2 ? params.rate_usd : (doc['product_platforms.currency_id'].value == 3 ? params.rate_eur : params.rate_try)) """,
"params": {
"rate_try": 1,
"rate_usd": 7,
"rate_eur": 8
}
}
}
}
},
"inner_hits": {
"name": "product_platforms",
"_source": true,
"size": 5,
"sort": {
"_script": {
"type": "number",
"script": {
"lang": "painless",
"source": """ doc['product_platforms.price'].value * (doc['product_platforms.currency_id'].value == 2 ? params.rate_usd : (doc['product_platforms.currency_id'].value == 3 ? params.rate_eur : params.rate_try)) """,
"params": {
"rate_try": 1,
"rate_usd": 7,
"rate_eur": 8
}
},
"order": "desc"
}
}
}
}
},
"sort": [
{
"_score": {
"order": "desc"
}
}
]
}
Я использую версию 7.10 кстати.
1 ответ
Вы можете повторить этот калькулятор очков еще раз, на этот раз в виде логического <tcode id="256525"></tcode>запрос сам по себе.
Теперь, поскольку ваш скрипт конвертации валюты повторяется слишком много раз, вы можете сохранить его и ссылаться на него по его идентификатору каждый раз, когда он вам понадобится. Вы, конечно, сохраните параметризацию ставок, но все будет немного более читаемым и обслуживаемым.
Итак, сначала сохраним скрипт:
POST _scripts/product-platforms-converter
{
"script": {
"source": """
def price = doc['product_platforms.price'].value;
def currency_id = doc['product_platforms.currency_id'].value;
def converted_price = price * (currency_id == 2
? params.rate_usd : (currency_id == 3
? params.rate_eur : params.rate_try));
if (params.final_range != null) {
def is_in_range = converted_price >= params.final_range.gte
&& converted_price <= params.final_range.lte;
return is_in_range;
}
return converted_price;
""",
"lang": "painless"
}
}
Обратите внимание, что если
final_range
предоставляется в
params
, скрипт возвращает
boolean
; если нет, он просто вернет
converted_price
.
После этого исходный запрос можно переписать как:
GET /test_products/_search
{
"query": {
"nested": {
"path": "product_platforms",
"score_mode": "max",
"query": {
"function_score": {
"query": {
"bool": {
"must": [
{
"term": {
"product_platforms.platform_id": {
"value": "3"
}
}
},
{
"script": {
"script": {
"id": "product-platforms-converter",
"params": {
"rate_try": 1,
"rate_usd": 7,
"rate_eur": 8,
"final_range": { <--- the desired "range" query
"gte": 10,
"lte": 100
}
}
}
}
}
]
}
},
"boost_mode": "replace",
"script_score": {
"script": {
"id": "product-platforms-converter",
"params": {
"rate_try": 1,
"rate_usd": 7,
"rate_eur": 8
}
}
}
}
},
"inner_hits": {
"name": "product_platforms",
"_source": true,
"size": 5,
"sort": {
"_script": {
"type": "number",
"script": {
"id": "product-platforms-converter",
"params": {
"rate_try": 1,
"rate_usd": 7,
"rate_eur": 8
}
},
"order": "desc"
}
}
}
}
},
"sort": [
{
"_score": {
"order": "desc"
}
}
]
}