Qdrant: фильтрация поиска по метаданным и проверка выдачи
На странице · 4 разделов
Qdrant принимает условия на поля payload в `filter` запросов Query API: `must` объединяет условия по И, `should` — по ИЛИ, `must_not` исключает совпадения. Для повторяемого теста создайте отдельную коллекцию с синтетическими точками разных меток и проверьте как допустимую, так и запрещённую выдачу. Фильтр поиска не заменяет аутентификацию, авторизацию приложения и тест прав на документ.
Проверено по документации 6 октября 2026 года. Команды ниже — воспроизводимый синтетический протокол для читателя, а не результат запуска Modelium. Точная версия Qdrant в этом тексте не закреплена: API и документация сохранены с SHA-256; на вашей установленной версии проверьте совместимость перед использованием. s1 s3
Когда метаданные полезнее одного векторного сходства?
Предположим, локальный поиск по документам нашёл похожие фрагменты из разных подразделений. У точки Qdrant можно хранить tenant_id, doc_type, идентификатор документа и текст в payload, а Query API ограничит кандидатов до сравнения результата по условиям filter. Это полезно для маршрута «запрос → embedding → Qdrant → разрешённые кандидаты → ответ локальной модели»; сам факт похожести векторов не означает, что документ разрешено показывать пользователю. s1 s3 s6 s4
must означает совпадение всех условий, should — хотя бы одного, must_not — исключение перечисленных совпадений. Для метки tenant_id и типа doc_type используйте match.value по заранее нормализованному keyword, а не полнотекстовое условие. В демонстрации must ограничивает поиск одновременно двумя метками. Одна перестановка цвета или текста в payload не меняет векторную семантику. s1
Как подготовить безопасный синтетический набор?
Выполните первый запуск Qdrant на изолированном локальном экземпляре с портом 6333, доступным только на loopback; не используйте реальные документы. Self-hosted Qdrant по умолчанию доступен без аутентификации, если не настроить защиту: связывание порта с 127.0.0.1 в демонстрации не делает общий сервер безопасным. Перед рабочей эксплуатацией нужны сетевые ограничения, ключи и TLS, а также собственная политика доступа приложения. s8 s4
Создайте новую коллекцию demo_filter_docs, чтобы не задеть прежнюю demo_docs: curl -fsS -X PUT 'http://127.0.0.1:6333/collections/demo_filter_docs' -H 'Content-Type: application/json' -d '{"vectors":{"size":3,"distance":"Cosine"}}'. Размерность 3 — только для искусственных координат ниже: не переносите её на embedding-модель. Сверьте status ответа прежде, чем писать точки. s8
До загрузки точек создайте два keyword payload-индекса: curl -fsS -X PUT 'http://127.0.0.1:6333/collections/demo_filter_docs/index?wait=true' -H 'Content-Type: application/json' -d '{"field_name":"tenant_id","field_schema":"keyword"}'; второй вызов с {"field_name":"doc_type","field_schema":"keyword"} для того же URL. Индекс ускоряет фильтр и помогает оценивать число кандидатов, но расходует ресурсы; на большом наборе индексируйте используемые поля, а не весь payload. Если коллекция уже заполнена, рекомендации Qdrant об HNSW после позднего создания индекса следует проверить отдельно. s2 s7
Запишите три точки одной операцией: curl -fsS -X PUT 'http://127.0.0.1:6333/collections/demo_filter_docs/points?wait=true' -H 'Content-Type: application/json' -d '{"points":[{"id":1,"vector":[1,0,0],"payload":{"tenant_id":"alpha","doc_type":"invoice","doc_id":"a1","text":"Тестовый счёт A"}},{"id":2,"vector":[1,0,0],"payload":{"tenant_id":"beta","doc_type":"invoice","doc_id":"b1","text":"Тестовый счёт B"}},{"id":3,"vector":[1,0,0],"payload":{"tenant_id":"alpha","doc_type":"memo","doc_id":"a2","text":"Тестовая заметка A"}}]}'. Равные искусственные векторы намеренно исключают смысловую разницу; это проверка фильтра, не модели, ранжирования или качества RAG. Убедитесь, что upsert вернул успешный статус, затем запросите результат. s6 s3
Как проверить совпадение и отсутствие чужих точек?
Отправьте POST /collections/demo_filter_docs/points/query с телом {"query":[1,0,0],"filter":{"must":[{"key":"tenant_id","match":{"value":"alpha"}},{"key":"doc_type","match":{"value":"invoice"}}]},"limit":3,"with_payload":true}. Например, локальный вызов: curl -fsS -X POST 'http://127.0.0.1:6333/collections/demo_filter_docs/points/query' -H 'Content-Type: application/json' -d '{"query":[1,0,0],"filter":{"must":[{"key":"tenant_id","match":{"value":"alpha"}},{"key":"doc_type","match":{"value":"invoice"}}]},"limit":3,"with_payload":true}'. По логике сохранённых меток в свежей коллекции result.points должен содержать только id=1; id=2 — чужой tenant, id=3 — другой тип. Это ожидаемый вывод из fixture, а не измеренный результат Modelium. s1 s3 s6
Повторите тот же запрос с tenant_id=beta: должен появиться только id=2. С tenant_id=gamma ожидается пустой result.points; пустой ответ полезен как отрицательный контроль, но не доказывает, что злоумышленник не может отправить запрос без фильтра. Проверьте именно клиентское ограничение на маршрут и права по документу отдельно. Если payload отсутствует, убедитесь, что установлен with_payload:true — по умолчанию API его не возвращает. Не делайте вывод о порядке точек с равными векторами. s1 s3 s4
Что меняется на реальных документах и больших коллекциях?
При реальной индексации нужен один выбранный способ получать embeddings как при записи, так и при запросе. Для self-hosted Qdrant документация рекомендует клиентский путь, например FastEmbed; это не универсальное утверждение «Qdrant не создаёт embeddings»: Cluster BM25 формирует sparse embeddings, Cloud Inference предлагает управляемые модели, а внешний провайдер — отдельный облачный путь. Для закрытого контура проверьте фактические сетевые обращения выбранного стека. Данные из демонстрации с трёхмерным вектором не подтверждают пригодность модели для русских документов. s5 s8
Нельзя считать tenant_id в payload самодостаточным разграничением прав: если клиент может передать другой tenant или снять filter, он получит иной набор точек при доступном API. Вывод: сервер приложения должен устанавливать условия из проверенной личности, ограничить прямой доступ к Qdrant и проверять документные ACL при выдаче и после изменения прав. Qdrant описывает API-key, read-only и granular collection keys, но метка документа и права внутри общего tenant — отдельная задача приложения. Пройдите проверку корпоративного поиска на двух ролях и после отзыва доступа. s1 s4
Если поле — массив объектов, два независимых key с [] могут совпасть на разных элементах одного массива. Для условия, которое должно выполниться на одном объекте, используйте nested с вложенным filter — это отдельно иллюстрирует документация Qdrant. Для незнакомого поля или нулевой выдачи проверьте написание и тип payload, настройки keyword-индекса, статус upsert и исходную коллекцию; не подменяйте отсутствие результатов «плохими embeddings» до такой проверки. s1 s2 s6
Не удаляйте рабочие коллекции ради демонстрации. Когда тест закончился и имя сверено, удалите только созданную здесь demo_filter_docs: curl -fsS -X DELETE 'http://127.0.0.1:6333/collections/demo_filter_docs'. Оценить полезность фильтра на ваших данных поможет протокол качества RAG: фиксируйте версию модели, доступное железо, способ векторизации, набор разрешённых и запрещённых запросов и фактические ошибки выдачи. Это следующий шаг читателя, не отчёт об эксперименте Modelium. s1 s3 s4
Источники
- Qdrant — Filtering: clauses, match и nested — Снимок s1-filtering.html; SHA-256 в manifest.json.. Получено 06.10.2026.
- Qdrant — Indexing: payload index — Снимок s2-indexing.html; SHA-256 в manifest.json.. Получено 06.10.2026.
- Qdrant REST API — Query points — Снимок s3-query-api.html; SHA-256 в manifest.json.. Получено 06.10.2026.
- Qdrant — Security & Access Control — Снимок s4-security.html; SHA-256 в manifest.json.. Получено 06.10.2026.
- Qdrant — Inference: local, BM25, Cloud — Снимок s5-inference.html; SHA-256 в manifest.json.. Получено 06.10.2026.
- Qdrant REST API — Upsert points — Снимок s6-upsert.html; SHA-256 в manifest.json.. Получено 06.10.2026.
- Qdrant REST API — Create payload index — Снимок s7-index-api.html; SHA-256 в manifest.json.. Получено 06.10.2026.
- Qdrant — Local Quickstart — Снимок s8-quickstart.html; SHA-256 в manifest.json.. Получено 06.10.2026.