Фильтрованный поиск с использованием StructArray

Используйте эту страницу для добавления скалярной фильтрации к векторному поиску по полям StructArray. Фильтрация StructArray имеет два уровня: фильтры на уровне строк выбирают родительские сущности, а фильтры на уровне элементов ограничивают набор элементов Struct, участвующих в векторном поиске на уровне элементов.

На этой странице используется коллекция « tech_articles » из раздела «Создание поля StructArray». В коллекции имеется поле StructArray с именем « chunks », содержащее скалярные подполя, такие как « section », « page », « quality_score » и « has_code », а также векторные подполя для поиска.

Выберите тип фильтра

ЦельИспользованиеПоведение результата
Фильтрация по скалярному полю верхнего уровня, например category.Обычное выражение фильтра.Выбирает родительские сущности до или во время поиска.
Ограничивает векторный поиск на уровне элементов элементами Struct, соответствующими скалярным условиям.element_filter.Выполняет поиск только среди соответствующих элементов Struct и может возвращать смещения найденных элементов.
Выбор сущностей в зависимости от того, соответствуют ли предкату какие-либо, все или определённое количество элементов Struct.MATCH_ANY, MATCH_ALL, MATCH_LEAST, MATCH_MOST или MATCH_EXACT.Фильтрация на уровне строк. Сами по себе эти операторы не возвращают смещения.

На этой странице объясняется, как использовать фильтры StructArray в рабочих процессах поиска. Полные правила синтаксиса, поддерживаемые типы предикатов и матрица неподдерживаемых предикатов см. в разделе «Операторы StructArray».

Фильтрация по полям верхнего уровня

Используйте обычные выражения фильтрации, если условие относится к родительской сущности, а не к отдельному элементу Struct. Это работает как при поиске по EmbeddingList, так и при поиске на уровне элементов.

from pymilvus import MilvusClient
from pymilvus.client.embedding_list import EmbeddingList

client = MilvusClient(
    uri="http://localhost:19530",
    token="root:Milvus",
)

query = EmbeddingList()
query.add([0.12, 0.21, 0.32, 0.44])
query.add([0.18, 0.23, 0.29, 0.36])

results = client.search(
    collection_name="tech_articles",
    data=[query],
    anns_field="chunks[emb_list_vector]",
    filter='category == "search"',
    limit=3,
    output_fields=[
        "doc_id",
        "title",
        "category",
        "chunks[text]",
        "chunks[section]",
    ],
)

Приведенный выше фильтр выбирает только сущности, у которых поле верхнего уровня category имеет значение "search". Он не выделяет один конкретный соответствующий элемент Struct.

Используйте element_filter(structArrayField, predicate), когда скалярные условия должны применяться к тому же элементу Struct, который участвует в векторном поиске на уровне элементов. Внутри предиката используйте $[subfield] для ссылки на скалярные подполя текущего элемента Struct.

query_vector = [0.19, 0.24, 0.30, 0.37]

filter_expr = (
    'category == "search" && '
    'element_filter(chunks, '
    '$[section] == "index" && '
    '$[quality_score] > 0.9 && '
    '$[has_code] == true)'
)

results = client.search(
    collection_name="tech_articles",
    data=[query_vector],
    anns_field="chunks[emb]",
    filter=filter_expr,
    limit=5,
    output_fields=[
        "doc_id",
        "title",
        "chunks[text]",
        "chunks[section]",
        "chunks[page]",
        "chunks[quality_score]",
        "chunks[has_code]",
    ],
)

for hits in results:
    for hit in hits:
        print(
            "doc_id:", hit["id"],
            "distance:", hit["distance"],
            "offset:", hit.get("offset"),
            "entity:", hit["entity"],
        )

В данном примере предикат верхнего уровня category == "search" выбирает кандидатов, а element_filter ограничивает векторный поиск на уровне элементов фрагментами, в которых section, quality_score и has_code совпадают в одном и том же элементе Struct.

Предупреждение

При объединении предиката верхнего уровня с оператором « element_filter » помещайте оператор « element_filter » в конец выражения. Выражение фильтра может содержать только один оператор « element_filter », и нельзя вкладывать операторы « element_filter » или « MATCH_* » внутрь другого оператора «StructArray».

Фильтрация сущностей с помощью операторов MATCH

Используйте операторы MATCH_*, когда фильтр должен определять, соответствует ли родительская сущность критериям на основе элементов Struct. Эти операторы являются фильтрами на уровне строк: они выбирают сущности, но сами по себе не возвращают смещения элементов.

ОператорИспользуйте его, когдаПример
MATCH_ANYПо крайней мере один элемент Struct должен удовлетворять предикату.MATCH_ANY(chunks, $[section] == "index")
MATCH_ALLВсе элементы Struct должны удовлетворять предикату.MATCH_ALL(chunks, $[quality_score] > 0.5)
MATCH_LEASTПо крайней мере N элементов структуры должны удовлетворять предикату.MATCH_LEAST(chunks, $[has_code] == true, threshold=2)
MATCH_MOSTНе более чем N элементов структуры должны удовлетворять предикату.MATCH_MOST(chunks, $[section] == "appendix", threshold=1)
MATCH_EXACTИменно N элементов Struct должны удовлетворять предикату.MATCH_EXACT(chunks, $[section] == "summary", threshold=1)
filter_expr = (
    'category == "search" && '
    'MATCH_ANY(chunks, $[section] == "index" && $[quality_score] > 0.9)'
)

results = client.search(
    collection_name="tech_articles",
    data=[query],
    anns_field="chunks[emb_list_vector]",
    filter=filter_expr,
    limit=3,
    output_fields=[
        "doc_id",
        "title",
        "category",
        "chunks[text]",
        "chunks[section]",
        "chunks[quality_score]",
    ],
)

Здесь используется « MATCH_ANY », поскольку результат поиска EmbeddingList находится на уровне сущности. Фильтр требует, чтобы по крайней мере один фрагмент в сущности был фрагментом « "index" » с высоким качеством, но сам результат поиска по-прежнему представляет родительскую сущность.

В гибридном поиске применяйте фильтры StructArray там, где условие должно вступать в силу. Фильтр верхнего уровня может использоваться во всем гибридном поиске. Фильтр « element_filter » следует привязывать к запросу на уровне элементов StructArray, который требует ограничений на уровне элементов.

from pymilvus import AnnSearchRequest, RRFRanker

query_vector = [0.19, 0.24, 0.30, 0.37]

title_req = AnnSearchRequest(
    data=[query_vector],
    anns_field="title_vector",
    limit=10,
)

chunk_req = AnnSearchRequest(
    data=[query_vector],
    anns_field="chunks[emb]",
    limit=10,
    expr='element_filter(chunks, $[section] == "index" && $[quality_score] > 0.9)',
)

results = client.hybrid_search(
    collection_name="tech_articles",
    reqs=[title_req, chunk_req],
    ranker=RRFRanker(),
    filter='category == "search"',
    limit=5,
    output_fields=[
        "doc_id",
        "title",
        "category",
        "chunks[text]",
        "chunks[section]",
        "chunks[quality_score]",
    ],
)

Аргумент « filter » применяет условие сущности верхнего уровня, тогда как « expr » в « chunk_req » ограничивает только запрос вектора на уровне элементов StructArray. Поддерживаемые комбинации гибридного поиска и ограничения для конкретных версий см. в разделах «Гибридный поиск с StructArray » и «Ограничения StructArray».

Сводка по поддержке предикатов

Используйте скалярные подполя в предикатах StructArray. Векторные подполя не являются входными данными для скалярных предикатов.

Тип подполяТипичные примеры предикатов
BOOL$[has_code] == true, !($[has_code] == true)
Целые типы$[page] >= 2, $[page] in [1, 2, 3]
FLOAT, DOUBLE$[quality_score] > 0.9, 0.7 < $[quality_score] < 0.95
VARCHAR$[section] == "index", $[text] like "range%"
Векторные подполяНе поддерживаются в качестве входных данных для скалярных предикатов $[...]. Вместо этого используйте векторные подполя с помощью векторного поиска.

В отношении неподдерживаемых случаев, таких как пути JSON, функции контейнеров массивов, функции сопоставления текста, предикаты null для $[...], функции Geometry, выражения Timestamptz и вызовы обобщённых функций, см. раздел «Операторы StructArray».

Распространённые ошибки

  • Использование ` $[subfield] ` вне контекста ` element_filter ` или ` MATCH_*`.

  • Использование chunks.section вместо синтаксиса операторов StructArray, например element_filter(chunks, $[section] == "index").

  • Использование element_filter, когда требуется только фильтрация на уровне строк. Если необходимо только выбрать сущности, используйте вместо этого MATCH_ANY.

  • Ожидание того, что оператор ` MATCH_* ` вернет смещения элементов. Эти операторы выбирают сущности и сами по себе не идентифицируют один соответствующий элемент.

  • Написание простых булевых предикатов, таких как $[has_code]. Используйте явные сравнения, такие как $[has_code] == true.

  • Размещение element_filter перед предикатом верхнего уровня в одном и том же выражении фильтра.

Следующие шаги

  1. Чтобы ознакомиться с полным синтаксисом фильтров StructArray, прочтите раздел «Операторы StructArray».

  2. Чтобы сначала выполнить поиск по векторам без фильтрации, ознакомьтесь с разделом «Базовый поиск по векторам с помощью StructArray».

  3. Чтобы создать скалярные индексы для часто используемых фильтров StructArray, ознакомьтесь с разделом «Индексирование полей StructArray».

  4. Чтобы ознакомиться с ограничениями на фильтрацию и поиск для конкретных версий, ознакомьтесь с разделом «Ограничения StructArray».