Думаю, всім зрозуміло, що коли асортимент магазину сягає десятків тисяч і більше, важливо мати зручний пошук на сайті. Стандартний LIKE-подібний пошук на великих обсягах не справляється ні зі швидкістю, ні зі зручністю відповіді. Я розповім, як зробити пошуковий механізм на основі систем, створених спеціально для пошуку.
Почну відразу з можливостей пошуку.
1. Пошук з урахуванням помилок
чихол -> чохол
Пошук артикулів у різних варіаціях:
SKU.1234 -> SKU-1234 -> SKU1234
2. Неправильна розкладка клавіатури:
xj[jk -> чохол
3. Пошук з урахуванням морфології мови: рід, число, відмінок:
сумка ноутбук -> сумки для ноутбуків
4. Семантичний пошук.
мікрохвильовка Samsung -> мікрохвильова піч Samsung
браслет мед макс -> браслет Mad Max
До цього були показані приклади лексичного пошуку, тобто пошуку за конкретними символами. Семантичний (векторний) пошук шукає за смисловою схожістю. Даний підхід пов'язаний з великими мовними моделями. Тренуючись на купі інформації, LLM навчилися класифікувати поняття за тисячами факторів. Коли ми індексуємо наші товари, ми не зберігаємо їх у вигляді ключових слів, ми створюємо векторне (математичне) представлення слів, фраз. Сам вектор являє собою масив float значень. Якими саме будуть значення і їх розмірність безпосередньо залежить від моделі. Пошуковий запит також перетворюється на вектор і за допомогою математичних виразів ми перевіряємо близькість наших векторів, тобто наскільки семантично близькі поняття.
Для використання семантичного пошуку вам не потрібно завантажувати мовні моделі на десятки гігабайт і мати топову відеокарту, все працює на звичайному VPS.
Які функції реалізовані:
Спливаюче вікно з автодоповненням
Пошукові підказки:
чохол:
чохол для телефону
чохол для ноутбука
категорії із зазначенням кількості знайдених товарів
Тут відображаються не категорії, які збіглися із запитом пошуку, а категорії товарів, які відповідають умовам пошуку.
товари
Ви мали на увазі
Коли користувач зробив помилку в пошуковому запиті, він бачить виправлене слово: можливо, ви мали на увазі. Ця функція реалізована технічно, але не виведена, щоб не перевантажувати пошуковий інтерфейс.
З чого складається дане рішення:
Пошуковий движок на вашому хостингу: Opensearch (форк ElasticSearch), Typesense або Meilisearch.
API додаток на піддомені на основі Laravel.
Embedding API
HTTP-клієнт для Opencart з модулем пошуку.
Наведені вище можливості пошуку вказані для Opensearch. Далі також буде описуватися саме цей движок. Готові приклади для інших движків ви зможете знайти в репозиторії проекту.
Порівняння можливостей движків:
OpenSearch
Простота встановлення: ❌
Пошук з помилками: ✅
Морфологія: ✅
Виправлення розкладки клавіатури: ✅
Семантичний пошук: ✅
Typesense
Простота встановлення: ✅
Пошук з помилками: ✅
Морфологія: ❌
Виправлення розкладки клавіатури: ❌
Семантичний пошук: ✅
Meilisearch
Простота встановлення: ✅
Пошук з помилками: ✅
Морфологія: ❌
Виправлення розкладки клавіатури: ❌
Семантичний пошук: ❌
Чому окреме API? Я робив універсальне рішення для різних движків, а зробити таке в рамках опенкарта - нагородити купу "лапшекода"" без можливості виконання cli-скриптів, черг та інших принадностей цивілізації. У будь-якому випадку, установка API буде не складнішою за установку Opencart, знати Laravel зовсім не обов'язково.
Embedding API використовується тільки в Opensearch і тільки для семантичного пошуку. Це однофайлове API на основі FastAPI і Python, яке приймає рядок і повертає його векторне представлення. Запускається парою команд, ніяких знань Python від вас не потрібно.
Встановлення
Для тих, хто не проти спробувати Docker, в репозиторії знаходиться готова конфігурація для запуску всієї інфраструктури однією командою.
1. Пошуковий движок.
Вибираєте пошуковий движок, який вам сподобався, і на офіційному сайті або в ChatGPT отримуєте відповідь про встановлення під вашу платформу. Від себе зазначу, що найпростіші в установці Typesense і Meilisearch.
Для Opensearch необхідно встановити плагін для векторного пошуку:
/usr/share/opensearch/bin/opensearch-plugin install opensearch-knn
А також морфологію для української та російської мов:
/usr/share/opensearch/config/hunspell — тут створюємо папки: ru_RU, uk_UA, в яких будуть лежати файли *.dic, *.aff. Самі файли завантажуєте звідси:
https://raw.githubusercontent.com/LibreOffice/dictionaries/master/ru_RU/ru_RU.dic
https://raw.githubusercontent.com/LibreOffice/dictionaries/master/ru_RU/ru_RU.aff
https://raw.githubusercontent.com/LibreOffice/dictionaries/master/uk_UA/uk_UA.dic
https://raw.githubusercontent.com/LibreOffice/dictionaries/master/uk_UA/uk_UA.aff
Щодо семантичного пошуку. У кожному з движків можна використовувати OpenAI embedding (перетворювач рядків у вектори), але ви повинні розуміти, що він платний, тому я його не реалізовував.
Якщо хочете використовувати тільки безкоштовні рішення:
OpenSearch - потрібен окремий Embedding API з безкоштовною моделлю.
Typsense - є вбудований механізм embedding-а і невеликий список доступних моделей
Meilisearch - вбудованих механізмів embedding-а не знайшов, а окремий API для цього просто не став робити. Якщо вже заморочуватися, то краще отримати всі можливості Opensearch.
2. Embedding API
Повторю, що цей крок потрібен тільки для Opensearch при використанні семантичного пошуку.
Вибираємо директорію для проекту і копіюємо туди файли:
docker/embedder/app/main.py - сам файл додатка
docker/embedder/requirements.txt - залежності, щось на зразок composer.json
Створюємо також .env файл з параметрами:
API_TOKEN=дивіться наступний пункт
SEARCH_EMBEDDING_MODEL=дивіться наступний пункт
Встановлюємо залежності (на сервері повинен бути Python)
pip install --no-cache-dir -r requirements.txt
Запускаємо сервер
uvicorn main:app --host 0.0.0.0 --port 8000
При першому запуску після установки знадобиться деякий час на ініціалізацію.
Щоб все це працювало після збоїв і перезапусків системи, створіть systemd unit (ChatGPT легко напише вам команди і конфігурацію)
3. Пошукове API
Копіюємо файли з github репозиторію в поточну директорію:
git clone https://github.com/ozzzi/api-opencart.git .
Створюємо файл середовища .env
cp .env.example .env
Заповнюємо його:
APP_NAME=API
APP_ENV=production
APP_DEBUG=false
APP_URL=http://ваш_урл
налаштовуємо підключення до бази опенкарт
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
доступ до API
API_TOKEN - токен аутентифікації для API
API_IP_ADDRESS - IP-адреса, з якої дозволені звернення
налаштування пошуку
SEARCH_API_KEY - токен доступу до opensearch, який вказали під час встановлення
SEARCH_USER - користувач opensearch
SEARCH_HOST
SEARCH_PORT
SEARCH_SSL - true/false
Модель для перетворення текстових даних у вектори
SEARCH_EMBEDDING_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
для Typsense є список доступних моделей: https://huggingface.co/typesense/models-moved/tree/main. Можна вибрати, наприклад, ts/multilingual-e5-large, але зверніть увагу на розмір моделі, він може бути як кілька сотень мегабайт, так і кілька гігабайт і буде завантажений автоматично на ваш диск при першому використанні.
EMBEDDED_URL - адреса Embedding API, за замовчуванням адреса http://localhost:8000/vectorize
SEARCH_DISTANCE_THRESHOLD - порогова оцінка документа, якщо у документа вона буде нижчою, то він не потрапить у видачу. Даний параметр пов'язаний із семантичним пошуком. Коли ми порівнюємо вектори, ми обчислюємо відстань або кут між ними. У нашому випадку, чим ближче оцінка до 1, тим семантично наші вектори близькі. Якщо ми не будемо обмежувати допустиму оцінку документа, ми просто отримаємо купу нерелевантних для нас результатів. Цей параметр підбирається індивідуально, можна почати зі значення 0.8. А щоб взагалі розуміти, яку оцінку отримав документ і чому якийсь документ взагалі потрапив у видачу, увімкніть дебаг-режим (див. параметр) і через dump()/dd() подивіться відповідь opensearch.
SEARCH_DEBUG - вмикає дебаг інформацію (true/false)
Встановлення залежностей (повинен бути встановлений composer)
composer install --no-dev --prefer-dist --optimize-autoloader
Генеруємо ключ додатка:
php artisan key:generate
Коли всі сервіси працюють, можна приступати до налаштування пошуку. Ось перелік консольних команд, як потрібно запустити в корневій директорії API
php artisan search:setup - налаштування семантичного пошуку (тільки для Opensearch)
php artisan search:create-index - створюємо пошуковий індекс
php artisan search:reindex - наповнюємо індекс даними
Опис ключових компонентів:
app/Providers/OpensearchProvider.php - тут конфігурується з'єднання з пошуковою системою і задається схема індексу: поля, їх типи, ваги, опції.
app/Services/Search/Engines/Opensearch/OpensearchIndexer.php - тут конфігурується індекс: маппінг полів, аналізатори, фільтри тощо.
app/Services/Search/Engines/Opensearch/OpensearchSearcher.php - всі налаштування пошуку
app/Console/Commands/Search/Reindex.php - консольна команда, в якій відбувається наповнення індексу даними з бази
4. Модуль opencart
Модуль додає autocomplete у форму пошуку і замінює стандартний пошук. Встановлюється модуль як зазвичай. Потім додаємо в config.php налаштування підключення до пошукового API:
define(“API_URL”, “https://ваша_адреса/api/”);
define(“API_TOKEN”, “токен”);
search_oc23.ocmod.zip - для opencart 2.3
search_oc3x.ocmod.zip - для opencart 3.x
Все. Пошук готовий до роботи.
При бажанні сюди можна додати ще фасетний пошук і тим самим фільтрувати результати за категоріями, брендами тощо.