English translation is not available yet. Showing Russian content.
Что такое Filtered ANN Search и как оно реализовано в Qdrant vs Weaviate?
Краткий тезис
Filtered ANN Search — это поиск приблизительных ближайших соседей с учётом дополнительных фильтров (по метаданным, числовым полям, тегам). Реализация в Qdrant и Weaviate принципиально различается: Qdrant использует pre-filtering (сначала применяет фильтр, затем ANN на отфильтрованном подмножестве), а Weaviate — post-filtering (сначала ANN, затем фильтрует результаты). Выбор стратегии критически влияет на производительность и точность в зависимости от селективности фильтра.
1. Термин: Filtered ANN Search
ANN (Approximate Nearest Neighbor) — метод поиска векторов, который находит приблизительно ближайшие соседи к запросу, жертвуя точностью ради скорости. Filtered ANN Search добавляет к этому поиску ограничения по невекторным полям (например, «только документы за 2024 год» или «категория = 'наука'»). Без фильтрации ANN ищет по всему пространству векторов; с фильтром нужно учитывать только те векторы, которые удовлетворяют условию.
Проблема: ANN-индексы (например, HNSW) не хранят метаданные напрямую, поэтому фильтрация либо применяется до, либо после поиска. Два основных подхода:
- Pre-filtering (фильтрация до ANN)
- Post-filtering (фильтрация после ANN)
2. Pre-filtering vs Post-filtering: общая концепция
| Характеристика | Pre-filtering | Post-filtering |
|---|---|---|
| Порядок операций | Сначала фильтр по метаданным → затем ANN на отфильтрованном наборе | Сначала ANN на всём индексе → затем фильтр по метаданным |
| Размер поискового пространства для ANN | Маленькое (только точки, прошедшие фильтр) | Всё (весь индекс) |
| Скорость при селективном фильтре | Высокая (ANN работает на малом подмножестве) | Низкая (ANN сканирует много лишних точек) |
| Скорость при неселективном фильтре | Может быть низкой (фильтр отсекает мало, но overhead фильтрации есть) | Высокая (ANN работает на всём индексе, фильтр только в конце) |
| Точность | Высокая (ANN видит только релевантные точки) | Может страдать, если ANN не нашёл нужные точки из-за приближения |
| Overhead | Нужен быстрый доступ к метаданным для фильтрации | Нужно хранить метаданные для каждого вектора |
Селективность фильтра — доля данных, проходящих через фильтр. Если фильтр пропускает <10% данных — он селективный; если >50% — неселективный.
3. Реализация в Qdrant: Pre-filtering
Qdrant по умолчанию использует pre-filtering. Процесс:
- Пользователь отправляет запрос с вектором и фильтром (например, must/filter conditions|should/filter conditions|must_not по скалярным полям).
- Qdrant сначала применяет фильтр к метаданным, получая список ID точек, удовлетворяющих условию.
- Затем запускает ANN-поиск (на основе HNSW) только по этим ID. Для этого Qdrant использует специальный механизм — oversampling (поиск большего числа кандидатов, чем нужно, чтобы компенсировать возможные потери из-за фильтрации) или прямой поиск по отфильтрованному подмножеству.
Ключевые особенности Qdrant:
- Фильтры могут быть сложными (вложенные логические условия, диапазоны, теги).
- Для ускорения фильтрации Qdrant хранит метаданные в payload index (индекс по полям, например, B-tree для чисел, inverted index для строк).
- При очень селективных фильтрах (например, 1% данных) pre-filtering даёт огромный выигрыш в скорости, так как ANN работает на малом наборе.
Пример кода (Python):
from qdrant_client import QdrantClient
from qdrant_client.http.models import Filter, FieldCondition, Range
client = QdrantClient(host="localhost", port=6333)
# Фильтр: год >= 2023
filter_condition = Filter(
must=[
FieldCondition(
key="year",
range=Range(gte=2023)
)
]
)
# Поиск с pre-filtering
results = client.search(
collection_name="my_collection",
query_vector=[0.1, 0.2, 0.3],
query_filter=filter_condition,
limit=10
)
4. Реализация в Weaviate: Post-filtering
Weaviate по умолчанию использует post-filtering. Процесс:
- Пользователь отправляет запрос с вектором и фильтром (через
whereclause). - Weaviate сначала выполняет ANN-поиск (на основе HNSW) по всему индексу, получая, например, 100 ближайших соседей (параметр certainty или
distance). - Затем применяет фильтр к этим 100 результатам, оставляя только те, которые удовлетворяют условию.
- Если после фильтрации осталось меньше, чем limit, Weaviate может повторить поиск с большим числом кандидатов (механизм autocut).
Ключевые особенности Weaviate:
- Фильтр применяется только к результатам ANN, а не ко всему индексу.
- При селективных фильтрах (например, 1% данных) может потребоваться много кандидатов, чтобы после фильтрации осталось нужное количество. Это увеличивает latency.
- Weaviate поддерживает hybrid search (векторный + keyword), где фильтр может применяться на разных этапах.
Пример кода (Python):
import weaviate
client = weaviate.Client("http://localhost:8080")
# Фильтр: year >= 2023
where_filter = {
"path": ["year"],
"operator": "GreaterThanEqual",
"valueNumber": 2023
}
# Поиск с post-filtering
results = client.query.get(
"Document",
["title", "year"]
).with_near_vector({
"vector": [0.1, 0.2, 0.3]
}).with_where(where_filter).with_limit(10).do()
5. Сравнительная таблица Qdrant vs Weaviate для Filtered ANN
| Критерий | Qdrant (pre-filtering) | Weaviate (post-filtering) |
|---|---|---|
| Стратегия фильтрации | Pre-filter | Post-filter |
| Механизм ANN | HNSW | HNSW |
| Производительность при селективном фильтре (<10%) | Высокая (ANN на малом подмножестве) | Низкая (нужно много кандидатов) |
| Производительность при неселективном фильтре (>50%) | Средняя (overhead фильтрации) | Высокая (ANN на всём индексе, фильтр лёгкий) |
| Точность при селективном фильтре | Высокая (ANN не теряет релевантные точки) | Средняя (ANN может пропустить точки, которые не попали в top-k кандидатов) |
| Сложность настройки | Нужно следить за размером payload index | Проще, но может потребоваться увеличение ef (параметр HNSW) |
| Поддержка сложных фильтров | Да (вложенные логические, гео, текст) | Да (where clause) |
| Типичный use case | RAG с жёсткими фильтрами по дате/категории | Полнотекстовый поиск с мягкими фильтрами |
6. Когда что выбирать
-
Qdrant (pre-filtering) лучше, когда:
- Фильтры селективные (<10% данных проходят).
- Фильтры сложные (много условий).
- Важна высокая точность при фильтрации (например, поиск только по документам определённого автора).
- Пример: RAG-система для юридических документов, где нужно искать только по делам за последний год.
-
Weaviate (post-filtering) лучше, когда:
- Фильтры неселективные (большая часть данных проходит).
- Фильтры простые (одно условие).
- Важна скорость при большом объёме данных без фильтрации.
- Пример: поиск изображений по сходству, где фильтр по категории применяется редко.
Компромисс: Некоторые системы (например, Milvus) поддерживают оба подхода и позволяют выбирать стратегию. Qdrant также имеет режим post-filtering (через параметр exact), но по умолчанию pre-filtering.
7. Пет-проект для закрепления
Задача: Сравнить производительность Filtered ANN Search в Qdrant и Weaviate на синтетическом датасете.
Инструменты: Python, Qdrant Client, Weaviate Client, numpy, time.
Шаги:
- Сгенерировать 100 000 векторов размерностью 128 и случайные метаданные (год от 2000 до 2024, категория из 10 вариантов).
- Загрузить данные в Qdrant и Weaviate (создать коллекции/классы, добавить векторы и метаданные).
- Для каждого из 100 тестовых запросов:
- Выполнить поиск с фильтром (селективным: год >= 2023, ~5% данных; неселективным: год >= 2000, ~100%).
- Замерить latency и количество найденных релевантных (по золотому стандарту) документов.
- Построить таблицу средних значений.
Ожидаемый результат:
- Для селективного фильтра Qdrant будет в 2-5 раз быстрее Weaviate.
- Для неселективного фильтра Weaviate может быть немного быстрее или наравне.
- Точность (recall) у Qdrant будет выше при селективном фильтре.
Код (фрагмент для Qdrant):
import time
from qdrant_client import QdrantClient
from qdrant_client.http.models import Filter, FieldCondition, Range
client = QdrantClient("localhost", 6333)
filter_cond = Filter(must=[FieldCondition(key="year", range=Range(gte=2023))])
start = time.time()
results = client.search("test_collection", query_vector=vec, query_filter=filter_cond, limit=10)
latency = time.time() - start
8. Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 225 | Что такое ANN и HNSW? |
| 227 | Сравнение Qdrant и Weaviate как векторных БД |
| 224 | Как работает HNSW-индекс? |
| 228 | Фильтрация в векторных БД: pre-filtering vs post-filtering |
| 230 | Оптимизация latency в RAG при фильтрации |
| 210 | Выбор векторной БД для RAG-системы |
9. Навигация
- Предыдущий: 225
- Следующий: 227
- Индекс: 00. Индекс разборов
Навигация
- Предыдущий: 225
- Следующий: 227
- Индекс: 00. Индекс разборов