В данном примере разберем, как пользоваться API поиска в известном поисковом сервисе Algolia.

Идея
Идея была предельно проста, необходимо было сделать бот, который при введении имени препарата находит информацию о том, через сколько можно употреблять алкоголь. Сразу скажу, что бот был запилен и успешно работает, помогая (я надеюсь) сохранить здоровье граждан. Если что, то Алкобот доктора Знаева тут
Задача
На входе имеем имя препарата, по нему хотим найти релевантную запись из базы данных и отправить пользователю. В принципе всё довольно просто, никаких ракетных технологий. Но проблема в том, что пользователи очень часто опечатываются или вводят название неправильно. Для решения этой задачи лучше всего подходил поисковый движок с fuzzy поиском. Решено было воспользоваться Algolia, так как в неё удобно добавлять данные и она предоставляет удобный довольно простой API для использования.
Вот кусочек данных в формате JSON
{ "drugs": [ // по этому полю будем искать "Допегит", "Метилдопа" ], "threashold": "12 часов", // это показываем пользователю "consequences": "Усиливает снижение давления", // и это показываем "objectID": "ff26ba646e8ba_dashboard_generated_id" // сгенерированный Algolia айди }
Данные
Данные собирали долго и мучительно. Профессиональный нарколог несколько недель собирал информацию с РЛС (Регистр Лекарственных Средств) и отправлял ее мне.
Индекс
Теперь необходимо создать проект и индекс. Тут всё довольно просто, нажимаем Create New, выбираем бесплатную версию (она допускает до 10 тыс запросов в месяц, больше чем достаточно). Далее выбираем дата центр. Algolia переместила свои датацентры из России, но мои лежат в Европе, пока проблем не было. Далее нажимаем все нужные галочки и нажимаем Создать

Вам сразу предложат создать первый индекс, что конечно же надо сделать. Не думаю, что есть смысл загружать каждый шаг, там в общем-то всё очень просто. Далее надо выбрать как будем вносить данные, я выбрал ручной способ.

Далее необходимо взять наши JSON объекты и вставить их во всплывающее окно.

При создании объектов таким образом, каждая запись получит id в формате ff26ba646e8ba_dashboard_generated_id. Выглядит так себе, но жить можно.
Теперь индекс надо настроить, что бы поиск происходил не по всем данным, а только по массиву drugs
Для этого в интерфейсе индекса идём во вкладку Configuration и добавляем поле для индексации (кнопка Add searchable Attribute)

Код
Что бы получить доступ к индексу нам понадобятся id нашего приложения и ключи. Я использую ключ администратора, так как иногда пишу в базу. Идём в Settings -> Api Keys, там лежат наши ключи.

Теперь перейдём к коду. Сервер крутиться на node.js, примеры будут на нём же.
Итак, импортируем библиотеку и инициализируем индекс
const algoliasearch = require('algoliasearch'); const client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_ADMIN_API_KEY); const alco = client.initIndex('alco'); // alco называется мой индекс в Algolia
Поиск
По названию препарата ищем запись
exports.findDrug = async (drug, user = {}) => { return alco.search(drug, { userToken: `${user.id}`, typoTolerance: 'strict', }); }
Эта функция вернёт объект в котором есть поле hits, это массив с результатами поиска. У Algolia отличная документация с примерами кода. Вот ссылка на нашу search функцию
Тут у нас есть три варианта
Ничего не найдено. Показываем сообщение, что ничего не найдено.
Найден один вариант. Берем единственный вариант, вынимаем нужные значения и формируем ответ пользователю.
Найдено несколько вариантов. Чуть сложней. Я не парюсь, беру первые два совпадения и выдергиваю из них названия и айдишник записи и формирую из них кнопку
if (hits.length > 1) { const ret = []; const firstMatches = hits.splice(0, Math.min(2, hits.length)); const matches = firstMatches.map(({ objectID, _highlightResult }) => ({ objectID, match: utils.fetchDrugNameFromAliases(_highlightResult.drugs) })); const buttons = matches.map(({ objectID, match }) => ([{ text: match, callback_data: `/match&${objectID}` }])); options.reply_markup = JSON.stringify({ inline_keyboard: buttons }); return [FEW_RESULTS_FOUND, options]; }
_highlightResult содержит массив drugs, потому что в индексе это поле так же массив, у каждой записи будет степень совпадения, по ней и фильтруем
none(0)partial(some)full(all)
Результат выглядит у пользователя вот так :

Поиск по ID
В каждой кнопке зашит id записи и при нажатии надо вытянуть именно её.
exports.find = async (drugId) => { return alco.getObject(drugId); }
Итого
Вот таким нехитрым способом мы интегрировали высококлассный поиск в наше скромное приложение. Не пришлось поднимать никаких баз данных, кластеров или прочего. Вообще Algolia очень мощная штука, у них там и AI встроен для рекомендаций и вообще всё кашерно.
Всем спасибо. Ваш Ö.
