В данном примере разберем, как пользоваться 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 встроен для рекомендаций и вообще всё кашерно.

Всем спасибо. Ваш Ö.

Комментарии (0)