Как организовать архитектуру для подписки по ключевым словам (как пример, Avito)?
Ситуация такая:
Имеется:
1) БД Postgres, в которой хранятся подписки пользователей ~50000 запросов. Запросы вида: Автомобили, Москва, пробег до 100 тыс., в описании есть "зимняя резина", "в родной краске".
2) Кластер ElasticSearch, в котором часто появляются новые документы - объявления. До 500 в минуту.
Задача: организовать рассылку по новым объявлениям как в avito, auto.ru.
Что происходит сейчас:
За минуту загружается ~500 новых объявлений, кладем их в ElasticSearch, и раз в минуту запускаем процесс проверки, какому пользователю какое объявление подходит. Т.е. формирую 50000 запросов к ElasticSearch с заданными id-шниками новых записей (потому что в запросах юзеров есть полнотекст, которым и занимается ES), и потихоньку, по 500 за раз выполняю их на кластере ElasticSearch. Некторые объявления удается отфильтровать на backend, например, если регион не подходит, но запросов все-равно очень много. Соответсвенно, эластик вешается от такого количества полнотекстовых запросов, и поиск по сайту начинает страшно тормозить.
Вид запросов условно такой:
1) Найти среди 500 новых объявлений те, в которых встречается словосочетание "цвет белый", в гороне Казань
2) Найти среди 500 новых объявлений те, в которых встречается "зимняя резина" или "полный электропакет", в городе Москва
........
и таких еще 49000
Есть какие-то идеи как лучше организовать такое решение с рассылкой? Или кто поделится опытом, как устроена система принятия решения, подходит ли документ пользователю или нет, у гигантов, как avito, auto.ru?
1. Выделить из запроса сущности (имена существительные): можно использовать это
2. Проверить по заранее подготовленному словарю синонимов и унифицировать всё, что имеет неточности и является синонимом.
3. Делаем привязку тегов текущего объявления к ОБЩЕМУ списку тегов объявлений всей системы.
4. Под объявлением отображаем только 5-6 тэгов, с максимальным количеством объявлений внутри каждого тега (кол-ва привязанных объявлений к этому тегу) по всей системе.
5. В очередь пользователя для отправки - помещаем ID объявлений по его подписке: тэги и т.д.
6. Как только пул новых объявлений превышает пороговое значение - делаем рассылку. Например, каждые 30 новых из общей очереди пользователя:
if($newItemsForUser>=30) {
/*
команда запроса инициализации рассылки
например, команда через API микросервису
*/
}
Думаю логично для подписок использовать модель pubsub в любой реализации.
И соответственно делать рассылку сразу после создания каждого объявления.
Точнее добавлять объявления в некий пул для рассылки, и как только наберется N количество, отправлять рассылку.
Создается объявление
Попадает в очередь для парсинга
Парсер вычленяет из объявления ключевые слова по базе подписок.
Если ключевые слова находятся, то объявление отправляется в соответствующие каналы(пулы) для рассылки
После того как пул набирает N количество объявлений - делается рассылка.
В итоге должно получиться что-то вроде того что нужно.
"Продам гараж в Москве, белый"
Парсер нашел: москва, белый гараж
Объявление попало в каналы: москва, белый гараж.
И после того как наберутся остальные объявления либо когда пройдёт N времени, очередь рассылается подписчикам этих двух каналов и очищается.
"pubsub" - это фигня в смысле сложности реализации.
Самое интересное в пунктах 3 и 4, а именно парсинг ключевых слов и сопоставление с заданными фразами подписчика