Painless was introduced in Elasticsearch 5.0. Since then, It has been possible to script the values of fields during indexing and the scores of documents during searching using Painless. Starting in Elasticsearch 6.0, support for the other scripting language plugins, like Python or Javascript, ended. So, what does it look like to use Painless today?
Let's build a toy search engine for this holiday season. We may have an existing inventory of toys that we have indexed like so:
PUT toys/doc/_bulk
{"index": {"_id": 1}}
{"name": "plastic reindeer", "price": 11.99}
{"index": {"_id": 2}}
{"name": "red plush reindeer", "price": 7.99}
{"index": {"_id": 3}}
{"name": "model train", "price": 112.99}
{"index": {"_id": 4}}
{"name": "backyard swing-set", "price": 329.99}
Now, we want to update all the toys to include their holiday discounts. We can do this by introducing a new field called holiday_price
that includes a date range for when a discount is in effect. For example, we may have this field be formatted like this:
{"start_date": "2017-12-01T00:00:00Z", "end_date": "2017-12-31T00:00:00Z", "discount": 0.20}
Painless During Updates
We can use the Update-By-Query API to create 20% discounts for toys under $100 and 15% discounts for all the other toys that are $100 or more.
POST toys/doc/_update_by_query
{
"script": {
"lang": "painless",
"source": """
ctx._source.holiday_price = ["start_date": "2017-12-01T00:00:00Z", "end_date": "2017-12-31T00:00:00Z", "discount": 0.20];
if(ctx._source.price >= 100) {
ctx._source.holiday_price.discount = 0.15;
}
"""
}
}
Painless During Querying
Now that we have introduced special holiday pricing, we have to take this into account. We want to sort our results by their price. If a user searches for toys within the holiday season holiday price date range, we want to use the discounted price. This is not as straightforward as using the holiday_price.discount
field, we need to have a conditional to know whether the current search time is during December. To retrieve the current query time, the application must pass it in as a script parameter since there is no way to accurately retrieve a consistent current time across all the nodes in Elasticsearch.
GET toys/doc/_search
{
"query": {
"term": { "name": "reindeer" }
},
"sort": {
"_script": {
"type": "number",
"script": {
"lang": "painless",
"source": """
long currentTime = ZonedDateTime.parse(params.current_time).toInstant().toEpochMilli();
long startDiscountTime = doc['holiday_price.start_date'].value.getMillis();
long endDiscountTime = doc['holiday_price.end_date'].value.getMillis();
if (currentTime >= startDiscountTime && currentTime <= endDiscountTime) {
return (1.0 - doc['holiday_price.discount'].value) * doc['price'].value;
}
return doc['price'].value;
""",
"params" : {
"current_time": "2017-12-05T12:32:16Z"
}
}
}
}
}
There we have it, we have prepared our toy search engine to support the temporary holiday specials!
Other Painless Topics
Although we didn't go into details about these things here, there are a few major topics of Painless that are worth mentioning.
If you see a use for Painless in your application, please look at our Painless Getting Started documentation for more information.
Happy shopping!