Поиск по семантическим векторам и другие темы для завоевания друзей и поклонников
Весь ландшафт поиска: точный, нечёткий, семантический, гибридный — и когда стоит комбинировать их все.
Поиск — это не одна вещь, и семантический поиск не заменяет всё остальное.
«Найти пользователя с почтой dan@example.com» и «найди мне статьи об отладке для начинающего инженера» — оба запроса называются поиском, но как инженерные задачи они почти не имеют общего. У первого есть правильный ответ и поиск по индексу за O(log n). У второго правильного ответа нет — есть только релевантность, — и требуется понимание языка, намерения и смысла.
Инженеры, которые убедительнее всего принимают решения о поиске — те, кто выигрывает споры и строит правильную систему, — понимают весь ландшафт. Они знают, какой инструмент взять и почему, и могут объяснить это чётко.
В этой статье рассматривается семантический слой: что на самом деле делает векторный поиск, где он выигрывает и где ему лучше не мешать. Полезный ответ — не «превратите всё в эмбеддинги». Полезный ответ — знать, когда векторы должны стоять рядом с лексическим, нечётким и точным поиском в гибридной архитектуре.
Лексическая и нечёткая половина картины — tsvector, pg_trgm, pg_search — описана в Postgres Text Searching Guide 2026.
Термины: краткий справочник
Векторное представление (embedding) — плотный список чисел с плавающей точкой, выдаваемый моделью; представляет фрагмент текста (или изображение, аудио и т. д.) как точку в многомерном пространстве. Семантически близкий контент оказывается рядом; далёкий — далеко.
Лексический поиск — поиск по точному совпадению слов и токенов. Быстрый, детерминированный, корректный для известных терминов. Не понимает синонимы, перефразировки или эквиваленты на других языках.
Семантический поиск — поиск по смыслу, а не по токенам. Запрос «как обрабатывать таймауты» может найти документ «настройка политик повторных попыток» без единого общего слова, потому что их векторные представления геометрически близки.
Вектор — список чисел. В контексте поиска — вывод модели эмбеддингов. «Векторный поиск» находит векторы, ближайшие к вектору запроса, по геометрическому расстоянию.
FTS (полнотекстовый поиск) — встроенный лексический поиск Postgres на основе tsvector / tsquery. Токенизирует, стеммирует и индексирует текст для запросов по ключевым словам. Силён для прозы и поиска по точным терминам; слеп к смыслу.
BM25 — алгоритм ранжирования для лексического поиска (используется в Elasticsearch, Qdrant и других). Оценивает результаты по частоте термина, взвешенной по редкости термина в корпусе. Лучше голого сопоставления ключевых слов; всё ещё лексический.
HNSW (Hierarchical Navigable Small World) — стандартный индекс приблизительного поиска ближайших соседей для векторного поиска. Строит многоуровневый граф близости для быстрых запросов с высокой полнотой. pgvector, Qdrant, Weaviate и большинство других используют его.
RRF (Reciprocal Rank Fusion) — алгоритм слияния ранжированных списков результатов из нескольких систем поиска. Использует только позицию в ранге — нормализация скоров не нужна. Результат, высоко стоящий в обоих списках (FTS и векторном), получает более сильный комбинированный скор, чем тот, что доминирует только в одном.
Что на самом деле делает семантический поиск
Векторные эмбеддинги преобразуют текст (или изображения, аудио и т. д.) в список чисел — точку в многомерном пространстве. Модель эмбеддингов обучена так, что семантически связанный текст оказывается рядом в этом пространстве. «Собака» и «пёс» оказываются близко. «Бежать марафон» и «запустить Python-скрипт» оказываются далеко друг от друга, несмотря на общее слово.
Поиск похожести в этом пространстве находит документы, чей смысл ближе всего к смыслу запроса, независимо от совпадения слов.
Это означает:
- «Как настроить таймауты запросов?» может найти статью «Установка лимитов соединений и политик повторных попыток» — ни одного общего ключевого слова, но высокая концептуальная релевантность
- «Что-нибудь лёгкое для летнего вечера» может найти рекомендацию вина без единого ключевого слова в описании продукта
- Запрос на английском может найти релевантные документы на французском, испанском или японском, если модель эмбеддингов обучалась на многоязычных данных
Лексический поиск (tsvector, pg_trgm) ничего из этого не умеет. Он работает со словами и символами, а не со смыслом. Эти инструменты не взаимозаменяемы — они решают разные задачи.
Когда pgvector выигрывает
Построение RAG. Retrieval-Augmented Generation извлекает фрагменты документов, смысл которых ближе всего к вопросу пользователя, а затем передаёт их языковой модели как контекст. Этот шаг извлечения — векторная операция. FTS пропустит перефразировки, синонимы и концептуальные совпадения, которые релевантный фрагмент может выразить иначе. Преимущество pgvector перед отдельным векторным хранилищем: он работает внутри вашего существующего экземпляра Postgres — не нужно разворачивать, обслуживать или синхронизировать отдельный сервис.
Пользователи описывают, что хотят, а не что искать. «Статьи о том, как строить уверенность в роли нового руководителя» — в релевантных постах нет ключевых слов, которые надёжно встречаются в таких запросах. «Лёгкий фреймворк для обработки побочных эффектов» — в документации может не быть именно этих слов. Векторный поиск сопоставляет намерение, а не написание.
Поиск похожих элементов. Похожие товары, аналогичные тикеты поддержки, дубликаты баг-репортов, статьи, которые могут понравиться. «Найти задачи, похожие на эту» — это поиск ближайших соседей: встраиваем элемент, находим его геометрических соседей. Важное ограничение: векторный поиск всегда возвращает результаты, даже когда ничего по-настоящему похожего нет. Для дедупликации и рекомендаций фильтруйте по минимальному порогу похожести (например, косинусная похожесть ≥ 0,80), чтобы не выдавать низковероятные совпадения за значимые.
Семантическая дедупликация. Перед индексацией контента для RAG или поиска часто нужно выявить почти-дубликаты в корпусе — статьи, пересмотренные несколько раз, тикеты поддержки, поданные дважды, записи базы знаний, которые существенно перекрываются. Создайте эмбеддинги документов и отфильтруйте их по косинусной похожести, чтобы пометить или объединить почти-дубликаты до того, как они загрязнят индекс. Это предотвращает выдачу нескольких почти идентичных фрагментов при извлечении и разбавление контекстного окна.
Многоязычный поиск. Многоязычные модели эмбеддингов отображают семантически эквивалентный контент на разных языках в близкие векторы. Запрос на испанском «perder peso» может найти английскую статью о «sustainable weight loss habits» — ни одного общего токена, тот же смысл. FTS требует настройки словарей для каждого языка и плохо справляется с кросс-язычными запросами. pg_trgm не зависит от языка, но работает на уровне орфографии, а не смысла.
Настройка pgvector
От установки расширения до запроса по похожести — всё укладывается в несколько SQL-команд:
CREATE EXTENSION IF NOT EXISTS vector;
ALTER TABLE documents ADD COLUMN embedding vector(1536);
-- HNSW обычно стоит попробовать первым для датасетов среднего размераCREATE INDEX documents_embedding_idx ON documents USING hnsw (embedding vector_cosine_ops);
-- Запрос семантического поискаSELECT id, title, 1 - (embedding <=> $1::vector) AS similarityFROM documentsORDER BY embedding <=> $1::vectorLIMIT 10;<=> — косинусное расстояние. 1 - cosine_distance даёт косинусную похожесть (1,0 = идентично, 0,0 = ортогонально). Для ivfflat (более старой, но быстрее строящейся альтернативы) используйте lists = sqrt(row_count) как отправную точку.
Что pgvector обрабатывает плохо
- Точное сопоставление токенов — SKU товаров, коды ошибок, имена функций.
ORD-12345не семантически похож ни на что. Поиск на основе эмбеддингов может вернутьORD-12344или ничего релевантного. Используйте FTS или B-tree индекс. - Имена и собственные существительные. Пространство эмбеддингов организовано по смыслу, а не по написанию. Запись пользователя «Micheal Jordan» не обязательно окажется рядом с «Michael Jordan» в векторном пространстве.
- Короткие строки, где посимвольная похожесть важнее смысла. Это обрабатывает
pg_trgm. - Запросы, где должен встретиться точный термин. BM25 и FTS надёжнее для поиска по известным терминам.
Гибридный поиск: аргумент за оба подхода
Техническая документация — самый очевидный пример, где одного инструмента недостаточно.
Пользователи, ищущие «как настроить таймауты», нуждаются в концептуальном совпадении: статья «Установка политик повторных попыток и лимитов соединений» не содержит общих ключевых слов, но это именно то, что им нужно.
Те же пользователи ищут withRetry(), ECONNRESET и ERR_SOCKET_TIMEOUT. Эти точные строки должны встретиться — семантический поиск может не найти их надёжно, а ложноположительный результат (концептуально похожий, но не тот API) будет откровенно вводить в заблуждение.
Векторный поиск обрабатывает концептуальные запросы. FTS обрабатывает точные термины. Ни один из них по отдельности хорошо не справляется с обоими типами.
Решение — гибридный поиск: запустить оба и слить результаты.
Reciprocal Rank Fusion
Reciprocal Rank Fusion (RRF) — стандартный алгоритм объединения ранжированных списков из разных систем поиска. Он не требует нормализации скоров между системами — использует только позиции в ранге. Результат, высоко стоящий в обоих списках, получает более сильный комбинированный скор, чем тот, что доминирует только в одном.
WITH fts_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY ts_rank(search_vector, query) DESC) AS rank FROM documents, to_tsquery('english', $1) query WHERE search_vector @@ query LIMIT 50),vector_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> $2::vector) AS rank FROM documents ORDER BY embedding <=> $2::vector LIMIT 50),rrf AS ( SELECT COALESCE(f.id, v.id) AS id, COALESCE(1.0 / (60 + f.rank), 0) + COALESCE(1.0 / (60 + v.rank), 0) AS rrf_score FROM fts_results f FULL OUTER JOIN vector_results v ON f.id = v.id)SELECT d.id, d.title, rrf.rrf_scoreFROM rrfJOIN documents d ON d.id = rrf.idORDER BY rrf_score DESCLIMIT 10;60 в знаменателе — константа RRF. Более высокие значения сглаживают разницу позиций; более низкие — усиливают. Значение по умолчанию 60 хорошо работает для большинства типов контента.
RRF позволяет избежать более сложной задачи нормализации ts_rank (логарифмически-частотный скор) относительно косинусного расстояния (геометрическая мера). Они не сопоставимы. RRF спрашивает только: «как высоко этот результат стоял в каждом списке?»
Гибридный поиск с триграммами
Для пользовательского поиска по смешанному контенту — где в одной сессии могут искать имя человека, концепцию или точный термин — трёхстороннее слияние обрабатывает всё:
WITH trgm_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY similarity(title, $1) DESC) AS rank FROM documents WHERE title % $1 LIMIT 50),fts_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY ts_rank(search_vector, to_tsquery('english', $1)) DESC) AS rank FROM documents WHERE search_vector @@ to_tsquery('english', $1) LIMIT 50),vector_results AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> $2::vector) AS rank FROM documents ORDER BY embedding <=> $2::vector LIMIT 50),rrf AS ( SELECT COALESCE(t.id, f.id, v.id) AS id, COALESCE(1.0 / (60 + t.rank), 0) + COALESCE(1.0 / (60 + f.rank), 0) + COALESCE(1.0 / (60 + v.rank), 0) AS rrf_score FROM trgm_results t FULL OUTER JOIN fts_results f ON t.id = f.id FULL OUTER JOIN vector_results v ON COALESCE(t.id, f.id) = v.id)SELECT d.id, d.title, rrf.rrf_scoreFROM rrfJOIN documents d ON d.id = rrf.idORDER BY rrf_score DESCLIMIT 10;Это обрабатывает: нечёткие совпадения имён (триграммы), точные совпадения ключевых слов (FTS) и концептуальные запросы (вектор). Одно поисковое поле может обслуживать все три намерения пользователя.
Многослойные гибридные архитектуры
Реальные приложения редко имеют одну поисковую поверхность. У них несколько, каждая со своей потребностью:
| Поверхность | Что ищут пользователи | Рекомендуемые слои |
|---|---|---|
| Поиск по блогу / документации | Ключевые слова + концепции | FTS + pgvector (RRF) |
| Поиск по именам пользователей/клиентов | Имена с опечатками | pg_trgm |
| Поиск товаров | Названия, описания, «похожие на» | pg_trgm + FTS + pgvector |
| Дедупликация тикетов поддержки | «Задачи, похожие на эту» | Только pgvector |
| Поиск по SKU/заказам | Точные идентификаторы | B-tree индекс |
| RAG по большой базе знаний | Вопросы на естественном языке | pgvector (фрагментированные документы) |
| E-commerce «вам может понравиться» | Поведенческая + семантическая похожесть | pgvector |
| Автодополнение | Префикс, устойчивость к ошибкам | pg_trgm |
Это не гипотетика. Большинству приложений, работающих с контентом, нужны как минимум две разные поисковые поверхности с разными формами запросов. Соблазн — выбрать один подход и использовать его везде — обычно векторный поиск, потому что это модный выбор. Это приводит к дорогим эмбеддингам для задач, где триграммный индекс был бы быстрее, дешевле и корректнее.
Эмпирическое правило
Добавляйте слой, когда появляется режим отказа, который текущий слой не может исправить:
- Пользователи жалуются, что опечатки не находятся → добавьте
pg_trgm - Пользователи ищут по концепции и не находят релевантные результаты → добавьте pgvector
- Пользователи ищут точные символы или коды и получают концептуальные результаты → добавьте FTS или проверьте, не переусердствуете ли вы с векторным поиском
- Задержка становится проблемой → оцените предварительную фильтрацию, приблизительные индексы или выделенное хранилище
Если всё-таки нужно выделенное векторное хранилище
pgvector обрабатывает множество сценариев до того, как понадобится другая база данных. Грубая граница зависит от количества векторов, настроек индекса, частоты записи, фильтров, оборудования и конкуренции, поэтому относитесь к правилу «до 10 млн векторов» как к начальному предположению для бенчмарка, а не как к ограничению продукта. Когда вы действительно вырастаете за эти рамки — очень высокая конкуренция, очень низкие требования к p99 задержке, миллиарды векторов или серьёзные потребности в мультитенантной изоляции — ландшафт выделенных векторных баз данных широк и заслуживает изучения.
Что на самом деле означают столбцы матрицы
Гибридный поиск означает, что поиск по ключевым словам BM25 и векторная похожесть выполняются в одном запросе, сливаются через RRF. Без него приходится либо выбирать один режим поиска, либо сливать два запроса самостоятельно.
Редкие (sparse) векторы идут дальше, чем BM25. Редкий вектор SPLADE имеет ~30 000 измерений (по одному на термин словаря), ~98% нулей. Ненулевые позиции показывают, какие термины важны и насколько. Запрос «dogs» также взвешивает «canine» и «pet» — точность уровня BM25 плюс расширение терминов внутри векторного индекса. Если этот столбец равен false, вам нужен отдельный слой FTS для запросов по точным терминам.
# SPLADE: ~30 000 измерений, ~60 ненулевых — срабатывают только позиции релевантных словdef encode_splade(text: str) -> dict: tokens = tokenizer(text, return_tensors="pt", truncation=True, max_length=512) with torch.no_grad(): output = model(**tokens) vec = torch.log1p(torch.relu(output.logits)).max(dim=1).values.squeeze() return {"indices": vec.nonzero().squeeze().tolist(), "values": vec[vec != 0].tolist()}SQL / SQL-подобный на самом деле касается фильтрации. Векторный поиск без фильтрации — это демо. Вам всё ещё нужны область тенанта, диапазоны дат, разрешения и фильтры по категориям. Полноценный SQL (pgvector, LanceDB) выражает это рядом с вашими существующими соединениями. Специализированные базы данных используют JSON-объекты фильтров (Qdrant, Pinecone), DSL запросов (Elasticsearch, Milvus) или GraphQL (Weaviate). Они работают; SQL становится привлекательнее по мере усложнения логики фильтрации.
-- pgvector: векторная похожесть — просто ещё одно выражениеSELECT id, title, 1 - (embedding <=> $1) AS scoreFROM documentsWHERE tenant_id = $2 AND category = ANY($3::text[]) AND created_at > NOW() - INTERVAL '90 days'ORDER BY embedding <=> $1LIMIT 10;# Qdrant: эквивалентный фильтр как Python-объект — тот же результат, больше церемонийresults = client.query_points( collection_name="documents", query=query_embedding, query_filter=models.Filter(must=[ models.FieldCondition(key="tenant_id", match=models.MatchValue(value=tenant_id)), models.FieldCondition(key="category", match=models.MatchAny(any=categories)), models.FieldCondition(key="created_at", range=models.DatetimeRange(gte=cutoff)), ]), limit=10,)Мультимодальность нативно означает, что база данных поставляет модели эмбеддингов для нетекстового контента. Вы отдаёте ей сырой URL изображения; она сама занимается векторизацией. Большинство баз данных не зависят от эмбеддингов — вы владеете конвейером эмбеддингов. Marqo и Weaviate (через модули CLIP/ImageBind) замыкают этот цикл.
# Marqo: POST сырых изображений, запрос текстом — без внешнего шага эмбеддингаmq.index("products").add_documents( [{"id": "shoe-001", "image": "https://cdn.example.com/shoes/001.jpg"}], tensor_fields=["image"])results = mq.index("products").search(q="lightweight shoes for summer")# Возвращает shoe-001 несмотря на нулевое совпадение ключевых слов — CLIP обрабатывает кросс-модальное совпадениеИндекс на диске — рычаг стоимости. Индексы HNSW в оперативной памяти могут требовать несколько ГБ RAM на миллион 1536-мерных векторов, если учитывать сырые векторы, накладные расходы графа и метаданные. Альтернативы, работающие с диском (Milvus DiskANN, Elasticsearch DiskBBQ, формат LanceDB Lance, объектное хранилище Turbopuffer), часто жертвуют задержкой запросов ради снижения стоимости инфраструктуры. Для RAG-задач, где задержка модели уже доминирует, этот обмен часто стоит бенчмарка.
Максимальное количество измерений — это миграция, прячущаяся в вашей архитектуре. text-embedding-3-large использует 3072 измерения, Jina v3 может выдавать большие эмбеддинги, а исследовательские модели продолжают двигаться ввысь. Некоторые управляемые сервисы публикуют жёсткие ограничения по измерениям; другие документируют высокие ограничения или отсутствие практического ограничения для типичных моделей эмбеддингов. Проверьте текущую документацию перед принятием решения. Выбирайте что-то с запасом; миграция векторного индекса из-за достижения потолка измерений — болезненный спринт.
Последняя проверка по публичной документации проектов и страницам продуктов на 8 мая 2026 года. Относитесь к таблице ниже как к вспомогательному инструменту для принятия решений, а не как к замене проверки текущих ограничений, цен и флагов функций управляемых сервисов.
Ландшафт
| База данных | Развёртывание | Лицензия | Гибридный поиск | Редкие векторы | SQL / SQL-подобный | Мультимодальность | Индекс на диске | Макс. измерений | Зона применения |
|---|---|---|---|---|---|---|---|---|---|
| pgvector | Self-host / managed (Supabase, Neon, RDS) | OSS (PostgreSQL) | Вручную (RRF через SQL) | ❌ | ✅ Полный SQL | ❌ | ✅ HNSW на диске | 16 000 хранение; 2 000 индексирование vector | Уже на Postgres; умеренное количество векторов |
| Qdrant | Self-host / Cloud | Apache 2.0 | ✅ Нативный BM25 | ✅ Зрелая поддержка | ❌ (REST/gRPC) | ❌ | ✅ | 65 535 | Фильтрованные запросы в масштабе; сложные метаданные |
| Weaviate | Self-host / Cloud | BSD 3 | ✅ Нативный BM25 + RRF | ✅ | ❌ (GraphQL / gRPC) | ✅ через модули | ✅ | 65 535 | Паттерны доступа GraphQL; встроенная векторизация |
| Pinecone | Только Cloud | Проприетарная | ✅ (добавлено в 2024) | ✅ | ❌ | ❌ | ✅ (serverless) | 20 000 | Простота управления; нет команды ops |
| Milvus / Zilliz | Self-host / Cloud (Zilliz) | Apache 2.0 | ✅ Нативный | ✅ | ✅ SQL-подобный (Milvus Query Language) | ✅ | ✅ DiskANN | 32 768 | Масштаб в миллиарды; enterprise on-prem |
| Chroma | Embedded / self-host | Apache 2.0 | ❌ | ❌ | ❌ | ❌ | ❌ | 65 535 | Только локальная разработка и прототипирование |
| LanceDB | Embedded / Cloud | Apache 2.0 | ✅ | ❌ | ✅ SQL через DataFusion | ✅ Нативная | ✅ (формат Lance) | Без ограничений | Edge / serverless; мультимодальное озеро данных |
| Orama | Embedded / Cloud | Apache 2.0 | ✅ Полнотекстовый + векторный | ❌ | ❌ | ❌ | ❌ | Зависит | JS/edge приложения; лёгкий поиск по сайту/приложению |
| Turbopuffer | Только Cloud (serverless) | Проприетарная | ✅ BM25 + векторный | ❌ | ❌ | ❌ | ✅ (объектное хранилище) | 16 000 | Multi-tenant SaaS; миллионы namespaces |
| Elasticsearch | Self-host / Elastic Cloud | SSPL / AGPLv3 | ✅ RRF + ELSER sparse | ✅ (ELSER) | ✅ Query DSL | ❌ | ✅ DiskBBQ | 4 096 | Уже на стеке Elastic; гибридный enterprise-поиск |
| OpenSearch | Self-host / AWS managed | Apache 2.0 | ✅ RRF + Neural Search | ✅ | ✅ Query DSL | ❌ | ✅ FAISS + HNSW | 16 000 | AWS-native; open-source альтернатива Elastic |
| Vespa | Self-host / Cloud | Apache 2.0 | ✅ Нативный | ✅ Тензоры / лексическое ранжирование | ✅ YQL | ✅ Тензоры | ✅ | Практически без ограничений | Поиск + ранжирование + рекомендательные системы |
| ClickHouse | Self-host / Cloud | Apache 2.0 | Вручную | ❌ | ✅ Полный SQL | ❌ | ✅ Columnar + HNSW | Зависит | Аналитика/логи с векторным поиском рядом с OLAP |
| MongoDB Atlas | Cloud / self-host | SSPL | ✅ Встроенный | ❌ | ✅ MQL + aggregation | ❌ | ✅ HNSW | 8 192 | Уже на MongoDB; документы + векторы в одном |
| Redis (VSS) | Self-host / Redis Cloud | RSALv2 / SSPL | ✅ (RediSearch) | ✅ | ❌ | ❌ | ❌ Только RAM | 32 768 | Ультра-низкая задержка; векторный поиск на уровне кэша |
| Marqo | Cloud / self-host | Apache 2.0 | ✅ | ❌ | ❌ | ✅ Нативный фокус | ✅ | Зависит | Сквозная мультимодальность: изображение + текст + видео |
Несколько вещей, не вошедших в таблицу
Мультитенантность Turbopuffer построена вокруг очень большого количества namespaces. Их публичное позиционирование и истории клиентов подчёркивают рабочие нагрузки с большим количеством tenants, например, крупный корпус Notion. Если каждому пользователю или организации нужна изолированная векторная похожесть, эта архитектура может изменить экономику, но всё равно бенчмаркайте свою собственную форму tenants.
Встроенный режим LanceDB — ближайшая вещь к «SQLite для векторного поиска». Работает в процессе, не требует сервера, работает в Lambda, Cloudflare Workers и edge-средах. Колончатый формат Lance делает встроенную работу практичной в реальном масштабе.
Chroma сильнее всего в dev/test и небольших приложениях. Если вы целитесь в очень крупные корпуса, HA, тяжёлую работу с диском или первоклассный гибридный поиск, оцените production-ориентированное хранилище перед продвижением прототипа в инфраструктуру.
Vespa — это то, к чему обращаются, когда извлечение — только половина продукта. Он объединяет лексическое извлечение, поиск ближайших соседей, тензоры, выражения ранжирования, группировку и онлайн-обслуживание. Эта мощь реальна, как и операционная и модельная сложность. Это подходит командам поиска/рекомендаций больше, чем «добавить семантический поиск в моё CRUD-приложение».
ClickHouse принадлежит к разговору, когда поиск прикреплён к аналитике. Если ваш источник истины — события, логи, трейсы или метрики, ClickHouse хранит векторное расстояние, фильтрацию, агрегацию и серьёзный полнотекстовый индекс в одном SQL-движке. Не специализированная векторная база данных, но часто скучно-правильный ответ для аналитического извлечения.
Редкие векторы — это способ получить качество сопоставления ключевых слов уровня BM25 внутри векторного индекса — без запуска отдельного полнотекстового движка. Qdrant и Elasticsearch имеют особенно зрелые реализации здесь. Если гибридный поиск критичен, а архитектура из двух систем — неприемлема, ищите поддержку редких векторов.
Выбор, когда вы выросли из pgvector
- SaaS-продукт с изоляцией на тенанта → Turbopuffer
- Сложная фильтрация по метаданным в масштабе → Qdrant
- Уже на стеке Elastic/ELK → Elasticsearch с DiskBBQ
- AWS-команда, которой нужен open-source → OpenSearch
- Платформа поиска/рекомендаций с серьёзными потребностями ранжирования → Vespa
- Аналитика, наблюдаемость, поиск по логам/событиям → ClickHouse
- Масштаб в миллиарды on-prem / self-hosted → Milvus
- Edge / serverless / мультимодальность → LanceDB
- Небольшое JS-приложение, сайт документации или edge-native поиск → Orama
- Ноль ops, стоимость вторична → Pinecone
- Мультимодальность в первую очередь (изображения, видео, аудио) → Marqo
- Уже на MongoDB → Atlas Vector Search
- Уже на Postgres, нужен больший запас → Supabase Vector или Neon (оба управляемые pgvector с лучшими инструментами)
Единственное, чего не стоит делать
Не используйте векторный поиск как нечёткий текстовый поиск для вещей, у которых есть правильные ответы.
«Найти пользователя с почтой dan@example.com» — это не задача векторного поиска. «Найти заказ с ID ORD-12345» — тоже. Создать эмбеддинг для ORD-12345 и искать по косинусной похожести вернёт что-то — но это может быть неправильно. У идентификатора есть правильный ответ. Приблизительное совпадение по идентификатору — это баг.
Векторный поиск возвращает наиболее похожую вещь в вашем датасете, даже когда ничего по-настоящему релевантного нет. Он не знает, когда хорошего ответа не существует. Это нормально для связанных документов. Это серьёзная проблема для точного поиска записей, где уверенный неправильный ответ хуже пустого результата.
То же самое относится и к другой стороне: не используйте FTS для запросов, где пользователь описывает концепцию. «Статьи о принятии трудных решений в условиях неопределённости» не содержит надёжных ключевых слов. FTS вернёт либо шум, либо ничего. Используйте правильный инструмент для формы запроса.
Полная картина
Большинству production-систем поиска нужно больше одного слоя:
pg_trgmдля имён, опечаток, автодополнения- FTS /
pg_searchдля поиска по ключевым словам в прозе - pgvector для семантических и концептуальных запросов
- RRF fusion для поверхностей, где пользователи смешивают типы запросов
- Обычные индексы для точных идентификаторов, фильтров и отсортированных списков
Это не конкурирующие инструменты. Они дополняют друг друга. Хорошо построенная поисковая система выбирает правильный слой для каждой формы запроса — а когда формы запросов перекрываются, она запускает несколько слоёв и сливает результаты.
Команды, которые успешно выпускают поисковые функции, понимают весь стек. Те, кто не понимает, берут векторную базу данных, встраивают всё подряд и удивляются, почему точный поиск иногда возвращает неправильную запись.