Dec 5th: [EN][Elasticsearch] Using Painless to Prepare for The Holiday Season Deals

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!

4 Likes

Use case is simple to understand and even more easy as it's adapted to the period of the year :wink:

is there is a way ,i could obtain the query params in script?
for example , word "reindeer" .

yes, but duplication is required.

you must also include the query in the params passed to the script.

so

"params": {
  "current_time":  "2017-12-05T12:32:16Z",
  "term": "reindeer"
}

then you can access the term "reindeer" in the script with params.term.