I'm filtering prices dynamically with the given currency rates and sorting them with the score which is generated by script. But there is one thing I could not figure out how to do is range filter.
For example I only want to get product_platforms only match score between 10 and 100.
Index request.
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"
}
}
}
}
}
}
Insert test documents:
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}]}
And here is the my search query:
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"
}
}
]
}
I'm using version 7.10 btw.
You could repeat that score calculator once again, this time in a boolean
script
query of its own.Now, since your currency conversion script repeats itself one too many times, you could store it and reference it by its ID every time you need it. You'll of course keep the rates parametrized but the whole thing will be a bit more readable and maintainable.
So, let's save the script first:
Notice that if
final_range
is provided in theparams
, the script returns aboolean
; if not, it'll simply return theconverted_price
.After that, the original query can be rewritten as: