Вопрос умной категоризации чего-либо встаёт остро при разработке очень многих сайтов. Конечно, всегда можно отдать это на заполнение человеку и результат поначалу будет куда лучше машинного, но, что если категоризировать нужно в реальном времени сотни и тысячи «товаров».
Придется отдать это на откуп машине. Тут вариантов не так много, а написание собственного ИИ для 99.9% задач пустая трата времени.
Заинтересовавшимся как это решить с помощью ElasticSearch прошу под кат.
Если вы еще не знакомы с ElastiSearch, то советую прекрасную статью «Быстрый полнотекстовый поиск ElasticSearch» от пользователя brujeo
Общая идея
В SmartProgress мы реализовали категоризацию в виде групп, которые объединяют цели пользователей в общие группы по интересам. Но как соотнести эти группы (которых уже более 100) с пользовательской целью так, чтобы ему на выбор было предложено максимум 3 группы и при этом они были максимально релевантные цели?
Простейшим вариантом было бы использовать, например, теги для привязки к той или иной группе, но в реальности это работает не так хорошо, как хотелось бы, плюс заставлять тех же пользователей заполнять теги может быть оправдано только для IT сферы.
Предположим, что у нас есть категория «Программирование на Ruby on Rails», тогда поисковый запрос по правилам simple query string будет выглядеть примерно так:
Ruby | RoR | "Ruby on Rails" | "программирование Ruby"~4 | "вставать на рельсы" -php -java -net
Немного поясню запрос: | — или, "..." — вхождения всего словосочетания, ~N — возможно разбавка словосочетания N словами
Если нужно найти все «товары»(в нашем случае цели), которые подходят под такой запрос, то достаточно просто выполнить поиск. А если надо найти все категории под определённый товар? Тут приходит на помощь Percolate API
Percolate API
Признаюсь честно, моё знакомство с ElasticS началось именно с этой «фишки», до этого я работал только со сфинксом, но он не умеет делать обратный поиск.
Поэтому, прочитав документацию, я толком не понял что это такое и как с этим работать, а информации в гугле, особенно по версии >1.X, было крайне мало. Но упорство победило (на >5 странице гугла есть жизнь).
Я попробую объяснить на пальцах как это работает:
- Мы создаём индекс или берём существующий
- В него добавляется документ(ы) с особым типом .percolator, любым уникальным id и c body в виде нашего запроса (пример ниже)
- Далее мы делаем запрос к _percolate и смотрим к каким категориям подходит «товар»
Рабочий пример
Давайте попробуем это в действии:
— Создаём индекс «test» (без mapping, он нам не понадобится)
curl -XPUT 'http://localhost:9200/test'
— Создаём .percolator
curl -XPUT 'http://localhost:9200/test/.percolator/simple-search' -d '
{
"query" : {
"simple_query_string" : {
"query" : "Ruby | RoR | \"Ruby on Rails\" | \"программирование Ruby\"~4 | \"вставать на рельсы\" -php -java -net",
"analyzer" : "simple",
"fields" : ["name^5", "description"],
"default_operator" : "and"
}
},
"language" : "ru",
}'
Подробнее:
test — Индекс
.percolator — Тип
simple-search — ID (может быть как int так и string)
"query"
— поискsimple_query_string — Функция для поиска. Полный список
"fields" : ["name^5", "description"]
— тут мы указали по каким полям идёт поиск, и указали коэффициент 5 для поля «name», т.к. обычно там самая важная информация. Подробнее."active" : 1
— Дополнительные параметры, не обязательные, может быть несколько любого типа, применяются в фильтрации результата.По сути .percolator это такой же объект, как и любой другой в индексе, поэтому к нему также можно применять mapping.
— Ищем:
curl -XPOST 'http://localhost:9200/test/category/_percolate?pretty' -d '
{
"doc" : {
"name" : "Изучить Ruby on Rails максимально быстро",
"description" : "Я хочу программировать на Ruby"
},
"filter" : {
"term" : {
"language" : "ru"
}
}
}'
Ответ:
{
"took" : 5,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"total" : 1,
"matches" : [ {
"_index" : "test",
"_id" : "simply-search"
} ]
}
Совпадений может быть несколько
Этот способ прекрасно подходит, если вы по какой-то причине не хотите переносить всю информацию в ElasticS. Или хотите потестить ваш percolator
Поиск по существующим данным (ElasticS>=v1.0)
Давайте добавим 1 запись в индекс test
curl -XPUT 'http://localhost:9200/test/category/1' -d '
{
"name" : "Изучить Ruby on Rails максимально быстро",
"description" : "Я хочу программировать на Ruby"
}'
И посмотри к каким категориям эта запись подходит:
curl -XGET 'http://localhost:9200/test/category/1/_percolate?pretty'
Ответ:
{
"took" : 4,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"total" : 1,
"matches" : [ {
"_index" : "test",
"_id" : "simply-search"
} ]
}
Ну, что же, мы смогли сделать «поиск наоборот». Где это можно использовать? Применений море, от хитрых выборок до напоминалок по событию, всё ограничивается только вашей фантазией и оперативной памятью.
Ложка дёгтя в бочке мёда
К сожалению не всё так замечательно, как выглядит со стороны. Да, это работает, но есть минусы:
- Все .percolator хранятся в оперативной памяти
- Каждый документ индексируется в оперативной памяти
- Время выполнения линейно количеству .percolator индексов
Да, они поддерживают репликацию, как любой объект ElasticSearch, но тем не менее применять этот механизм нужно крайне осторожно.
Пару простых советов как избежать out of memory:
- Если вашу выборку можно сделать более простым методом, например, использую macth/ bool query то используйте это, query язык довольно медленный относительно обычного сравнения значений
- Используйте фильтры, сужайте поиск настолько, насколько вам это позволяет логика приложения, это сэкономит вам немного памяти
- Не создавайте слишком много .percolator индексов, если у Вас предполагается тысячи таких индексов, значит вам стоит пересмотреть вашу логику или запастись оперативной памятью
Полезная информация
- Whats new in percolator — прекрасная презентация от разработчиков ES, которая очень доходчиво объясняет суть технологии
- Percolator API — страница официальной документации
- DHC — REST HTTP API Client — замечательный плагин для Google chrome позволяющий быстро и удобно общаться с ES
Еще моя статья по ES — ElasticSearch — агрегация данных
P.S. Я не являюсь гуру ES, поэтому рад любым замечаниям и дополнениям.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Нужны ли еще статьи по ElasticSearch
72.1% Нужны по более простым вещам (CRUD, настройка, ...)168
74.68% Нужны по более сложным вещам174
0.86% Не нужны2
Проголосовали 233 пользователя. Воздержался 41 пользователь.