Современный поиск для электронной коммерции должен быть быстрым, релевантным и иметь возможность продвижения определенных результатов. Это статья расскажет как типы полей elasticsearch rank_feature
и rank_features
могут помочь в этой цели на примере электронного магазина обуви.
Проблема № 1: улучшение релевантности
Проблема № 1: Нам нужно найти хороший способ ранжирования результатов поиска удовлетворяющих пользовательскому запросу. Сейчас очень распространено применять различные метрики популярности для ранжирования результатов поиска. Эти метрики популярности определяют релевантность документа вне зависимости от пользовательского запроса (алгоритм PageRank Google – известный пример такой метрики). Для нашего магазина обуви такими метриками популярности для определенного товара могут служить количество раз этот товар был просмотрен, куплен нашими пользователями или рейтинг товара по оценкам пользователей.
Решение: Один из способов решения данной проблемы – это моделирование метрик популярности обуви с помощью полей rank_feature
. Для каждой отдельной метрики мы создадим отдельное поле: "views_count" (количество просмотров), "ordered_count" (количество заказов) и "rating" (рейтинг обуви):
PUT shoes
{
"mappings": {
"properties": {
"product_name" : {
"type" : "text"
},
"views_count" : {
"type" : "rank_feature"
},
"ordered_count" : {
"type" : "rank_feature"
},
"rating" : {
"type" : "rank_feature"
}
}
}
}
Проиндексируем примерные документы:
POST shoes/_bulk
{ "index" : { "_id" : "1"} }
{"product_name" : "Nike Air Zoom Structure", "views_count" : 900, "ordered_count": 14, "rating" : 4.9 }
{ "index" : { "_id" : "2"} }
{"product_name" : "Nike Air Max", "views_count" : 1780, "ordered_count": 17, "rating" : 4.7}
{ "index" : { "_id" : "3"} }
{"product_name" : "Adidas ULTRABOOST 20", "views_count" : 2560, "ordered_count": 23, "rating" : 4.9}
И теперь мы готовы улучшить релевантность текстовых запросов с помощью метрик популярности путем совмещения match
запроса со специальным rank_feature
запросом. Например, нижеследующий запрос сперва находит и ранжируют все товары соответствующие пользовательскому запросу, а потом к каждому из этих товаров добавляет дополнительные очки, подсчитанные на основе популярности этого товара. Таким образом, чем более популярен товар с точки зрения количества просмотров, заказов и рейтинга, тем больше увеличится его релевантность и он продвинется выше в списке результатов поиска. Нижеследующий запрос можно настраивать путем использования индивидуальных параметров boost для каждого rank_feature
запроса в зависимости от того насколько важен каждый из этих метрик популярности.
GET shoes/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"product_name": <user_query>
}
}
],
"should": [
{
"rank_feature" : {
"field" : "views_count"
}
},
{
"rank_feature" : {
"field" : "ordered_count"
}
},
{
"rank_feature" : {
"field" : "rating"
}
}
]
}
}
}
Проблема № 2: контроль результатов поиска
Проблема № 2: Нам нужно продвигать определенные товары в ответ на определенные запросы пользователя. Такие результаты называются платными или рекламными.
Решение: Один из способов решения этой проблемы – это rank_features
тип поля в elasticsearch. Для каждого товара мы определяем набор категорий которые характеризуют этот товар. Для каждой из категорий товара мы назначаем числовое значение, указывающее насколько мы хотим поднять уровень этого товара когда пользователь ищет по этой категории. Категории могут быть очень разными и разреженными, то есть может быть очень много категорий и каждая категория может применяться только к небольшому количеству товаров. В elasticsearch мы создаем поле "categories" типа rank_features
:
PUT shoes
{
"mappings": {
"properties": {
"product_name": {
"type": "text"
},
"categories": {
"type": "rank_features"
}
}
}
}
Нижеследующий запрос на индексацию ставит целью продвинуть обувь "Nike" в категории "sneakers" путем назначению более высоких значений для обуви Nike и более низких значений для другой обуви в категории "categories.sneakers".
POST shoes/_bulk
{ "index" : { "_id" : "1"} }
{"product_name" : "Nike Air Zoom Structure", "categories" : {"sneakers" : 10, "running" : 10, "athleisure" : 2} }
{ "index" : { "_id" : "2"} }
{"product_name" : "Nike Air Max", "categories" : {"sneakers" : 10, "athleisure" : 10} }
{ "index" : { "_id" : "3"} }
{"product_name" : "Adidas ULTRABOOST 20", "categories" : {"sneakers" : 8, "running" : 10, "athleisure" : 3} }
Таким образом, если мы зададим rank_feature
запрос с "categories.sneakers", обувь "Nike" будет выведена в самом начале списка результатов поиска:
GET shoes/_search
{
"query": {
"rank_feature" : {
"field": "categories.sneakers"
}
}
}
Для категории "athleisure", мы увидим совсем другое ранжирование обуви, основанное на числовых значениях назначенных обуви для этой категории.
GET shoes/_search
{
"query": {
"rank_feature" : {
"field": "categories.athleisure"
}
}
}
Технические подробности
Elasticsearch сохраняет значения полей rank_feature(s) как частоты терминов (term frequency), и rank_features соответствующие одному и тому же полю будут сохранены в одном поле документа. Например, если поле "categories" имело следующую схему:
"categories" : {
"type" : "rank_features"
}
Этот запрос на индексацию:
{ "index" : { "_id" : "1"} }
{"product_name" : "Nike Air Zoom Structure", "categories" : {"sneakers" : 10, "running" : 10, "athleisure" : 2} }
создаст документ с полем "categories" у которого будут 3 термина:
- "sneakers" с частотой термина в 10
- "running" с частотой термина в 10
- "athleisure" с частотой термина в 2
Elasticsearch храня все rank features в одном поле, позволяет нам создать их большее количество и избежать бесконтрольного роста схемы (mapping explosion), который бы произошел если бы мы отвели отдельное поле для каждой отдельной категории.
И как дополнительным преимуществом запроса rank_feature
может служить тот факт, что этот запрос очень быстрый. Запрос rank_feature
может эффективно перескакивать через документы с низкими значениями частотой терминов, то есть документов у которых нет шанса быть в начале списка результатов.
Заключение
Это статья рассказала как поля rank_feature
и rank_features
могут быть полезными для настройки релевантности на примере электронного коммерческого магазина. В заключение, мы хотели отметить что настройки релевантности и ранжирования результатов поиска – это сложная и развивающаяся тема. Целью этой статьи не является предписания метода ранжирования, а скорее демонстрация примера как elasticsearch rank_feature(s) могут помочь в ранжировании результатов.
Ссылки
-
Rank feature(s) документация и блог: rank_feature field, rank_features field, rank_feature query, блог по rank features.
-
Flattened field, еще один способ избежать бесконрольного роста схемы.
-
Dense vector, еще один тип полей полезный для ранжирования; блог по dense vectors
-
Distance Feature query, еще один запрос полезный для ранжирования; блог по distance_feature query
-
Pinned query, еще один тип полей полезный для ранжирования