Одна сущность, много векторов: поиск на уровне сущности и элемента с Milvus 3.0 StructArray
Большинство схем векторных баз данных начинаются с простого предположения: одна сущность — одно векторное представление (embedding). Продукт получает один вектор, как и документ. Пользовательский запрос преобразуется в вектор и сравнивается с этими векторами через поиск приблизительных ближайших соседей (ANN). Эта модель работает для первого поколения сценариев использования векторного поиска, включая RAG, семантический поиск и рекомендательные системы.
Однако реальные данные ИИ редко соответствуют этому предположению. Видео содержит клипы, кадры или ключевые кадры, каждый из которых имеет собственное векторное представление, временной диапазон, подпись, метку сцены и оценку уверенности. Продукт может иметь несколько изображений и углов обзора. Длинный документ содержит отрывки или разделы, локальное значение которых важнее, чем одно векторное представление всего документа. Популярные модели позднего взаимодействия (late-interaction) проявляют то же ограничение на ещё более мелком уровне: ColBERT создаёт один вектор на токен, а ColPali — один вектор на визуальный патч.
В каждом случае родительская сущность остаётся той единицей, которую приложение хранит, отображает, защищает и возвращает. Однако релевантность, фильтрация и объяснение результатов часто зависят от элементов внутри этой сущности.
Новая функция StructArray предоставляет Milvus нативную модель данных для такой структуры: одна сущность содержит упорядоченный массив элементов Struct, определённых схемой, и каждый элемент может содержать скалярные метаданные, векторные представления или и то и другое. Milvus может фильтровать поля, принадлежащие одному и тому же элементу, сравнивать два списка векторных представлений на уровне сущности или искать отдельные элементы и возвращать соответствующий смещение (offset).
В этой статье на примере поиска по видео объясняется модель данных, а затем рассматриваются проектирование схемы, фильтрация, гранулярности векторного поиска, стратегии индексации EmbeddingList, схлопывание результатов гибридного поиска и физическое расположение данных, которое делает эту функцию реализуемой.
Почему модели «один вектор» и «одна плоская строка» больше недостаточно
Рассмотрим пользователя, который ищет в видеокаталоге «человек нарезает овощи на кухне». Релевантный сигнал может находиться в одном восьмисекундном клипе, а не в векторном представлении всего видео. Сжатие каждого клипа, объекта и действия в один вектор может сохранить общую тему, но при этом смазать локальные детали.
Такое же несоответствие проявляется и в других сценариях:
- Релевантность продукта может определяться одним из нескольких изображений или ракурсов.
- Документ может совпадать благодаря одному отрывку, а не своей общей теме.
- Память агента может содержать несколько наблюдений, из которых только одно важно для текущей задачи.
- Запись ColBERT или ColPali содержит список векторов токенов или патчей переменной длины, а не один плотный вектор.
Одна из альтернатив — разделить каждый клип, изображение или отрывок на отдельную строку базы данных. Это обеспечивает локальный поиск, но также отделяет каждый фрагмент от его родительской сущности. Метаданные родительской сущности могут повторяться в строках, а поиск на уровне сущностей после этого требует группировки, дедупликации и повторного ранжирования после поиска по фрагментам.
Само по себе вложенное хранение не решает проблему запросов. JSON может хранить объекты, но не даёт Milvus предопределённую схему подполей для индексации векторов и скаляров. Параллельные массивы могут хранить подписи, метки сцен и значения уверенности, но приложение должно поддерживать выравнивание смещений. База данных не может безопасно определить, что scene_type[3] и label_confidence[3] описывают один и тот же клип, если эта связь не является частью модели данных.
StructArray кодирует эту связь напрямую. Он сохраняет локальные элементы внутри родительской сущности, одновременно предоставляя их выровненные подполя для проверки схемы, индексации, фильтрации и векторного поиска.
Что такое StructArray и его модель данных?
StructArray, также известный как массив структур, хранит упорядоченный набор элементов Struct в каждой сущности. Поле StructArray — это Array, все элементы которого соответствуют одной предопределённой схеме Struct. Для коллекции видео логическая структура может выглядеть так:
Plaintext
clips: ARRAY<STRUCT<
clip_embedding_list: FLOAT_VECTOR,
clip_embedding: FLOAT_VECTOR,
start_sec: DOUBLE,
end_sec: DOUBLE,
caption: VARCHAR,
scene_type: VARCHAR,
label_confidence: FLOAT
>>
Здесь:
clips— родительское поле StructArray.clip_embedding_list,clip_embedding,start_secи другие атрибуты — подполя.clips[0]— первый клип.- Каждое подполе со смещением
0принадлежит этому же клипу. - Каждое подполе со смещением
3принадлежит другому клипу.
Два векторных подполя служат для разных режимов поиска. clips[clip_embedding_list] индексируется с метрикой MAX_SIM* для поиска EmbeddingList на уровне сущностей, а clips[clip_embedding] индексируется с обычной векторной метрикой для поиска на уровне элементов. Поскольку векторное поле или векторное подполе принимает только один индекс, коллекция, которой нужны оба режима, должна определять и индексировать два подполя отдельно.
Эта модель поддерживает три различные семантики запросов.
1. Поиск EmbeddingList возвращает родительские сущности
Векторы в clips[clip_embedding_list] образуют один список векторных представлений для видео. Запрос также является EmbeddingList. Milvus сравнивает список запроса с каждым сохранённым списком, используя метрику MAX_SIM*, и возвращает результат на уровне сущности.
Plaintext
clips[clip_embedding_list] = [
embedding_0,
embedding_1,
embedding_2,
...
]
2. Семейство MATCH_* фильтрует родительские сущности
MATCH_ANY, MATCH_ALL, MATCH_LEAST, MATCH_MOST и MATCH_EXACT вычисляют предикат для элементов Struct, подсчитывают, сколько элементов удовлетворяют условию, и определяют, проходит ли родительская сущность фильтр.
Например:
Plaintext
MATCH_ANY(clips, $[scene_type] == "kitchen" && $[label_confidence] > 0.8)
Оба скалярных условия должны быть истинны для одного и того же смещения клипа. Milvus не комбинирует метку «кухня» из одного клипа с высоким значением уверенности из другого.
3. Поиск на уровне элементов возвращает смещение совпавшего элемента
Обычный вектор запроса может искать по всем векторам в clips[clip_embedding] независимо. Каждое совпадение идентифицирует родительскую сущность и смещение (с нуля) совпавшего элемента Struct. element_filter может ограничить, какие элементы участвуют в этом векторном поиске.
Эти операции объединяет одна предпосылка: Milvus знает, какие векторные и скалярные значения принадлежат одному элементу и какие элементы принадлежат одной сущности.
StructArray — это не универсальная система произвольной вложенности. Его текущая модель — один Array элементов Struct с поддерживаемыми скалярными и векторными подполями. Такая граница делает индексацию подполей и выполнение с учётом элементов выполнимыми на практике.
Создание схемы, индексов и пути вставки
Следующий упрощённый пример на PyMilvus создаёт коллекцию видео с одним векторным полем верхнего уровня и StructArray для клипов. В нём используются отдельные векторные подполя клипов, чтобы одна и та же коллекция могла демонстрировать оба режима поиска.
from pymilvus import DataType, MilvusClient
client = MilvusClient(uri=“http://localhost:19530”)
schema = client.create_schema(auto_id=False, enable_dynamic_field=False)
schema.add_field(“id”, DataType.INT64, is_primary=True)
schema.add_field(“title”, DataType.VARCHAR, max_length=512)
schema.add_field(“video_embedding”, DataType.FLOAT_VECTOR, dim=768)
# Define the Struct schema explicitly.
clip_schema = client.create_struct_field_schema()
clip_schema.add_field(“clip_embedding_list”, DataType.FLOAT_VECTOR, dim=768)
clip_schema.add_field(“clip_embedding”, DataType.FLOAT_VECTOR, dim=768)
clip_schema.add_field(“start_sec”, DataType.DOUBLE)
clip_schema.add_field(“end_sec”, DataType.DOUBLE)
clip_schema.add_field(“caption”, DataType.VARCHAR, max_length=2048)
clip_schema.add_field(“scene_type”, DataType.VARCHAR, max_length=128)
clip_schema.add_field(“label_confidence”, DataType.FLOAT)
schema.add_field(
“clips”,
datatype=DataType.ARRAY,
element_type=DataType.STRUCT,
struct_schema=clip_schema,
max_capacity=1024,
)
client.create_collection(“videos”, schema=schema)
Векторные подполя должны быть проиндексированы до поиска. Поскольку семейство метрик определяет режим поиска, каждое векторное подполе получает собственный индекс:
index_params = client.prepare_index_params()
# EmbeddingList search.
index_params.add_index(
field_name="clips[clip_embedding_list]",
index_type=“HNSW”,
metric_type=“MAX_SIM_COSINE”,
index_name=“clips_clip_embedding_list_maxsim_idx”,
params={“M”: 16, “efConstruction”: 200},
)
# Element-level search.
index_params.add_index(
field_name="clips[clip_embedding]",
index_type=“HNSW”,
metric_type=“COSINE”,
index_name=“clips_clip_embedding_cosine_idx”,
params={“M”: 16, “efConstruction”: 200},
)
client.create_index(“videos”, index_params=index_params)
Скалярные индексы необязательны, но подполя, которые часто используются в фильтрах при больших масштабах данных, должны использовать совместимый скалярный индекс. Например, clips[scene_type] может использовать инвертированный индекс, а числовое подполе, такое как clips[label_confidence], может использовать индекс, подходящий для числовой фильтрации.
Вставляйте данные в их естественной форме сущности: одна строка видео с массивом объектов клипов. Чтобы пример был компактным, в оба векторных подполя записывается один и тот же вектор клипа.
rows = [
{
"id": 1,
"title": "cooking tutorial",
"video_embedding": video_vec,
"clips": [
{
"clip_embedding_list": clip_vec_1,
"clip_embedding": clip_vec_1,
"start_sec": 0.0,
"end_sec": 8.0,
"caption": "A person washes vegetables.",
"scene_type": "kitchen",
"label_confidence": 0.92,
},
{
"clip_embedding_list": clip_vec_2,
"clip_embedding": clip_vec_2,
"start_sec": 8.0,
"end_sec": 16.0,
"caption": "A person cuts carrots on a board.",
"scene_type": "kitchen",
"label_confidence": 0.96,
},
],
}
]
client.insert(“videos”, rows)
client.flush(“videos”)
client.load_collection(“videos”)
На границе API clips остаётся массивом структурированных объектов. Внутри Milvus каждое подполе следует типизированному пути, требуемому для его собственного индекса, фильтра и поведения при выводе. Это различие прозрачно при вставке, но фундаментально для всего, что следует далее.
Фильтрация по одному элементу — это разница между структурой и параллельными массивами
Основное преимущество фильтрации — не более короткий синтаксис для вложенных полей. Это корректная корреляция между скалярными подполями.
Предположим, приложению нужны видео, содержащие кухонный клип с уверенностью метки выше 0.8. Недостаточно, чтобы видео содержало какой-то кухонный клип и какой-то клип с высокой уверенностью; один и тот же клип должен удовлетворять обоим условиям.
Семейство MATCH_* StructArray выражает это напрямую:
Plaintext
MATCH_ANY(clips, $[scene_type] == "kitchen" && $[label_confidence] > 0.8)
MATCH_ALL(clips, $[label_confidence] > 0.5)
MATCH_LEAST(clips, $[scene_type] == "sports", threshold=3)
MATCH_MOST(clips, $[label_confidence] < 0.2, threshold=1)
MATCH_EXACT(clips, $[scene_type] == "intro", threshold=1)
Milvus вычисляет предикат для каждого смещения элемента, а затем применяет квантор оператора, чтобы определить, проходит ли родительская сущность:
MATCH_ANY: Хотя бы один элемент совпадает.MATCH_ALL: Каждый элемент совпадает.MATCH_LEAST: Как минимумthresholdэлементов совпадает.MATCH_MOST: Не болееthresholdэлементов совпадает.MATCH_EXACT: Ровноthresholdэлементов совпадает.
Если бы те же данные хранились в виде двух независимых массивов, следующее выражение не сохранило бы эту корреляцию:
Plaintext
array_contains(clips[scene_type], "kitchen")
AND
array_contains(clips[label_confidence], 0.9)
Два значения могут находиться по разным смещениям. Это может быть допустимо для несвязанных атрибутов, но некорректно, когда оба условия описывают один и тот же клип, изображение продукта или отрывок документа.
StructArray делает идентичность элемента частью предиката базы данных, а не соглашением, которое приложение должно обеспечивать самостоятельно.
Две гранулярности векторного поиска, две идентичности результатов
Когда сущность хранит несколько векторов, поиск должен решить модельный вопрос до начала ANN-поиска:
Должны ли векторы оцениваться вместе как одно представление родительской сущности, или каждый вектор элемента должен конкурировать независимо?
StructArray поддерживает обе модели, но они используют разные формы запросов, семейства метрик, векторные подполя и идентичности результатов.
Поиск EmbeddingList: список векторов запроса находит сущность
Запрос EmbeddingList содержит несколько векторов. Видео запроса может быть разделено на несколько клипов; товарный запрос может содержать несколько эталонных изображений; запрос ColBERT содержит по одному вектору на токен запроса.
Для каждой сущности Milvus сравнивает список запроса с сохранённым списком векторных представлений сущности. При оценке в стиле MaxSim каждый вектор запроса выбирает своё наилучшее совпадение в списке сущности, и Milvus агрегирует эти оценки наилучших совпадений в оценку сущности. Итоговое совпадение представляет родительскую сущность, а не конкретный элемент Struct.
from pymilvus.client.embedding_list import EmbeddingList
query = EmbeddingList()
query.add(query_clip_vec_1)
query.add(query_clip_vec_2)
client.search(
collection_name=“videos”,
data=[query],
anns_field="clips[clip_embedding_list]",
search_params={“metric_type”: “MAX_SIM_COSINE”},
limit=10,
)
Этот поиск отвечает на вопрос: Какие видео являются наилучшим общим совпадением для этого набора клипов запроса?
Он подходит для поиска «видео-по-видео», мультиизображенческого поиска товаров, поиска в стиле ColBERT и ColPali и других случаев, когда и запрос, и сохранённая сущность представлены несколькими векторами.
Поиск на уровне элементов: один вектор запроса находит клип внутри сущности
Поиск на уровне элементов использует обычный вектор запроса. Каждый вектор в clips[clip_embedding] участвует в ANN-поиске как независимый кандидат. Каждое совпадение идентифицирует родительскую сущность и смещение совпавшего элемента.
client.search(
collection_name="videos",
data=[query_vec],
anns_field="clips[clip_embedding]",
search_params={"metric_type": "COSINE"},
limit=10,
output_fields=["id", "title", "clips"],
)
Чтобы искать только по выбранным клипам, добавьте element_filter, скалярные условия которого применяются к одному и тому же клипу:
client.search(
collection_name="videos",
data=[query_vec],
anns_field="clips[clip_embedding]",
search_params={"metric_type": "COSINE"},
filter='element_filter(clips, $[scene_type] == "kitchen" && $[label_confidence] > 0.8)',
limit=10,
output_fields=["id", "title", "clips"],
)
Фильтр не выбирает сначала кухонный клип, а затем ищет другой клип с высокой уверенностью. И предикаты, и векторный кандидат относятся к одному и тому же элементу Struct.
Несгруппированный ответ может выглядеть так:
Plaintext
id = 1, offset = 1, distance = 0.91
id = 8, offset = 4, distance = 0.88
id = 1, offset = 3, distance = 0.84
Одна и та же сущность может появляться несколько раз, потому что несколько клипов могут совпадать. Это полезно, когда приложению нужно показать не только то, какое видео или документ релевантно, но и какой клип или отрывок дал совпадение.
| Аспект | Поиск EmbeddingList | Поиск на уровне элементов |
|---|---|---|
| Входные данные запроса | Один или несколько векторов запроса в EmbeddingList | Один обычный вектор запроса |
| Пример цели | clips[clip_embedding_list] | clips[clip_embedding] |
| Семейство метрик | MAX_SIM* | Обычные метрики, такие как COSINE, IP или L2 |
| Единица ANN-кандидата | Список векторных представлений родительской сущности | Каждый вектор элемента Struct |
| Идентичность результата | Родительская сущность | Родительская сущность плюс смещение элемента |
| Типичный сценарий использования | Сопоставление многовекторного запроса с многовекторной сущностью | Поиск наиболее релевантного клипа, изображения, отрывка, патча или факта |
Чтобы поддерживать оба режима в одной коллекции, определите и индексируйте отдельные векторные подполя. Форма запроса, семейство метрик и целевой индекс должны совпадать.
Индексация EmbeddingList — это решение о качестве и стоимости
При одном векторном представлении на сущность ANN-индекс находит сущности, близкие к вектору запроса. Поиск EmbeddingList более затратен, поскольку релевантность зависит от попарных взаимодействий между двумя списками векторов.
Вычисление точного MaxSim для каждого вектора в каждой сущности даёт наиболее чистый эталонный рейтинг, но полное сканирование обычно слишком дорого для онлайн-поиска. Поэтому Milvus использует двухэтапную модель:
- Стратегия приближённого поиска извлекает кандидатов — родительские сущности.
- Когда
emb_list_rerankвключён, Milvus пересчитывает MaxSim по этим кандидатам для построения итогового рейтинга.
Извлечение большего числа кандидатов на первом этапе обычно повышает шанс, что истинные лучшие результаты достигнут повторного ранжировщика, но также увеличивает задержку и вычислительные затраты. Три стратегии различаются в основном тем, как они формируют этот набор кандидатов.
| Стратегия | Представление кандидатов на первом этапе | Хорошая отправная точка, когда | Основной компромисс |
|---|---|---|---|
| TokenANN | Индексирует каждый вектор в каждом списке векторных представлений. Векторы запроса выполняют ANN независимо; совпадения агрегируются обратно в родительские сущности перед повторным ранжированием MaxSim. | Качество является приоритетом, списки короткие или средние, а отдельные векторы обладают высокой различительной способностью. | Размер индекса и работа по поиску на первом этапе растут с длиной списка и количеством векторов запроса. |
| MUVERA | Кодирует каждый список векторных представлений в один вектор фиксированной размерности с помощью случайных проекций, затем выполняет обычный ANN. | TokenANN слишком тяжёл, и предпочтительно сжатие без конвейера обучения. | Кодирование теряет информацию; более сильные настройки проекций увеличивают размерность кодирования и стоимость ANN. |
| LEMUR | Обучает модель, которая отображает список векторных представлений в вектор родительской сущности фиксированной размерности. | Векторные представления менее различительны, списки велики, или рабочая нагрузка является визуальной или мультимодальной. | Требуется обучение, и модель может быть чувствительна к распределению корпуса и смещению длины документов. |
Ни одна стратегия не является лучшей для всех рабочих нагрузок. Начните с целевых данных и распределения запросов:
- Используйте TokenANN как базовый вариант, ориентированный на качество, когда размер набора данных позволяет.
- Попробуйте MUVERA, когда индекс TokenANN или извлечение кандидатов становятся слишком дорогими по мере роста длины списка, и вы хотите избежать конвейера обучения.
- Оцените LEMUR, когда пространство векторных представлений зашумлено или слабо различимо, или когда рабочая нагрузка является визуальной или мультимодальной.
- Измеряйте recall или nDCG наряду с задержкой и размером индекса. Стратегия, которая работает для коротких текстов, может вести себя иначе с длиннохвостыми длинами документов или тысячами визуальных патчей.
StructArray решает одну проблему: как представить выровненные, фильтруемые элементы с векторными представлениями внутри одной сущности. Стратегия EmbeddingList решает другую: как аппроксимировать MaxSim с приемлемой стоимостью для конкретной модели и корпуса.
Гибридный поиск делает идентичность результатов явной
Продакшн-поиск редко следует одному векторному пути. Видеозапрос может комбинировать векторное представление видео верхнего уровня, одно или несколько векторных представлений на уровне клипов, сигнал подписи или транскрипта и повторный ранжировщик.
Когда кандидаты на уровне элементов попадают в этот конвейер, движок должен решить, что идентифицирует итогового кандидата.
| Состав гибридного запроса | Область итогового кандидата | Идентичность результата |
|---|---|---|
| Все подзапросы являются запросами на уровне элементов и нацелены на векторные подполя в одном и том же StructArray | Уровень элемента | Первичный ключ плюс поле StructArray плюс смещение элемента |
| Включено векторное поле верхнего уровня | Уровень сущности | Первичный ключ |
| Включён запрос EmbeddingList | Уровень сущности | Первичный ключ |
| Запросы на уровне элементов нацелены на разные поля StructArray | Уровень сущности | Первичный ключ |
Первая конфигурация сохраняет идентичность элемента, поскольку смещение 3 относится к одному и тому же элементу Struct для каждого подзапроса в рамках данного родительского StructArray. Это подходит для приложения, которое хочет вернуть наиболее релевантный клип или отрывок после объединения нескольких сигналов на уровне элементов.
Остальные конфигурации смешивают гранулярности кандидатов или пространства имён элементов. Поэтому совпадение на уровне элемента должно быть схлопнуто в оценку на уровне сущности перед итоговым повторным ранжированием. Milvus поддерживает несколько стратегий схлопывания:
| Стратегия схлопывания | Оценка сущности из возвращённых совпадений элементов | Важное условие |
|---|---|---|
max | Лучшая оценка элемента | Работает с поддерживаемыми обычными векторными метриками |
sum | Сумма всех возвращённых оценок элементов | Используйте с метриками положительной корреляции, такими как IP или COSINE |
avg | Среднее возвращённых оценок элементов | Работает с поддерживаемыми обычными векторными метриками |
topk_sum | Сумма лучших K возвращённых оценок элементов | Требуется положительный topk; используйте с IP или COSINE |
topk_avg | Среднее лучших K возвращённых оценок элементов | Требуется положительный topk |
Схлопывание работает только с теми совпадениями элементов, которые возвращены данным ANN-подзапросом; оно не сканирует каждый элемент сущности после поиска. Поэтому limit запроса определяет, какие совпадения элементов доступны функции схлопывания.
Этот выбор формирует семантику поиска, а не только форматирование вывода. Если приложение показывает клип или отрывок, сохранение смещения при объединении естественно. Если оно показывает видео, продукт или документ, схлопывание на уровне сущности естественно. Когда сигналы работают на разных гранулярностях, системе нужно явное правило оценки «элемент-в-сущность».
StructArray переносит проблему идентичности и схлопывания из специальной постобработки в модель выполнения поиска.
Как Milvus выполняет StructArray, не рассматривая его как единый блок данных
Пользовательская модель — это ARRAY<STRUCT>. Однако хранение всего значения как одного непрозрачного блока сделало бы индексы подполей, фильтры и выборочный вывод неэффективными.
Milvus использует схему «логический родитель, физические дочерние колонки».
На уровне схемы clips — это логическое родительское поле. Оно определяет такие свойства, как схема Struct, максимальная ёмкость и допустимость NULL. Его подполя нормализуются в пути, такие как clips[clip_embedding_list], clips[clip_embedding], clips[scene_type] и clips[label_confidence].
Скалярные подполя следуют путям хранения скалярных массивов для каждой сущности, а векторные подполя — путям векторных массивов. Каждое подполе затем может использовать путь данных, соответствующий его типу: скалярную фильтрацию и скалярные индексы для метаданных, векторные индексы и ANN-поиск для векторных представлений.
При приёме данных Proxy разворачивает вложенный список Struct в типизированные дочерние колонки. Во время выполнения Milvus поддерживает связь между каждым физическим элементом и его родительской сущностью. Концептуально эта связь выглядит так:
Plaintext
entity 0 -> elements [0, 1, 2]
entity 1 -> elements [3]
entity 2 -> elements []
entity 3 -> elements [4, 5, 6, 7]
Когда поиск на уровне элементов возвращает физический идентификатор элемента, Milvus сопоставляет его обратно с родительской сущностью и смещением элемента. Когда element_filter создаёт битовую карту на уровне элементов, движок согласует её с видимостью родительской сущности, удалениями и другими фильтрами.
При возврате результатов Milvus использует логическую схему и общие смещения для восстановления формы StructArray, которую вставило приложение. Система может выполнять операции над типизированными дочерними колонками, пока пользователь продолжает читать и записывать естественные вложенные объекты. Такое физическое расположение делает StructArray чем-то большим, чем типизированный JSON: вложенная связь участвует в модели индексации и выполнения.
Где StructArray уместен, а где нет
StructArray отлично подходит, когда выполняются все следующие условия:
- У приложения есть значимая родительская сущность, такая как видео, продукт, документ, визуальная страница или запись памяти.
- Каждая родительская сущность содержит упорядоченный набор локальных элементов переменной длины.
- Этим элементам нужны собственные скалярные метаданные, векторные представления или и то и другое.
- Поиск или фильтрация должны сохранять связь между подполями при одном и том же смещении элемента.
- Приложению нужен многовекторный поиск на уровне сущностей, совпадения на уровне элементов или и то и другое.
StructArray не автоматически лучше для каждой коллекции. Короткому документу или простому запросу может вполне подойти одно плотное векторное представление. Многовекторная индексация добавляет затраты на хранение и поиск, поэтому дополнительное представление должно оправдывать своё место за счёт улучшенного качества поиска или более полезной гранулярности результатов.
Текущие границы схемы и выполнения также важны:
Structподдерживается как тип элементаArray, а не как поле коллекции верхнего уровня.- Все элементы в одном StructArray используют одну предопределённую схему.
max_capacityобязателен и ограничивает количество элементов на сущность.- Вложенные подполя
Struct,Array,ArrayOfStructиJSONне поддерживаются внутри StructArray. - Векторное подполе принимает один индекс. Используйте отдельные векторные подполя для поиска EmbeddingList и поиска на уровне элементов, когда требуются оба режима.
- Векторные подполя должны быть проиндексированы до поиска. Скалярные подполя, активно используемые в фильтрах, должны быть соответствующим образом проиндексированы.
- Схема подполей фиксируется после создания поля StructArray, поэтому планируйте атрибуты элементов до производственного развёртывания.
Эти ограничения делают модель более узкой, чем произвольная вложенность документо-ориентированных баз данных, но они также дают Milvus достаточно структуры, чтобы рассуждать об идентичности элементов, индексировать каждое подполе и выполнять поиск на двух гранулярностях.
StructArray сохраняет локальные свидетельства как первостепенные, не теряя сущность
StructArray даёт Milvus объект поиска, который плоские схемы с трудом представляют: родительскую сущность с упорядоченным набором структурированных элементов. Связи между этими элементами участвуют в фильтрации, индексации и поиске, а не существуют только в хранилище.
Каждый элемент сохраняет собственные метаданные и векторные представления. Элементы могут удовлетворять скалярным предикатам одного элемента, участвовать вместе в поиске EmbeddingList на уровне сущностей или независимо конкурировать в поиске на уровне элементов. В то же время они остаются привязанными к родительской сущности, чьи метаданные, разрешения и идентичность в приложении дают им контекст.
Для видеоклипов, изображений продуктов, отрывков документов, визуальных патчей и фрагментов памяти локальные свидетельства можно искать и фильтровать, не теряя сущность, которой они принадлежат. Оставшиеся проектные решения явны: выберите гранулярность поиска, задайте каждому векторному подполю соответствующую метрику и индекс и решите, должны ли гибридные результаты сохранять смещения элементов или схлопываться обратно в сущности.
Попробуйте StructArray в Milvus 3.0
StructArray доступен в Milvus 3.0. Начните с обзора StructArray. Если вы оцениваете многовекторный поиск на уровне сущностей, прочитайте руководство по стратегиям EmbeddingList. О гранулярности результатов и поведении схлопывания см. Гибридный поиск со StructArray.
Для более широкого контекста релиза см. анонс запуска Milvus 3.0, примечания к выпуску и репозиторий milvus-io/milvus.
Zilliz Cloud также поддерживает StructArray и поиск EmbeddingList для управляемых развёртываний. Ознакомьтесь с руководством Zilliz Cloud по StructArray для ограничений, специфичных для сервиса. В Zilliz Cloud скалярные операторы для StructArray в настоящее время документированы для кластеров On-Demand.
Чтобы обсудить схему или дизайн поиска с командой, присоединяйтесь к сообществу Milvus в Discord или запишитесь на сессию Office Hours Milvus.
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word



