У меня на проекте должен быть функционал подписки по ключевым словам на уведомления о новых вакансиях.
Я сохраняю строку содержающую ключевые слова, и email пользователя в таблице.
Теперь мне нужно при создании вакансии, запускать event о создании вакансии, и отправлять в очередь job в котором будет описана логика в которой нужно будет проходится по всем записям из таблицы с ключевыми словами.
Вопрос только в том как описать эту логику.
У модели вакансии есть следующие поля в которых нужно будет проверять наличие ключевых слов:
Name, description, у нее так же есть метод который определяет отношение HasMany к ключевым навыкам, в которых так же надо будет искать вхождения.
Буду благодарен любым предложениям, как можно реализовать логику поиска
не совсем понятно что вы хотите конкретно. чисто теоретически я бы сделал так. создал набор ключевых слов/токенов вообще. при создании вакансии искал бы их в данных и создавал связку вакансия <-> ключевые слова. А дальше найти людей которые привязались к этим же ключевым словам - вопрос одного запроса вообщем то.
Дмитрий, Мне вот как раз нужно и искать в данных вакансии ключевые слова/токены, которые как раз у меня перечислены в таблице подписок в поле key_words. Вопрос в том как реализовать этот самый поиск(я просто ни разу подобного не делал).
KonstantinDyulin, в поле key_words - по мне так плохое решение и название так себе. у вас должна быть таблица keywords. и связка многие ко многим user_to_keywords и vacancy_to_keywords. тогда при прописанных релейшенах у вас будет что нибудь
iljaGolubev, ну этот произвольный текст и стоит превратить в ключевые слова и связь многие ко многим. и это будет наверное правильнее - нежели каждый раз в базе разбирать строку keywords у пользователя и сидеть в той же базе матчить совпадения. без вопросов можно - но скорее всего тогда лучше не мускул использовать, а какие нибудь поисковые движки.
так что https://laravel.com/docs/10.x/scout,
или https://laravel.com/docs/10.x/queries#full-text-wh... (mysql, postgres),
или своё что-то городить с FTS и DB::raw,
или движок какой-то использовать (Sphinx, Elastic, Solr).
Но без полнотестового не получится имхо. Или ущербно. А при большом количестве пользователей и иинтенсивных изменениях вакансий ещё и медленно.
У меня есть подобная форма.
Когда пользователь заполняет эту форму и отправляет ее, я сохраняю данные из формы в таблицу подписок.
key_words: ключевые слова из формы
email: email из формы
user_id: id пользователя.
В дальнейщем при создании вакансии, мне нужно получить все записи, и перебирать их, в случае если найдено ключевое слово отправлять уведомление на email, в противном случае перебирать дальше.
О заранее заготовленных ключевых словах/токенах мой работодатель ничего не говорил(я спрашивал).
Я понял что вы имеете ввиду, и что вашим способом было бы правильнее и проще. Но что поделать
KonstantinDyulin,
1. key_words - пишется как keywords. Как бы откройте html документ и посмотрите на тэг meta name="keywords"
2. прекрасно. у вас добавляется 3 таблицы одна называется keywords куда вы вписываете по одному те ключевые слова что пользователь вбил. и вторая таблица user_to_keywords для отношения многие ко многим - пользователи к ключевым словам, третья vacancy_to_keywords для отношения многие ко многим - вакансии к ключевым словам
3. при создании вакансии вы смотрите какие слова из таблицы keywords в ней содержится и добавляете связку.
после чего вы получаете приблизительно тот запрос что я написал.
держать в таблице user строку где через запятую лежат ключевые слова - не надо. нормализация, реляционные бд - вот это все немножко против
iljaGolubev, ага. у нас есть список ключевых слов которые навбивали пользователи, мы можем посмотреть встречаются ли они в произвольном тексте и превратить их в связку.
-- при создании вакансии нужно заполнить индекс
insert into "vacancy_fts" ("vacancy_id", "vector") select v.id,
setweight(to_tsvector('russian_ispell',v.name), 'A') ||
setweight(to_tsvector('russian_ispell',coalesce(v.description,'')), 'B') ||
setweight(to_tsvector('russian_ispell',coalesce(k.tags,'')), 'D') -- pseudo
from "vacancies" as "v" left join "keywords" as k -- on ...
where v.id=:id
Оптимальнее - отбирать пользователей с подходящими подписками.
для подписок пользователе создать подобную таблицу с полнотекстовым вектором subscription_fts (user_id, vector)
тогда что-то вроде
SELECT s.user_id FROM subscription_fts as u
LEFT JOIN vacancy_fts as v ON s.vector = v.vector
WHERE v.id = :id
покажет всех пользователей.
и в обратную сторону (вакансии для подписки) тоже сработает....
должно=) этот код не настоящий
а то что у меня в полях name, description тип поля указан как jsonB
insert into "vacancy_fts" ("vacancy_id", "vector") select v.id,
setweight(to_tsvector('russian_ispell',v.name), 'A') ||
ибъединяете всё что нужно при простоении индекса.
russian_ispell - это для description->ru.
Логику дальше сами придумывайте в соответсвии с выбранным решением.
Если FTS не сталкивались раньше - берите Scout как в единственном ответе.