中文翻译暂不可用,显示俄语原文。

Как вы фильтруете документы по метаданным в векторной БД?

Краткий тезис

Фильтрация по метаданным (metadata filtering) — это возможность искать документы не только по семантической близости (поиск|векторный поиск), но и по атрибутам: дата, автор, тип документа, категория, источник. В векторных БД есть два подхода: pre-filtering (сначала фильтр, потом поиск) и post-filtering (сначала поиск, потом фильтр). Pre-filtering точнее, но может не найти ничего, если отфильтровать слишком много документов. Post-filtering быстрее, но может потерять релевантные документы, не попавшие в top-k. Лучшие практики: создавать индексы на часто фильтруемых полях и выбирать подход в зависимости от селективности фильтра.

Ключевая идея поиск|Векторный поиск сам по себе не учитывает метаданные. Фильтрация — это отдельный этап, который добавляется до или после ANN поиска.


1. Термин: Метаданные (Metadata)

Что это Данные о данных. Не сам текст документа, а информация о нём.

Примеры метаданных для документа

{
  "text": "Содержимое документа...",
  "metadata": {
    "source": "s3://bucket/policy.pdf",
    "doc_type": "pdf",
    "author": "legal_department",
    "created_date": "2025-01-01",
    "category": "legal",
    "department": "finance",
    "is_confidential": true,
    "version": 3
  }
}

Термин «Селективность» (Selectivity Доля документов, проходящих фильтр. Если фильтр оставляет 1% документов — высокая селективность. Если 80% — низкая.


2. Pre-filtering (сначала фильтр, потом поиск)

Что это Сначала применяем фильтр по метаданным (оставляем только документы, удовлетворяющие условию), затем выполняем поиск|векторный поиск только по отфильтрованному подмножеству.

Запрос пользователя: "финансовые отчеты за 2025 год про прибыль"

Шаг 1 (фильтр по метаданным):
  - doc_type = 'financial'
  - year = 2025
  → Оставляем только 1000 документов из 1 млн

Шаг 2 (векторный поиск):
  - Ищем по вектору "про прибыль" среди этих 1000 документов
  → Возвращаем top-10

Плюсы

  • Точность: все найденные документы точно соответствуют фильтру
  • Нет риска потерять документы из-за top-k (поиск идёт по всем отфильтрованным)

Минусы

  • Если фильтр слишком строгий (оставил 0 документов) → пустой результат
  • Медленнее при очень селективных фильтрах (фильтр оставляет мало документов — поиск всё равно должен просмотреть их все)

Термин «Фильтр высокой селективности» Фильтр, который оставляет очень мало документов (например, doc_type='rare_type'). Pre-filtering эффективен, так как поиск идёт по маленькому множеству.

Термин «Фильтр низкой селективности» Фильтр, который оставляет много документов (например, year=2025 — 50% всех документов). Pre-filtering не даёт большого выигрыша.


3. Post-filtering (сначала поиск, потом фильтр)

Что это Сначала выполняем векторный поиск по всей БД, находим top-k документов (например, 100), затем применяем фильтр к этим 100, оставляя только те, которые соответствуют условию.

Запрос пользователя: "финансовые отчеты за 2025 год про прибыль"

Шаг 1 (векторный поиск):
  - Ищем по всей БД (1 млн документов) top-100 по семантической близости
  → Находим 100 документов (могут быть любых типов и годов)

Шаг 2 (фильтр):
  - Оставляем только те, у которых doc_type='financial' AND year=2025
  → Может остаться 0, 5 или 50 документов

Плюсы

  • Быстрый (векторный поиск по всей БД, фильтр только на top-k)
  • Гарантированно возвращает top-k по семантике (но после фильтра их может стать меньше)

Минусы

  • Риск если релевантные документы не попали в top-k по семантике, они будут отфильтрованы
  • Пример: запрос про прибыль (семантически близок к финансовым отчётам), но в top-100 попали только технические документы (потому что в них слово «прибыль» встречается редко, но есть другие совпадения). Финансовые отчёты на позициях 101-200 — потеряны.

Когда использовать post-filtering

  • Фильтр низкой селективности (большая часть документов подходит)
  • Скорость критична
  • Потери релевантных документов допустимы

4. Pre-filtering vs Post-filtering: сравнение

ХарактеристикаPre-filteringPost-filtering
ПорядокФильтр → векторный поискВекторный поиск → фильтр
Точность (recall100% (все подходящие под фильтр)Риск потери (если релевантные не в top-k)
Скорость при селективном фильтре (<10%)Быстро (мало документов для поиска)Быстро (векторный поиск по всей БД)
Скорость при неселективном фильтре (>50%Медленно (почти все документы)Быстро (фильтр только top-k)
Риск пустого результатаЕсли фильтр слишком строгийЕсли релевантные не в top-k
Когда использоватьСелективные фильтры (<10%)Неселективные фильтры (>50%)

Термин «Recall» (в контексте фильтрации Доля релевантных документов (подходящих под фильтр), которые вернулись в результате.


5. Реализация в популярных векторных БД

5.1 Qdrant (рекомендуется)

Qdrant поддерживает pre-filtering с богатым синтаксисом условий.

from qdrant_client import QdrantClient
from qdrant_client.http.models import Filter, FieldCondition, Range, Match

client = QdrantClient(host="localhost", port=6333)

# Фильтр: doc_type = 'financial' И year >= 2024 И year <= 2025
filter_condition = Filter(
    must=[
        FieldCondition(key="doc_type", match=Match(value="financial")),
        FieldCondition(key="year", range=Range(gte=2024, lte=2025))
    ]
)

# Pre-filtering поиск
results = client.search(
    collection_name="documents",
    query_vector=query_embedding,
    query_filter=filter_condition,  # сначала фильтр, потом поиск
    limit=10
)

Операторы Qdrant

ОператорЧто делаетПример
mustВсе условия должны выполняться (AND)doc_type='financial' AND year=2025
shouldХотя бы одно условие (OR)doc_type='financial' OR doc_type='legal'
must_notУсловие не должно выполняться (NOT)NOT doc_type='draft'

5.2 Weaviate

Weaviate поддерживает pre-filtering через where фильтр.

import weaviate

client = weaviate.Client("http://localhost:8080")

# where фильтр: doc_type = 'financial'
where_filter = {
    "path": ["doc_type"],
    "operator": "Equal",
    "valueString": "financial"
}

result = client.query.get(
    "Document", ["text", "doc_type", "year"]
).with_near_vector({
    "vector": query_embedding
}).with_where(where_filter).with_limit(10).do()

5.3 Pinecone

Pinecone поддерживает post-filtering (после поиска).

import pinecone

pinecone.init(api_key="...")
index = pinecone.Index("documents")

# Поиск без фильтра (векторный)
results = index.query(
    vector=query_embedding,
    top_k=100,
    include_metadata=True
)

# Post-filtering вручную
filtered_results = [
    r for r in results['matches']
    if r['metadata']['doc_type'] == 'financial'
][:10]  # оставляем топ-10 после фильтрации

Важно Pinecone не поддерживает pre-filtering. Если вам нужен pre-filtering, рассмотрите Qdrant или Weaviate.


6. Оптимизация: индексы для часто фильтруемых полей

Проблема Фильтрация по метаданным без индекса — это линейное сканирование всех документов (O(n)). Для 1 млн документов — медленно.

Решение Создавать индексы (как в обычных БД) для полей, по которым часто фильтруют.

Термин «Индекс» (Index) в контексте метаданных Структура данных (B-tree, hash), ускоряющая поиск по значениям поля.

Какие поля индексировать

ПолеТип индексаПочему
doc_typeHash (enum)Мало уникальных значений (pdf, docx, xlsx)
yearB-tree (range)Фильтрация по диапазонам (year >= 2024)
author_idHashТочное совпадение
timestampB-treeСортировка и диапазоны
categoryHashКатегоризация

Qdrant: индексация метаданных

from qdrant_client.http.models import PayloadSchemaType

client.create_payload_index(
    collection_name="documents",
    field_name="doc_type",
    field_schema=PayloadSchemaType.KEYWORD  # hash-индекс
)

client.create_payload_index(
    collection_name="documents",
    field_name="year",
    field_schema=PayloadSchemaType.INTEGER   # B-tree индекс
)

Результат Время фильтрации падает с O(n) до O(log n).


7. Гибридный подход (лучшая практика)

Идея Использовать двухэтапную фильтрацию для баланса скорости и точности.

def hybrid_filter(query_embedding, strict_filter, top_k=10):
    # Шаг 1: Pre-filtering с широкими условиями (нестрогий фильтр)
    loose_filter = Filter(
        should=[
            FieldCondition(key="doc_type", match=Match(value="financial")),
            FieldCondition(key="doc_type", match=Match(value="legal"))
        ]
    )
    candidates = client.search(
        query_vector=query_embedding,
        query_filter=loose_filter,
        limit=100  # больше, чем нужно
    )
    
    # Шаг 2: Post-filtering с точными условиями
    exact_filtered = [
        r for r in candidates
        if r.payload['year'] >= 2024 and r.payload['year'] <= 2025
    ]
    
    return exact_filtered[:top_k]

Преимущества

  • Шаг 1 (loose filter) отсекает явно нерелевантные типы документов
  • Шаг 2 (exact filter) применяется только к top-100, быстро
  • Риск потери релевантных документов минимален (мы взяли top-100, а не top-10)

8. Когда использовать фильтрацию (use cases)

Use caseФильтр поПочему
Юридический RAGdoc_type='contract', year>=2020Клиент хочет договоры только за последние 5 лет
Корпоративный поискdepartment='finance', is_confidential=falseСотрудник из финансов видит только свои документы
Медицинский RAGpatient_id='...', doc_type='lab_result'Врач видит только лабораторные результаты конкретного пациента
E-commercecategory='electronics', price <= 50000Поиск товаров только в категории электроника
Новостной поискsource='reuters', date>='2025-01-01'Новости только от Reuters за последний месяц

Термин «Multi-tenant RAG» Система, обслуживающая多个 клиентов. Фильтрация по tenant_id обязательна для изоляции данных.


9. Пет-проект для закрепления

Задача Реализовать поиск с фильтрацией по метаданным в Qdrant и сравнить pre-filtering vs post-filtering.

Инструменты Python, Qdrant (Docker), Faker (для синтетических данных)

Шаги

  1. Сгенерировать 100 000 документов со случайными метаданными:
    • doc_type: "financial", "legal", "technical", "marketing"
    • year: 2020-2025
    • author: "alice", "bob", "charlie"
  2. Загрузить в Qdrant, создать индексы на doc_type и year
  3. Реализовать три подхода:
  4. Для 100 запросов замерить:
    • Latency (среднее, p95)
    • Recall (доля релевантных документов в результате)
  5. Проанализировать:
    • При каком фильтре pre-filtering быстрее?
    • При каком post-filtering теряет релевантные документы?
    • Гибридный подход даёт лучший trade-off?

Ожидаемый результат Вы поймёте, когда какой подход выбирать.


Связь с другими вопросами

ВопросТема
1RAG архитектура (retrieval с фильтрацией)
4Выбор векторной БД (Qdrant vs Pinecone для фильтрации)
5Оценка retrieval (recall может падать при post-filtering)
6Гибридный поиск (можно комбинировать с фильтрацией)
84Multi-tenant RAG (фильтрация по tenant_id обязательна)
84Collection per tenant vs metadata filtering

12. Как вы фильтруете документы по метаданным в векторной БД|12. Как вы фильтруете документы по метаданным в векторной БД|12. Как вы фильтруете документы по метаданным в векторной БД|12 полностью разобран. Переходим к вопросу 13, когда будете готовы|Вопрос 12 полностью разобран. Переходим к вопросу 13, когда будете готовы]]


Навигация