0

we have a shop with a few products (~ 5000). There are, of course, category overview sites which show all products that are in the current category. A requirement is that all products can be sorted by price (ASC and DESC).

This already works (partially), because the problem is, in our Elasticsearch, we currently only have the "original" price, so any product discounts are not considered and therefore the sorting does not work correctly.

My task is it now to fix that. But I am already struggling with "how to" persist the "special prices" into Elasticsearch.

The problem is every product can be discounted in general, on a customer level, on a customer group level and on a country level.

So I imagine a structure like this would be a start:

# current
{
  "articleNumber": "12345",
  ...
  "price": 9.99,
  ...
}

# new
{
  "articleNumber": "12345",
  ...
  "price": 9.99,
  ...
  "special_prices": [
    {
      "customer": "123456",
      "client_price": 5.99,
      "client_group_price": null,
      "country_de": null
      "country_es": null,
      ...
    },
    ...
  ]
}

Following thoughts:

  • The specials prices could be stored as a nested object inside the product index (but I am not sure how to do the sorting on it later)
  • Maybe I could create a second index with prices, then I would have two queries, but I guess that would be ok? Because I have to build a whole matrix with every customer we have (also ~5000), with every product with every possible price. But if I would have a second index then I would have to join and maybe the sorting is incorrect then
  • If possible, I would like to only persist any prices if a product has a special price and if not, I don't want to blow up the index

I tried something with painless to return the special price if one exists for the product and customer, but this gives me this:

...
"script": "if (doc['special_prices.customer'] != null && doc['special_prices.customer'].value == '123456') { return 12.45; } else { return doc['price']; }",
          "lang": "painless",
          "caused_by": {
            "type": "illegal_argument_exception",
            "reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [special_prices.customer] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead."
...

Maybe something like SQL ORDER BY CASE WHEN would be an option?

Any ideas on how I should model and persist the special prices? And how can I achieve the sorting? Is joining a second index a good idea?

Best regards

user3180943
  • 181
  • 1
  • 2
  • 17

1 Answers1

0

The error you see is because special_prices.customer is not indexed as keyword, and instead is a text (which allows full-text search). If you didn't specify mapping explicitly, Elasticsearch most likely created a keyword for you. Just try to replace special_prices.customer with special_prices.customer.keyword in your script.

The idea of using a script for sorting is good, given that you only have 5000 documents. Scripts do not have good performance, but in your case this might not matter.

In general this looks like a tough case, because you need some kind of joining between products and prices, and Elasticsearch is not good at joins. It has got some joining options: nested datatype, join datatype (a.k.a. parent-child), and denormalization. The last one you have already considered - when you put different prices in the original product document.

Unfortunately I can't recommend one over another, because there is no single recipe. I would try with scripts, and if performance is not good enough consider remodelling the data.

Hope that helps!

Nikolay Vasiliev
  • 5,656
  • 22
  • 31