Сортировка по цене товара с учетом специальных цен (клиент, группа, страна)

у нас есть магазин с несколькими продуктами (~ 5000). Есть, конечно, обзорные сайты категорий, которые показывают все продукты, которые находятся в текущей категории. Требование состоит в том, чтобы все товары можно было отсортировать по цене (ASC и DESC).

Это уже работает (частично), потому что проблема в том, что в нашем Elasticsearch в настоящее время у нас есть только «исходная» цена, поэтому любые скидки на товары не учитываются, и поэтому сортировка работает неправильно.

Моя задача сейчас исправить это. Но я уже борюсь с тем, «как» сохранить «специальные цены» в Elasticsearch.

Проблема в том, что каждый продукт может быть уценен в целом, на уровне клиента, на уровне группы клиентов и на уровне страны.

Итак, я думаю, что такая структура будет началом:

# 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,
      ...
    },
    ...
  ]
}

Следующие мысли:

  • Специальные цены могут храниться как вложенный объект внутри индекса продукта (но я не уверен, как сделать сортировку по нему позже)
  • Может быть, я мог бы создать второй индекс с ценами, тогда у меня было бы два запроса, но, думаю, это было бы нормально? Потому что мне нужно построить целую матрицу с каждым клиентом, который у нас есть (тоже ~ 5000), с каждым продуктом по каждой возможной цене. Но если бы у меня был второй индекс, мне пришлось бы присоединиться, и, возможно, тогда сортировка неверна.
  • Если возможно, я хотел бы сохранять любые цены только в том случае, если у продукта есть специальная цена, а если нет, я не хочу взорвать индекс.

Я пробовал что-то с painless, чтобы вернуть специальную цену, если она существует для продукта и клиента, но это дает мне это:

...
"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."
...

Может быть, что-то вроде SQL ORDER BY CASE WHEN было бы вариантом?

Любые идеи о том, как мне моделировать и сохранять специальные цены? И как мне добиться сортировки? Является ли присоединение ко второму индексу хорошей идеей?

С наилучшими пожеланиями


person user3180943    schedule 12.09.2019    source источник


Ответы (1)


Ошибка, которую вы видите, связана с тем, что special_prices.customer не индексируется как keyword, а вместо этого text (который позволяет полнотекстовый поиск). Если вы не указали сопоставление явно, Elasticsearch, скорее всего, создал для вас keyword. Просто попробуйте заменить special_prices.customer на special_prices.customer.keyword в вашем скрипте.

Идея использования скрипта для сортировки это хорошо, учитывая, что у вас всего 5000 документов. Скрипты не отличаются хорошей производительностью, но в вашем случае это может и не иметь значения.

В целом это выглядит как сложный случай, потому что вам нужно какое-то соединение между продуктами и ценами, а Elasticsearch не очень хорош в соединениях. У него есть некоторые варианты присоединения: nested тип данных, тип данных join (он же родитель-потомок) и денормализация. Последнее вы уже рассмотрели — когда вы проставляете разные цены в исходном товарном документе.

К сожалению, я не могу рекомендовать один против другого, потому что нет единого рецепта. Я бы попробовал со сценариями, и если производительность недостаточно высока, рассмотрите возможность ремоделирования данных.

Надеюсь, это поможет!

person Nikolay Vasiliev    schedule 13.09.2019