如何在嵌套 script_score 上添加过滤器?
How to add between filter on nested 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 版。
你可以再次重复那个分数计算器,这次是它自己的布尔值 script
query。
现在,由于您的货币转换脚本自身重复了太多次,您可以 store it 并在每次需要时通过 ID 引用它。你当然会保持利率参数化,但整个事情会更具可读性和可维护性。
所以,让我们先保存脚本:
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"
}
}
请注意,如果 params
中提供了 final_range
,则脚本 return 是 boolean
;如果不是,它只是 return 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"
}
}
]
}
我使用给定的货币汇率动态过滤价格,并使用脚本生成的分数对它们进行排序。但是有一件事我不知道该怎么做,那就是范围过滤。
例如我只想得到 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 版。
你可以再次重复那个分数计算器,这次是它自己的布尔值 script
query。
现在,由于您的货币转换脚本自身重复了太多次,您可以 store it 并在每次需要时通过 ID 引用它。你当然会保持利率参数化,但整个事情会更具可读性和可维护性。
所以,让我们先保存脚本:
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"
}
}
请注意,如果 params
中提供了 final_range
,则脚本 return 是 boolean
;如果不是,它只是 return 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"
}
}
]
}