PunchBruno Fernandes’ home burgled while in Italy for Como matchEl ComercioMaría Corina Machado redobla la presión en Venezuela: protestas, elecciones, presos políticos y su regreso al paísInquirerClasses suspended in Pagudpud due to Typhoon RosalRTP DesportoI Liga: Jornada abre em GuimarãesDaily MaverickClaude Monet’s ‘Iris’ becomes most expensive artwork sold at auction in France, Sotheby’s saysBollywood HungamaNo football, no gym, no restaurants? Tiger Shroff shooting raises a FRIGHTENING question: Are Bollywood stars becoming PRISONERS of their own fame?ZDF heuteEntdecken Sie das ZDF-NachrichtenstudioRapplerRain eases in Batanes as Typhoon Rosal starts moving awayThe Jerusalem PostIsrael’s election is being cast as a historic showdown - but four trends tell a different storyVanguard2027: Adeleke backs Tinubu; maintains support for Accord legislative candidatesESPNCaleb Williams, Bears find just enough to hold off PatsRai NewsTG3 Fuori TG del 23/10/2026
The Daily Newsstand · Free, Always
Friday, October 23, 2026

[Перевод] Искали носки, получили брюки: сравнили шесть способов поиска на одном каталоге

Translate

Искали носки, получили брюки: сравнили шесть способов поиска на одном каталоге

Время на прочтение13 мин

Охват и читатели190

Перевод

Искали носки, получили брюки: сравнили шесть способов поиска на одном каталоге

Проверили полнотекстовый, векторный, гибридный, поиск по фото, геопоиск и AI на 82 524 товарах. Показываем, где они ошибаются, что находят и какой SQL стоит за результатами.

«Мир изменился. Я чувствую это в воде, чувствую в земле, ощущаю в воздухе».

  • Галадриэль, «Властелин колец: Братство кольца»

С поиском тоже многое изменилось. Долгое время, когда нужно было сделать поиск по каталогу, первым делом думали о словах: как их разобрать, как учесть опечатки и как отсортировать найденное. Эти вопросы никуда не делись. Просто теперь к полнотекстовому поиску добавились векторы, гибридный поиск, поиск по фотографиям и AI, который вместо списка товаров может сразу дать ответ.

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

Так какой поиск лучше? Мы решили не спорить на словах, а дать разным способам поиска один и тот же каталог и посмотреть, что получится.

Для этого собрали Manticore Search Playground. В нём семь вкладок: можно искать по словам, по смыслу, по фотографии, по координатам, сравнивать выдачи и задавать вопросы AI. Причём под каждой выдачей виден SQL, который её получил, и аналогичный запрос к HTTP JSON API.

Manticore Search Playground

Manticore Search Playground

Получилось не просто демо с красивыми карточками, а небольшое приложение с открытым кодом. Его можно запустить локально через Docker Compose и посмотреть, как всё сделано. Полнотекстовый, векторный, гибридный поиск и геопоиск используют одну таблицу товаров. Для фотографий мы добавили отдельную таблицу и сервис векторизации, а для AI - CALL CHAT (SQL-команда Manticore)

Ниже покажем конкретные запросы, неожиданные результаты и SQL, который за ними стоит. Все числа по количеству результатов в выдаче и времени работы - за сентябрь 2026 года. После обновления каталога, моделей или самого приложения они могут измениться.

Что именно мы сравниваем

Мы взяли 82 524 товара из ConvApparel - датасета от Google по подбору одежды. В рекомендациях из этих диалогов есть названия, описания, характеристики и фотографии товаров.

На этих данных работают семь вкладок: Full-text, Vector, Hybrid, Image, Geo, Compare и Ask AI. Для каждого запроса Playground показывает запрос и в виде SQL и виде команды curl: их можно выполнить на своей мантикоре.

Почему это важно? Потому что сравнение поисковиков часто сводится к двум разным демо с разными данными. Здесь каталог один. Меняется только способ поиска, и разницу видно сразу.

Для большинства режимов нам хватило одной таблицы:

CREATE TABLE convapparel_products (
    id bigint,
    item_id text,
    title text,
    description text,
    features text,
    image_url text,
    category string indexed attribute,
    source text,
    categories json,
    lat float,
    lon float,
    embedding_vector float_vector knn_type='hnsw' hnsw_similarity='COSINE'
        model_name='onnx-models/all-MiniLM-L12-v2-onnx' from='title,description,features'
) min_infix_len='2'

Что тут важно и что стоит иметь в виду, если вы сами делаете что-то с Manticore:

  • min_infix_len='2' позволяет искать части слов длиной от двух символов - это пригодится для подсказок и автодополнения

  • model_name вместе с from='title,description,features' включает автоматическую векторизацию. Добавляете товар - Manticore рассчитывает вектор по его текстовым полям. Ищете по смыслу - запрос преобразуется в вектор той же моделью. Для этого не нужно поднимать ещё один сервис.

С фотографиями будет иначе, но до них доберёмся попозже.

1. Полнотекстовый поиск: две опечатки - и поиск ничего не находит

Начнём с обычного полнотекстового поиска. Допустим, покупатель хотел написать leather jacket, но ввёл lether jaket. Две опечатки в двух словах - ничего необычного для интернет-магазина.

Если отключить Typo tolerance, Playground вернёт ноль товаров.

Включаем исправление опечаток - и получаем 1 027 товаров. В начале списка, как и ожидалось, кожаные куртки.

Вот запрос, который выполняется при включённой опции:

SELECT id, title, description, features, category, image_url, weight() AS text_score
FROM convapparel_products
WHERE MATCH('lether jaket')
LIMIT 12
OPTION fuzzy=1
FACET categories ORDER BY COUNT(*) DESC

Что тут происходит? MATCH() ищет по текстовым полям. weight() показывает оценку релевантности. По умолчанию используется proximity_bm25: на результат влияют и BM25, и расстояние между словами.

Но главное в этом примере - OPTION fuzzy=1. Эта опция позволяет находить слова с похожим написанием. Убрали её - получили ноль. Вернули - получили кожаные куртки.

Есть два нюанса по user experience, о которых стоит помнить, когда добавляешь такой поиск в приложение.

  • Покажите пользователю, что запрос был исправлен. Playground вызывает CALL QSUGGEST и пишет Showing results for leather jacket. Если поисковик однажды неправильно угадает слово, пользователь хотя бы поймёт, откуда взялась странная выдача.

  • В режиме исправления опечаток не предлагайте пользователям использовать весь синтаксис полнотекста Manticore. Люди вводят обычные фразы, а не специально составленные выражения для MATCH(). В режиме поиска с опечатками символы операторов экранируются (кроме кавычек для фразового поиска).

Нашлось 1 027 товаров, а по категориям - 1 127. Как так?

В конце того же SQL есть FACET categories. Он возвращает не только товары, но и разбивку результатов по категориям. Для нашего запроса получается:

Outerwear - 960, Footwear - 58, Tops - 55, Bottoms - 54.

Складываем. Получается 1 127, хотя всего найдено 1 027.

Нет, это не ошибка в арифметике. Один товар может относиться к нескольким категориям, поэтому при подсчёте по категориям он встречается несколько раз, а в общем числе товаров - один.

Это считается так:

  1. Храним категории в JSON-массиве. У одного товара, например, могут быть сразу ["outerwear", "tops"].

  2. Для фильтрации используем categories IN ('outerwear', 'tops'): товар подходит, если у него есть хотя бы одна из выбранных категорий.

  3. Общее количество товаров получаем отдельным COUNT(*), чтобы не складывать значения фасетов и не считать один товар несколько раз.

Почему автодополнение не должно предлагать print на запрос int

Есть ещё одна привычная вещь, которая часто раздражает, если её неправильно настроить: подсказки в поисковой строке.

Откройте вкладку Full-text, выключите Search as you type (иначе выдача перекроет подсказки) и наберите linen sh. Ещё до того, как вы закончите слово, появятся linen shirt, linen shoes и linen shorts.

За это отвечает всего один запрос:

CALL AUTOCOMPLETE('linen sh', 'convapparel_products', 0 AS fuzziness, 1 AS append, 0 AS prepend)

Но сами параметры мы выбрали не случайно.

  • Передаём весь введённый текст. Для linen sh Manticore сохраняет linen и дополняет только последнее слово — sh. Каждая строка ответа уже готовая подсказка.

  • Не ищем совпадения посреди слова. При 0 AS prepend к запросу слева не добавляется *. Иначе на int система может предложить print: технически совпадение есть, но человеку, который начал вводить слово, оно вряд ли нужно.

  • Не исправляем опечатки прямо в подсказках. Параметр 0 AS fuzziness означает, что система продолжает именно введённое слово. Исправление опечаток происходит уже при поиске, и там пользователь видит, что изменилось.

Все настройки CALL AUTOCOMPLETE есть в документации.

2. Векторный поиск понимает запрос, даже если вы не знаете название товара

Теперь другая задача и другой тип поиска. Человек пишет не название вещи, а то, зачем она ему нужна: outfit for a job interview. То есть хочет подобрать одежду для собеседования.

Полнотекстовому поиску здесь непросто: он в общем случае соединяет слова через AND (можно и иначе конечно, но по дефолту так), и товаров, в которых одновременно встречаются outfit, job и interview, нет. Получаем ноль результатов.

А на вкладке Vector появляются деловые костюмы и комплекты с пиджаками. Хотя пользователь не писал ни business suit, ни blazer.

SELECT id, title, description, features, category, image_url, knn_dist() AS distance
FROM convapparel_products
WHERE knn(embedding_vector, 100, 'outfit for a job interview')
LIMIT 12

Заметьте, что в этом SQL нет готового вектора - передаётся обычный текст. Manticore преобразует его в вектор той же моделью, которой обработаны товары, и через индекс HNSW ищет ближайшие. Модель связывает слова из запроса с описаниями вроде business suit, office и blazer.

Иногда это помогает даже с опечатками. Например, warm waterprof jacket возвращает водонепроницаемые куртки, хотя никакого специального исправления опечаток здесь не включали.

Казалось бы, отлично. Но вернёмся к носкам из начала статьи.

По запросу merino wool socks первые четыре результата - брюки из мериносовой шерсти, куртка, ещё одни брюки и футболка. Носки только пятые.

Векторный поиск уловил merino wool, но слову socks не придал достаточного значения. Точно так же он может слишком свободно отнестись к цвету, размеру или конкретному типу товара. Смысл похожий, а товар всё-таки не тот.

3. Гибридный поиск: если можно искать и по словам, и по смыслу, зачем выбирать?

После примера с носками возникает закономерный вопрос: почему не объединить оба подхода?

Именно это делает гибридный поиск. Откройте merino wool socks во вкладке Hybrid. Теперь носки на первом месте, а дальше - другие товары из мериносовой шерсти.

SQL выглядит так:

SELECT id, title, description, features, category, image_url,
    weight() AS text_score, knn_dist() AS distance
FROM convapparel_products
WHERE MATCH('merino wool socks')
  AND knn(embedding_vector, 100, 'merino wool socks')
LIMIT 12
OPTION fuzzy=1, fusion_method='rrf'

Manticore выполняет полнотекстовый и векторный поиск, а затем объединяет их результаты через Reciprocal Rank Fusion (RRF). Проще говоря, учитывает место товара в обоих списках. Если он стоит высоко и там, и там, это помогает ему подняться в общей выдаче.

В интерфейсе это видно по меткам Words, Meaning и Words + meaning. Они показывают, какой из способов нашёл конкретный товар.

Для случая, когда обе части используют один и тот же текст, есть и короткая запись - hybrid_match(). Она сама запускает полнотекстовый поиск, преобразует запрос в вектор, выполняет KNN и объединяет выдачи через RRF.

Ещё один пример — bicycle shoes. Велотуфли MTB и Unisex Bike Shoes находятся и по словам, и по смыслу. Векторный поиск добавляет Cycling Shoes, в которых вообще не встречается слово bicycle. Но есть и неожиданный результат: на первом месте оказываются классические оксфорды Paul Parkman, не имеющие отношения к велоспорту. Просто у них носок типа bicycle toe. Из-за этого совпадения они возглавляют полнотекстовую выдачу, а поскольку векторный поиск тоже их находит, гибридный ставит их первыми.

А для warm waterprof jacket полнотекстовая часть исправляет опечатку, пока векторная помогает подобрать тёплые водонепроницаемые куртки. Оба способа полезны, просто по-разному.

Сравнили три выдачи рядом - разница стала очевиднее

Чтобы не переключаться туда-сюда, мы сделали вкладку Compare. Она запускает запрос сразу тремя способами - Full-text, Vector и Hybrid - и показывает результаты в соседних колонках.

Если один товар встретился в нескольких выдачах, Playground это отмечает. Навели курсор на карточку - подсветились все её появления. Иногда полнотекстовый и векторный поиск действительно находят одно и то же:

Но интереснее случаи, когда они расходятся.

Запрос outfit for a job interview: слева у полнотекстового поиска пусто, а векторный и гибридный предлагают костюмы и комплекты с пиджаками.

Запрос merino wool socks: полнотекстовый находит носки, векторный увлекается брюками и куртками, а гибридный ставит носки первыми.

Для такого сравнения лучше всего брать не придуманные нами фразы, а запросы, которые вводят ваши пользователи. Если хотите просто посмотреть, как это работает, начните с bicycle shoes, lether jaket или steel toe boots.

Похожие товары: можно обойтись даже без текста запроса

Допустим, посетитель уже открыл кожаную куртку и хочет посмотреть что-то похожее. Ему не нужно снова вводить слова в поисковую строку.

В карточках Playground для этого есть два блока: Similar by description и Similar by photo.

Для поиска похожих товаров по описанию выполняется такой SQL:

SELECT id, title, description, features, category, image_url, knn_dist() AS distance
FROM convapparel_products
WHERE knn(embedding_vector, 8, 2388085963546244483)
LIMIT 8

Здесь число в knn() - это id товара, а не сам вектор. Manticore находит сохранённый вектор товара и ищет по нему ближайшие. Приложению не нужно сначала вытаскивать вектор из базы, а потом отправлять его обратно.

Для похожих фотографий принцип тот же. Только векторы изображений лежат в отдельной таблице.

4. Поиск по фотографиям

Пробовали описать в магазинной карточке конкретную клетчатую рубашку? Название и характеристики конечно несут какую-то информацю о ткани и размере, но не всегда помогут представить рисунок и оттенок. Здесь полезнее смотреть на сами фотографии.

На вкладке Image можно ввести red plaid flannel shirt - или вообще загрузить, перетащить либо вставить картинку, по которой хочется найти похожую вещь.

В демо, используя специализированную модель, мы преобразуем и текст, и фотографии в сопоставимые векторы. Поэтому описание красной клетчатой рубашки и её изображение оказываются рядом в многомерном пространстве при векторном поиске.

В отличие от текстового векторного поиска, для изображений используется отдельный сервис векторизации. Сначала он рассчитывает вектор запроса - в публичном Playground это обычно занимает 50–250 мс, и время показывается как turned into a vector in … ms. Затем Manticore ищет ближайшие векторы фотографий и получает из основной таблицы соответствующие товары.

SELECT id, knn_dist() AS distance
FROM convapparel_product_images
WHERE knn(image_vector, 100, (-0.0021, -0.01, 0.0782, …))
LIMIT 100;

SELECT id, title, description, features, category, image_url
FROM convapparel_products
WHERE id IN (3698494869106650404, 3557146672156430291, 1902002584948931003, …)
LIMIT 100

Здесь мы сократили SQL для наглядности: в настоящем запросе передаются все 512 чисел вектора и 100 айдишек товаров.

В реализации есть два решения, которые могут пригодиться и в вашем приложении:

  • Поиск по изображениям не зависит от основного каталога. Для текста мы в данном случае использовали MiniLM, а для фотографий - модель Fashion CLIP. Векторы изображений храним в отдельной таблице, поэтому можем заменить модель или пересчитать все векторы фотографий, не затрагивая товары и их текстовый поиск.

  • Не индексируем одинаковые картинки-заглушки. Если вы вдруг сами кравлите, то понимаете, что стандартная заглушка No image available ничего не говорит о товаре, даже если встречается тысячу раз. Заглушки лучше вообще не индексировать. Поэтому в нашем демо из 82524 товаров векторов изображений нет ну 0.3%.

5. Геопоиск: товары на карте

С координатами была одна проблемка: в исходном ConvApparel их нет. Чтобы показать геопоиск, мы присвоили товарам случайные координаты в районе Нью-Йорка просто, чтобы показать сам механизм работы геопоиска.

Во вкладке Geo можно перетащить точку по карте, изменить радиус и тут же увидеть, каким стал SQL.

SELECT id, title, description, features, category, image_url, lat, lon,
    GEODIST(40.712800, -74.006000, lat, lon, {in=degrees, out=km}) AS distance_km
FROM convapparel_products
WHERE distance_km <= 10
ORDER BY id ASC
LIMIT 100

GEODIST() считает расстояние. Его результат, если нужно, можно вывести в SELECT, использовать в фильтре или сортировке.

В нашем демо точки сортируются по id, чтобы они распределялись по всей выбранной области. Для реальной задачи типа “покажи ближайшие магазины” логичнее ORDER BY distance_km ASC: тогда первыми окажутся ближайшие объекты.

6. AI-поиск: умный совет дороже золота

А если покупатель вообще не хочет разбираться в фильтрах и списках? Допустим, он спрашивает: What should I wear to a summer wedding on the beach? - что надеть на летнюю свадьбу на пляже?

На вкладке Ask AI можно задать этот вопрос и получить ответ, основанный на товарах из каталога.

В Manticore для этого есть CALL CHAT:

CALL CHAT('What should I wear to a summer wedding on the beach?',
    'convapparel_products', 'shopping_assistant', '', 'embedding_vector')

CALL CHAT работает с таблицей товаров и векторным индексом. Для ответа система может сначала найти подходящие товары и передать языковой модели сведения о них. В нашем случае это поля title, description и features. Playground добавляет к ответу ссылки на найденные карточки.

Диалог можно продолжить. Например, сначала спросить I need warm boots for walking the dog in snow, а следом - Which of those are waterproof? Контекст предыдущего вопроса сохраняется, и не приходится заново объяснять, о каких ботинках речь.

Что стоит учесть:

  • Показывайте товары, на которые ссылается AI. Если он уверенно советует «дышащее платье», пусть человек сможет открыть карточку и проверить, действительно ли это написано в описании.

  • Не ждите, что языковая модель исправит все ошибки поиска. Сначала подходящие товары должны попасть в контекст. Если нужная вещь не нашлась на этом этапе, AI не сможет надёжно учесть её в рекомендации. Соответственно влияет и качество модели и настройки полнотекстового поиска.

И ещё скорость. В публичном Playground ответ AI обычно занимает 5–15 секунд. Это тоже зависит много от чего, но в первую очередь от LLM-модели и провайдера LLM (или вашего железа, если вы запускаете всё локально, что тоже вполне возможно с Manticore). О настройке чата и оценке качества поиска подробнее писали в отдельной статье.

А если мне нужен не SQL, а HTTP API?

Manticore говорит на двух языках: SQL и JSON. Про SQL было приведено много примеров выше, для любителей JSON через HTTP в демо помимо SQL те же примеры приведены в виде команды curl. Она обращается к HTTP JSON API и выполняет тот же поиск, что показан в SQL.

Например, для запроса с опечатками команда выглядит так:

curl -X POST localhost:9308/search \
  -H 'Content-Type: application/json' \
  -d '{
  "table": "convapparel_products",
  "query": {"query_string": "lether jaket"},
  "options": {"fuzzy": true},
  "limit": 12,
  "_source": ["title", "description", "features", "category", "image_url"],
  "aggs": {"categories": {"terms": {"field": "categories"}, "sort": [{"count(*)": {"order": "desc"}}]}}
}'

Если нужная таблица уже загружена в Manticore, команда вернёт те же товары и разбивку по категориям.

Кстати, возвращаясь к теме того, что по умолчанию полнотекст объединяет все слова, которой коснулись выше: здесь есть неочевидная разница между двумя вариантами JSON-запроса. query_string, как и SQL MATCH(), по умолчанию соединяет слова через AND. А JSON-запрос match - через OR. Если переписываете запрос из одного формата в другой, об этом лучше помнить.

Всё можете запустить у себя локально

Playground открыт на GitHub. Чтобы запустить его локально, достаточно Docker Compose:

git clone https://mnt.cr/go/KMnCwP
cd demo-search-playground
cp .env.example .env
./scripts/init_manticore.sh
docker compose up --build app embed

После запуска откройте https://mnt.cr/go/3j4ju6

В репозитории уже лежат рассчитанные векторы товаров, поэтому заново считать их при установке не нужно. Если хотите попробовать Ask AI, заранее добавьте ключ OpenRouter в OPENROUTER_API_KEY файла .env. Для остальных вкладок ключ не требуется.

При первом запуске сервис изображений скачивает модель Fashion CLIP размером около 600 МБ. Пока она загружается, другие режимы уже работают. А если захотите выполнить любой SQL со страницы вручную, используйте docker compose exec manticore mysql.

Что попробовать в Playground - короткая шпаргалка

Возможность

В Manticore

Попробовать

Полнотекстовый поиск с сортировкой по релевантности

MATCH(), weight()

levis 501

Устойчивость к опечаткам

OPTION fuzzy=1, CALL QSUGGEST

lether jaket

Группировка по категориям

FACET

lether jaket

Автодополнение

CALL AUTOCOMPLETE

На вкладке Full-text введите linen sh

Векторный поиск с автоматической векторизацией

knn() с текстовым запросом

outfit for a job interview

Гибридный поиск

MATCH() + knn(), fusion_method='rrf'

merino wool socks

Похожие товары

knn() с id документа

Откройте любой товар

Поиск по изображениям

knn() по векторам фотографий

red plaid flannel shirt или загрузите фото

Геопоиск

GEODIST()

вкладка Geo

AI-поиск

CALL CHAT

summer wedding on the beach

JSON API

эндпоинт /search

Команда curl под любым результатом

Публичный Playground работает на небольшом сервере. Большинство запросов, кроме AI-чата, выполняется примерно за 5–100 мс.

На этих примерах видно, почему нет смысла выбирать один способ поиска на все случаи. Полнотекстовый хорош, когда важны сами слова; векторный помогает, когда пользователь описывает задачу; гибридный сочетает оба подхода. Для фотографий и координат нужны свои способы поиска, а AI пригодится, когда вместо списка товаров хочется получить готовый ответ.

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

Сможете такой придумать?

Откройте Playground и попробуйте. Если найдёте особенно неудачный запрос, создайте issue в GitHub или напишите нам в Slack. А если хотите собрать похожий поиск в своём приложении, код Playground можно взять за основу.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.