От поиска к структурированным результатам: агрегация и ORDER BY в Milvus 3.0
Рассмотрим знакомый сценарий поиска товаров. Покупатель загружает фотографию платья, а векторный поиск извлекает релевантный набор кандидатов из каталога, содержащего десятки миллионов товаров.
Однако странице нужен не только ранжированный список. Ей нужны фасеты по брендам. Нужна сортировка по цене. Команда мерчандайзинга хочет знать, какие бренды доминируют в этом наборе результатов, каков ценовой диапазон внутри каждого бренда и какие несколько репрезентативных товаров есть в каждой группе.
До Milvus 3.0 приложения обычно выполняли этот второй шаг самостоятельно: получали строки из Milvus, группировали и сортировали их в pandas или на уровне сервиса, а затем собирали ответ. Некоторые команды поддерживали отдельный аналитический конвейер исключительно для вычисления счетчиков и распределений по данным, которые уже находились в векторной базе данных.
Векторная база данных находила кандидатов; приложение должно было превращать их в структурированный результат.
Milvus 3.0 переносит большую часть этой работы в механизм извлечения. Он добавляет три связанные, но разные возможности:
- Агрегация запросов вычисляет
count,sum,avg,minиmaxпо отфильтрованным видимым строкам с необязательными полямиGROUP BY. - Search Aggregation организует сохраненных кандидатов приблизительного поиска ближайших соседей (ANN) в корзины, вычисляет метрики по корзинам, строит вложенные корзины и возвращает репрезентативные совпадения.
- Серверный
**ORDER BY**сортирует результаты запросов или ANN-кандидатов по одному или нескольким скалярным полям до того, как приложение их получит.
Различие между query и search важно:
| Возможность | Данные, которые обобщаются или упорядочиваются | Основная форма результата | Граница точности |
|---|---|---|---|
| Агрегация запросов | Все видимые строки, соответствующие фильтру | Одна строка на группу с агрегированными значениями | Точно по видимому набору строк запроса |
| Search Aggregation | Кандидаты, сохраненные ANN-поиском и этапом группировки | Корзины, метрики, репрезентативные совпадения и необязательные дочерние корзины | Приблизительно по замыслу |
Query ORDER BY | Видимые строки, соответствующие фильтру | Отсортированные строки | Точно по отфильтрованному результату запроса |
Search ORDER BY | ANN-кандидаты | Отсортированные поисковые совпадения или группы | Не расширяет границу полноты ANN |
В этой статье объясняется, почему такие операции должны выполняться внутри базы данных, как работает распределенная агрегация, чем Search Aggregation отличается от Grouping Search и где заканчиваются новые семантики.
Почему постобработка на стороне приложения перестает работать
Перенос агрегации и сортировки в приложение может выглядеть как небольшое архитектурное решение. В масштабе оно создает три более серьезные проблемы.
Приложение перемещает гораздо больше данных, чем содержит ответ
Предположим, операционной панели нужны количество товаров и средняя цена для каждой категории среди двух миллионов строк товаров в наличии. Даже при грубой оценке полезной нагрузки всего в 100 байт на строку для категории, цены, первичного ключа и накладных расходов сериализации приложение должно получить около 200 МБ данных, прежде чем сможет вычислить результат.
Если в каталоге 200 категорий, ответ — это всего несколько сотен ключей и чисел, то есть порядка килобайт. Приложение перемещает на несколько порядков больше данных, чем возвращает, платит ту же цену при каждом обновлении и требует достаточного объема клиентской памяти, чтобы удерживать или потоково обрабатывать промежуточные строки.
Агрегация внутри движка меняет единицу перемещения данных. Сырые строки остаются там, где находятся. Между узлами и в конечном итоге за пределы Milvus передается гораздо меньший набор частичных и финальных состояний групп.
Сортировка в пределах страницы не является глобальной сортировкой
Сортировка после пагинации — это ошибка корректности, а не просто неэффективная реализация.
Если приложение получает строки с 11-й по 20-ю и сортирует только эти строки по цене, оно получает ценовой порядок внутри этой страницы, а не строки с 11-й по 20-ю глобально отсортированного по цене результата. Более поздняя страница может содержать товары дешевле любого товара на первой странице.
Та же граница важна и в векторном поиске. Получение небольшого набора Top-K и его сортировка в приложении может переупорядочить только этих кандидатов. Она не может восстановить релевантных кандидатов, которых этап ANN не вернул, и часто заставляет приложения запрашивать избыточное количество результатов, чтобы клиентская сортировка была полезной.
Серверная сортировка дает Milvus контроль над порядком и последовательностью пагинации. Для query-нагрузок движок сортирует отфильтрованный набор строк до применения окна страницы. Для search-нагрузок он сортирует в пределах границы ANN-кандидатов и явно сохраняет это ограничение.
Клиент не может воспроизвести видимость базы данных
Агрегация также зависит от того, какие строки видимы на временной метке запроса. Удаления, сущности с истекшим сроком действия и параллельные записи регулируются многоверсионным управлением конкурентным доступом (MVCC) и семантиками консистентности Milvus.
После того как сырые строки покидают базу данных, приложение обычно предполагает, что полученный пакет представляет корректный снимок. Воссоздать те же правила видимости на клиенте практически невозможно, особенно когда коллекция принимает записи и удаления.
Распространенный обходной путь — второй аналитический движок, наполняемый через экспорт и ETL, — добавляет еще одну копию данных, еще одну границу консистентности и еще один конвейер для эксплуатации. Счетчики, метрики и сортировка должны выполняться там, где уже находятся и данные, и правила их видимости.
Теперь посмотрим, что предлагает Milvus 3.0.
Агрегация запросов: точная статистика по видимым строкам
Агрегация запросов отвечает на вопросы вроде:
- Сколько товаров в наличии есть в каждой категории?
- Какова средняя цена по каждому бренду?
- Каковы минимальные и максимальные временные метки событий для каждого хоста?
- Сколько записей остается после применения фильтра и видимости TTL?
API выглядит знакомо всем, кто использовал SQL: передайте одно или несколько полей в group_by_fields, затем поместите агрегирующие выражения в output_fields.
res = client.query(
collection_name="products",
filter='status == "on_sale"',
group_by_fields=["category"],
output_fields=["category", "count(*)", "avg(price)"],
)
# [
# {"category": "books", "count(*)": 18734, "avg(price)": 45.3},
# …
# ]
Синтаксис — простая часть. Модель выполнения — вот что делает результат полезным в распределенной векторной базе данных.
Локальные состояния сегментов заменяют перемещение сырых строк
Коллекция Milvus может охватывать сотни или тысячи сегментов, распределенных по нескольким query-узлам, при этом недавно записанные данные все еще находятся на потоковом пути. Ни один отдельный узел выполнения изначально не имеет всех видимых строк.
Поэтому Milvus проталкивает агрегацию вниз, к сегментам:
- Каждый сегмент локально применяет фильтр и правила видимости MVCC.
- Сегмент выдает одно частичное состояние на группу вместо совпавших строк.
- Частичные состояния объединяются внутри query-узла.
- Прокси выполняет финальное межузловое слияние и возвращает завершенные группы.
Теперь объем промежуточных данных масштабируется с количеством групп и агрегатных состояний, а не напрямую с количеством совпавших строк.
Операция слияния зависит от агрегата:
| Агрегат | Частичное состояние | Правило слияния |
|---|---|---|
count | Частичный счетчик | Сложить счетчики |
sum | Частичная сумма | Сложить суммы |
min | Частичный минимум | Взять минимум |
max | Частичный максимум | Взять максимум |
avg | Частичная сумма и счетчик | Сложить оба состояния, затем разделить один раз на финальном этапе |
avg — показательный случай. Усреднение двух частичных средних некорректно, если разделы содержат разное количество строк. Milvus независимо переносит sum и count и вычисляет финальное среднее только после того, как оба значения были глобально объединены.
Это одна из причин, по которой агрегация должна находиться в базе данных: операция не сводится к тому, чтобы просто «запустить одну и ту же функцию на нескольких пакетах». Движок должен сохранять алгебру каждого агрегата при переходе через границы сегментов и узлов.
Видимость применяется до агрегации
Удаленные и истекшие строки исключаются из частичных состояний на уровне сегмента в соответствии с границей видимости запроса. Они не передаются выше, чтобы затем быть исправленными в приложении.
Поэтому результат описывает строки, которые Milvus считает видимыми для данного запроса, а не произвольный набор пакетов, полученных в слегка разные моменты времени.
limit теперь считает группы
В обычном запросе limit управляет количеством возвращаемых строк сущностей. В сгруппированном запросе он управляет количеством возвращаемых групп. Поскольку кардинальность результата определяется группами, а не совпавшими строками, агрегация запроса также может опускать limit, когда ей нужны все группы.
Это звучит как небольшая деталь API, но она отражает другую модель результата: вывод больше не является страницей сущностей. Это отношение, строки которого представляют группы.
Search Aggregation: представление ANN-кандидатов в виде корзин
Агрегация запросов отвечает на вопрос: «Как выглядят видимые строки, соответствующие этому фильтру?» Search Aggregation задает другой вопрос: «Как выглядит набор кандидатов, извлеченный для этого вектора?»
У этой операции нет точного SQL-эквивалента. ANN-поиск сначала устанавливает границу кандидатов на основе сходства. Затем Milvus организует сохраненных кандидатов по скалярным ключам и возвращает дерево корзин вместо обычного плоского списка совпадений.
Корзина может содержать:
- ключ, например
brand, или составной ключ, например(brand, color); - счетчик сохраненных кандидатов;
- метрики, включая
count,sum,avg,minиmax; - репрезентативные сущности, выбранные с помощью
top_hits; и - вложенную
sub_aggregation, создающую дочерние корзины.
Для страницы поиска товаров один запрос может вернуть корзины брендов, среднюю цену внутри каждой корзины и три репрезентативных товара на бренд:
from pymilvus import SearchAggregation, TopHits
aggregation = SearchAggregation(
fields=[“brand”],
size=10, # Return up to 10 brand buckets
metrics={“avg_price”: {“avg”: “price”}},
order=[{“_count”: “desc”}], # Order by retained-candidate count
top_hits=TopHits(
size=3,
sort=[{“_score”: “desc”}], # Use “asc” for L2 distance
),
)
res = client.search(
collection_name=“products”,
data=[query_vector],
anns_field=“embedding”,
search_aggregation=aggregation,
output_fields=[“title”, “brand”, “price”],
)
buckets = res.agg_buckets[0]
Когда задан search_aggregation, обычный список совпадений пуст. Приложение читает ответ с корзинами из result.agg_buckets.
Спецификация агрегации задает две разные границы
Search Aggregation не выполняет GROUP BY по каждой сущности в коллекции и не просто берет обычный ответ Top-K и агрегирует этот плоский список.
Ее выполнение состоит из трех этапов:
- Milvus выполняет ANN-поиск, чтобы извлечь кандидатов рядом с вектором запроса.
- Этап группировки сохраняет ограниченное количество кандидатов для каждого полного ключа корзины.
- Milvus строит корзины, вычисляет метрики по сохраненным кандидатам, упорядочивает корзины и прикрепляет репрезентативные совпадения или дочерние корзины.
Два параметра управляют разными частями результата:
SearchAggregation.sizeограничивает количество корзин, возвращаемых на данном уровне агрегации.- Наибольшее значение
TopHits.sizeв любом месте дерева агрегации задает бюджет сохраненных кандидатов для каждого полного составного ключа. Если запрос не содержитtop_hits, бюджет на ключ по умолчанию равен одному.
Верхнеуровневый search limit не управляет этим режимом и игнорируется, когда присутствует search_aggregation.
Это различие существенно при чтении count или метрик корзины. При TopHits(size=3) корзина бренда может обобщать не более трех сохраненных кандидатов для своего полного ключа, даже если коллекция содержит тысячи релевантных товаров этого бренда. Увеличение TopHits.size расширяет окно метрик на ключ, но не превращает ANN-поиск в точное сканирование.
Если приложению нужны точные статистики по каждой видимой строке, соответствующей фильтру, следует использовать агрегацию запросов. Search Aggregation предназначена для описания и сравнения кандидатов, полученных поиском по сходству.
Search Aggregation и Grouping Search решают разные задачи
Milvus поддерживает Grouping Search (group_by)с Milvus 2.4. Легко увидеть слово «grouping» в обеих возможностях и предположить, что это два интерфейса для одной и той же операции. Их контракты вывода различаются.
Grouping Search меняет то, какие сущности появляются в ранжированном списке результатов. Распространенный паттерн RAG хранит фрагменты как отдельные сущности, группирует их по doc_id и возвращает один или несколько фрагментов из каждого документа. Основным выводом остаются обычные поисковые совпадения, но с меньшим числом повторяющихся значений поля группировки.
Search Aggregation возвращает статистическое представление. Основной вывод — это дерево корзин, содержащее ключи, счетчики, метрики, репрезентативные совпадения и необязательные дочерние корзины.
| Потребность приложения | Предпочтительно | Что использовать |
|---|---|---|
| Ранжированный список сущностей с большим разнообразием по полю | Grouping Search | Обычные поисковые совпадения |
| Счетчики фасетов, метрики по группам, репрезентативные совпадения или вложенные распределения | Search Aggregation | Объекты AggregationBucket в result.agg_buckets |
Практическое правило — начинать с формы ответа UI или API. Если приложение отображает список, Grouping Search обычно является правильным примитивом. Если оно отображает фасеты, карточки распределений или иерархию групп, используйте Search Aggregation.
Эти два режима взаимоисключают друг друга в одном запросе, потому что определяют разные основные формы результата.
ORDER BY: перенесите сортировку до границы приложения
Сортировка — наименее экзотическая возможность в этом релизе и одна из тех, которые проще всего неправильно реализовать вне движка.
Milvus 3.0 предоставляет сортировку как для query, так и для search, но эти два пути используют разные параметры SDK и работают с разными входными наборами.
Сортировка query упорядочивает отфильтрованный набор строк
Query в PyMilvus использует order_by, выраженный как список строк "field:direction". Движок применяет фильтр, упорядочивает видимые строки, а затем применяет limit и offset.
res = client.query(
collection_name="products",
filter='category == "books"',
output_fields=["title", "price"],
order_by=["price:desc", "title:asc"],
limit=10,
offset=10, # Rows 11-20 in the filtered, price-sorted result
)
Это делает query полезным для просмотра в бизнес-порядке: самые новые загруженные записи, самые дорогие товары внутри фильтра, минимальные остатки или экстремальные значения для исследования данных. Без серверного упорядочивания приложениям приходилось сначала получать строки, и они не могли задать надежный бизнес-порядок на разных страницах.
Для nullable-полей query при сортировке по возрастанию null-значения помещаются в конец, а при сортировке по убыванию — в начало. Поле сортировки не обязано присутствовать в output_fields; включайте его только тогда, когда приложению нужно это значение в ответе.
Сортировка search переупорядочивает набор ANN-кандидатов
Search в PyMilvus использует order_by_fields, где каждая запись задает имя скалярного поля и направление:
res = client.search(
collection_name="products",
data=[query_vector],
anns_field="embedding",
limit=50,
output_fields=["title", "price"],
order_by_fields=[
{"field": "price", "order": "asc"},
],
)
ANN по-прежнему определяет, какие сущности становятся кандидатами. order_by_fields меняет то, как эти кандидаты возвращаются; он не заставляет поиск глобально сканировать коллекцию в поисках самых дешевых товаров.
Эта граница дает двум API разные задачи:
- Используйте query плюс
order_by, когда сам скалярный порядок определяет результат, например десять самых дешевых товаров в наличии. - Используйте search плюс
order_by_fields, когда семантическая или векторная релевантность определяет набор кандидатов, а скалярное поле определяет, как эти кандидаты должны быть представлены.
Многоуровневая сортировка применяет ключи в порядке списка. Когда search-кандидаты имеют одинаковые значения для каждого заданного скалярного ключа, Milvus сохраняет их исходный порядок по оценке сходства.
Сортировка также сочетается с Grouping Search. Milvus упорядочивает группы по настроенному скалярному значению из верхней сущности каждой группы, сохраняя сгруппированную форму результата. Это полезно, когда приложению нужны и разнообразие по полю, и бизнес-релевантный порядок групп.
Что позволяют эти возможности
Эти API являются универсальными примитивами базы данных, но несколько retrieval-нагрузок получают от них немедленную пользу.
RAG и агенты: анализ концентрации извлечения
RAG- или агентная система может раскладывать извлеченные фрагменты по исходному документу, продуктовой линейке, арендатору или типу контента. Результат, сконцентрированный в двух документах, несет другой сигнал покрытия, чем результат, распределенный по десяткам источников.
Такое распределение не является гарантией качества ответа. Однако это полезная диагностика извлечения, которую приложение или агент может сочетать с оценками, цитированием и другими проверками при решении, расширять ли запрос, выполнять извлечение снова или попросить уточнение.
Grouping Search остается правильным выбором, когда цель — просто разнообразить возвращаемые фрагменты. Search Aggregation полезна, когда системе нужно само распределение.
E-commerce и рекомендации контента: возвращайте фасеты вместе с поиском
Начальная страница поиска товаров может получить из Milvus корзины брендов, ценовые метрики, репрезентативные позиции и отсортированный по скалярному полю список кандидатов. Приложение по-прежнему контролирует представление и бизнес-логику, но ему больше не нужно восстанавливать базовые семантики корзин из экспортированных совпадений.
Логи и безопасность: сочетание сходства с распределением инцидентов
Поиск по сходству может находить события, связанные с подозрительной строкой лога. Затем Search Aggregation может показать, какие хосты доминируют среди этих кандидатов, минимальную и максимальную временную метку в каждой корзине хоста или как кандидаты распределяются по severity и сервису.
Результат остается представлением извлеченных кандидатов, а не точным глобальным счетчиком инцидентов. Когда расследованию нужны точные счетчики по каждому событию, соответствующему фильтру, агрегация запросов предоставляет второй путь.
Операции и исследование данных: вычислять вместо экспортировать
Панели мониторинга и административные инструменты могут выполнять точные счетчики и средние значения по отфильтрованным строкам, а затем просматривать базовые сущности в заданном скалярном порядке. Это устраняет множество одноразовых утилит «экспортировать, вычислить и отсортировать», не делая вид, что Milvus стал полноценной аналитической базой данных.
Границы: что агрегация и ORDER BY не заменяют
Эти функции расширяют retrieval-движок; они не превращают Milvus в систему оперативной аналитической обработки (OLAP).
- Агрегация запросов поддерживает группировку плюс
count,sum,avg,minиmax. Она не добавляет соединения, оконные функции или сложные подзапросы. Крупные офлайн-аналитические задания по-прежнему относятся к системам вроде Spark, которые могут работать со снимками Milvus 3.0 и общими путями хранения. - Ключи групп query поддерживают поля integer,
VARCHARиTIMESTAMPTZ. Ключи корзин Search Aggregation дополнительно поддерживают булевы поля. Значения с плавающей точкой, векторы, JSON и массивы не являются ключами корзин. - Для Search Aggregation
countпринимает"*"или не-JSON, нединамический источник;sumиavgтребуют числовых источников; аminиmaxтакже поддерживают строковые иTIMESTAMPTZисточники. Агрегация запросов следует тем же границам арифметических типов. Обратитесь к руководству API перед применением агрегата к сложному типу поля. - Агрегация запросов может упорядочивать сгруппированный вывод по ключам групп, тогда как упорядочивание по вычисленному агрегату, например
count(*), остается текущей границей. Без явного порядка порядок групп не гарантируется. - Search Aggregation в настоящее время нельзя сочетать в одном запросе с Hybrid Search, Grouping Search, Search Iterators, ненулевым offset или подсветкой.
- Счетчики и метрики Search Aggregation описывают сохраненных ANN-кандидатов, а не всю коллекцию и не каждую сущность, которая может быть семантически релевантной.
- Search
ORDER BYменяет представление кандидатов. Он не исправляет пропущенных ANN-кандидатов и не превращает поиск по сходству в точный скалярный запрос Top-N.
Самый простой способ выбрать между новыми примитивами — начать с вопроса:
- Для точной статистики по отфильтрованным видимым строкам используйте агрегацию запросов.
- Для распределения по кандидатам поиска по сходству используйте Search Aggregation.
- Для разнообразного ранжированного списка используйте Grouping Search.
- Для заданного скалярного порядка используйте query или search
ORDER BYв зависимости от того, какой путь сформировал набор результатов.
От списков кандидатов к структурированным результатам
Векторные базы данных традиционно оптимизировали один вопрос: какие K сущностей находятся ближе всего к этому вектору?
Производственные retrieval-системы сразу задают последующие вопросы. Какие группы доминируют в результате? Каковы их счетчики и диапазоны? Какие примеры представляют каждую группу? В каком бизнес-порядке приложение должно показывать строки или кандидатов?
Milvus 3.0 переносит эти операции в тот же движок, которому принадлежат данные, граница ANN-кандидатов и семантики видимости. Агрегация запросов выполняет точное распределенное сокращение по видимым строкам. Search Aggregation строит представление в виде корзин по сохраненным ANN-кандидатам. ORDER BY дает путям query и search серверный скалярный порядок, не заставляя приложение восстанавливать его постранично.
Результат — это не OLAP-движок, скрытый внутри векторной базы данных. Это retrieval-движок, который может возвращать больше структуры, фактически необходимой приложениям.
Попробуйте агрегацию и ORDER BY в Milvus 3.0
Milvus 3.0 уже доступен. Используйте руководство по Query для точной агрегации и сортировки query, руководство по Search Aggregation для семантик корзин и ограничений, руководство по Basic Vector Search для сортировки search и руководство по Grouping Search, когда ваша основная цель — разнообразие результатов.
Подробнее о релизе в целом см. блог о запуске Milvus 3.0, заметки о выпуске Milvus 3.0 и репозиторий milvus-io/milvus.
Если вы хотите оценить те же API без самостоятельной эксплуатации кластера, попробуйте их в Zilliz Cloud. Текущие справочник Zilliz Cloud по query и справочник по search описывают доступность и параметры для типов управляемых кластеров.
Чтобы обсудить нагрузку или пограничный случай с командой, присоединяйтесь к сообществу Milvus в Discord или запишитесь на сессию Milvus Office Hours.
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word



