メインコンテンツまでスキップ

Search Aggregation

ユーザーが「日常トレーニング用の黒いランニングシューズ」を検索すると、近似最近傍(ANN)検索は商品をベクトル類似度に基づいてランキングし、フラットな Top-K リストを返します。結果は関連性が高いものの、重複が目立つ場合があります。例えば、以下のケースでは最初の6件中4件がブランドAの商品であり、ブランドBとブランドCはそれぞれ1件ずつしか表示されません。

フラットなリストのままでは、バケット単位のサマリーを直接得ることはできません。アプリケーションによっては、保持された候補数や平均価格を使ってブランドを比較したり、各ブランドから少数の代表的な商品を確認したり、結果を複数のバケット階層に整理したりする必要があるかもしれません。

Search Aggregation は、保持された ANN 候補を選択したスカラーフィールドに基づいてバケットに整理します。この例では、各ブランドが個別のバケットとなります。Zilliz Cloud を使用すると、各バケットの統計計算、バケットの並べ替え、代表商品の付与が可能です。アプリケーションは、このバケット優先のレスポンスを result.agg_buckets を通じて受け取ります。

CPHkwKQTRhuEQKbsxdacRZuCnVe

Search Aggregation は、コレクション全体に対する厳密な集約処理を行うわけではありません。バケットの有無、件数、メトリクス、並び順、および代表的なヒット結果は、ANN およびグループ化ステージで保持された候補に依存します。

仕組み

Edbbw7oulhszR2baU7BcsjiMntf

  1. 候補の取得: Zilliz Cloud は ANN 検索を実行し、クエリベクトルに最も近いエンティティを取得します。その後、グループ化ステージで完全複合キーごとに一定数の候補を保持します。このキーごとの候補バジェットは、集約ツリー内で設定される最大の TopHits.size 値、あるいはどのレベルでも top_hits が設定されていない場合は 1 となります。

  2. バケットの構築: SearchAggregation.fields パラメーターがバケットキーを定義します。フィールド値の一意の組み合わせごとに個別のキーが生成されます。図の例では、fields=["brand"] によって (Brand A)(Brand B)(Brand C) のバケットキーが作成されています。同じキーを持つ保持候補は同一のバケットに分類され、その count に寄与します。SearchAggregation.size は、Zilliz Cloud が返すバケット数の上限を指定します。

  3. 結果の計算と返却: 返される各バケットには、キーと保持された候補数が含まれます。Zilliz Cloud はさらに、設定されたメトリクスの計算、バケットのソート、代表エンティティの返却、子バケットの構築も実行できます。result.agg_buckets 内の各 AggregationBucket には、keycountmetricshitssub_groups が含まれます。Search Aggregation が有効な場合、通常の検索ヒットリストは空になります。

図の例では、TopHits.size=4 によりキーごとの候補バジェットが4に設定されているため、保持された4件のブランドA候補から count: 4 が生成されます。なお、図を見やすくするため、ブランドAのカードには返された4件の代表ヒットのうち2件のみを表示しています。

sub_aggregation を使用すると、Zilliz Cloud は各親バケット内でステップ2と3を繰り返します。ANN の再現率やキーごとの候補バジェットを変更すると、バケット数、メトリクス、並び順、ヒット内容、およびネストされた結果が変化する可能性があります。

制限事項

Search Aggregation を使用する際は、以下の制限事項にご注意ください。

  • ネストされた集約: 1つのリクエストには、ルート SearchAggregation を1つと、最大3階層のネストされた sub_aggregation を含めることができ、合計で最大4階層まで指定可能です。全階層を通じて、バケットキーの作成に使用できるフィールドは最大10個です。

  • バケットキー作成に使用するフィールド: SearchAggregation.fields パラメーターには、Boolean、整数、VARCHARTIMESTAMPTZ フィールドを使用できます。FLOATDOUBLEARRAYJSONGEOMETRYTEXT、ベクトル、および動的フィールドはサポートされていません。

  • メトリクスフィールド: count 操作には、"*" または非 JSON・非動的フィールドを使用でき、フィールド指定時は NULL 値がスキップされます。sumavg には整数および浮動小数点フィールドを使用できます。minmax では、さらに文字列および TIMESTAMPTZ フィールドも使用可能です。

  • Top Hits のソートフィールド: TopHits.sort パラメーターには、比較可能な Boolean、整数、浮動小数点、文字列、TIMESTAMPTZ フィールドのほか、_score を使用できます。ARRAYJSONGEOMETRY、ベクトル、および動的フィールドはサポートされていません。

  • 候補バジェット: 集約ツリー内で設定される最大の TopHits.size が、完全複合キーごとに保持される候補数となります。どのレベルでも top_hits が設定されていない場合、Zilliz Cloud はキーごとに1件の候補のみを保持します。バケットの count やメトリクスはこれらの保持候補から計算されるため、TopHits.size を変更すると結果も変わる可能性があります。

  • Null 許容バケットフィールド: NULL 値も独立したバケットキーとして扱われます。null バケットを除外したい場合は、検索リクエストに brand is not null などのフィルターを追加してください。

  • フィールドの重複禁止: 同じフィールドを複数の SearchAggregation.fields リストに指定することはできません。たとえば、ルート集約で fields=["category"] を使用した場合、ネストされた sub_aggregation で再度 fields=["category"] を使うことはできません。

  • 併用できない機能: Search Aggregation は、0以外の offset、Search Iterators、Hybrid Search、Highlighter、Grouping Search と併用できません。トップレベルの offset0 を指定した場合は、パラメーターを省略したときと同じ動作になります。REST v2 プロトコルでは、searchAggregationids を同時に指定することはできません。

  • 返却エントリ数: 計算上の最大結果エントリ数は10,000以下に抑えてください。サーバーはこの最大値を number of query vectors × product of the effective search_size at every aggregation level × largest TopHits.size at any level として算出します。

    どのレベルでも TopHits が設定されていない場合、最後の係数には 1 が使われます。たとえば、クエリベクトルが1つ、ルートバケットが10個、各ルートバケットに子バケットが5個、各子バケットのヒット数が2件の場合、計算上の最大値は 1 × 10 × 5 × 2 = 100 となります。

Search Aggregation の使い方

目的に応じて以下の例を選択してください。

項目説明主な設定
バケットの比較とソートバケットごとの統計情報を算出して比較し、返されたバケットをメトリクス、件数、またはキーでソートします。fields, size, metrics, order
各バケットの代表結果の表示各バケットから限られた数のエンティティを取得し、それらをスカラーフィールドまたはベクトルスコアで個別にソートします。top_hits, TopHits.size, TopHits.sort
複数レベルでの結果グループ化結果を親・子のバケット階層に整理し、複数のディメンションを段階的に分析します。sub_aggregation

以下の例では、brand、category、color、price、rating フィールドを持つ商品コレクションを使用します。ブランド名、商品名、価格、評価、および検索結果はすべて合成データです。次のセクションを展開してコレクションを作成し、共通の検索変数を定義してください。

サンプルコレクションをセットアップする
python
from pymilvus import DataType, MilvusClient, SearchAggregation, TopHits

client = MilvusClient(
uri="YOUR_CLUSTER_OR_PROJECT_ENDPOINT",
token="YOUR_AUTHORIZED_TOKEN",
)

collection_name = "product_search_aggregation"

if client.has_collection(collection_name):
client.drop_collection(collection_name)

schema = client.create_schema(auto_id=False, enable_dynamic_field=False)
schema.add_field("id", DataType.INT64, is_primary=True)
schema.add_field("embedding", DataType.FLOAT_VECTOR, dim=5)
schema.add_field("name", DataType.VARCHAR, max_length=200)
schema.add_field("brand", DataType.VARCHAR, max_length=100)
schema.add_field("category", DataType.VARCHAR, max_length=100)
schema.add_field("color", DataType.VARCHAR, max_length=50)
schema.add_field("price", DataType.DOUBLE)
schema.add_field("rating", DataType.DOUBLE)
schema.add_field("in_stock", DataType.BOOL)

index_params = client.prepare_index_params()
index_params.add_index(
field_name="embedding",
index_type="AUTOINDEX",
metric_type="COSINE",
)

client.create_collection(
collection_name=collection_name,
schema=schema,
index_params=index_params,
# Make preceding writes visible to searches from this client.
consistency_level="Session",
)

client.insert(
collection_name=collection_name,
data=[
{
"id": 1,
"embedding": [0.12, 0.42, 0.18, 0.66, 0.31],
"name": "Runner A1",
"brand": "Brand A",
"category": "running_shoes",
"color": "black",
"price": 129.99,
"rating": 4.7,
"in_stock": True,
},
{
"id": 2,
"embedding": [0.10, 0.39, 0.20, 0.61, 0.29],
"name": "Trail A2",
"brand": "Brand A",
"category": "running_shoes",
"color": "blue",
"price": 139.99,
"rating": 4.6,
"in_stock": True,
},
{
"id": 3,
"embedding": [0.14, 0.44, 0.19, 0.68, 0.33],
"name": "Runner B1",
"brand": "Brand B",
"category": "running_shoes",
"color": "white",
"price": 159.99,
"rating": 4.8,
"in_stock": True,
},
{
"id": 4,
"embedding": [0.16, 0.41, 0.22, 0.62, 0.30],
"name": "Runner C1",
"brand": "Brand C",
"category": "running_shoes",
"color": "red",
"price": 119.99,
"rating": 4.4,
"in_stock": False,
},
{
"id": 5,
"embedding": [0.48, 0.20, 0.59, 0.15, 0.71],
"name": "Jacket A1",
"brand": "Brand A",
"category": "jackets",
"color": "black",
"price": 99.99,
"rating": 4.5,
"in_stock": True,
},
{
"id": 6,
"embedding": [0.45, 0.18, 0.55, 0.17, 0.69],
"name": "Jacket B1",
"brand": "Brand B",
"category": "jackets",
"color": "blue",
"price": 89.99,
"rating": 4.3,
"in_stock": True,
},
{
"id": 7,
"embedding": [0.09, 0.38, 0.17, 0.60, 0.27],
"name": "Runner A3",
"brand": "Brand A",
"category": "running_shoes",
"color": "black",
"price": 159.99,
"rating": 4.8,
"in_stock": True,
},
{
"id": 8,
"embedding": [0.13, 0.43, 0.21, 0.65, 0.32],
"name": "Runner A4",
"brand": "Brand A",
"category": "running_shoes",
"color": "black",
"price": 149.99,
"rating": 4.9,
"in_stock": True,
},
],
)

client.load_collection(collection_name)

query_vector = [0.11, 0.40, 0.19, 0.64, 0.30]
search_params = {
"metric_type": "COSINE",
"params": {},
}

上記の設定では、ベクトルインデックスと検索パラメーターの両方に COSINE を指定しています。そのため、以降の例では {"_score": "desc"} を使用して、コサイン類似度が高い順に並べ替えます。L2 のような距離メトリックの場合は、{"_score": "asc"} を使用してください。

バケットの比較とソート

このパターンは、取得したエンティティのグループを統計値に基づいて比較し、バケットの返却順序を制御したい場合に使用します。この例では、Zilliz Cloud が取得した製品を brand でグループ化し、各ブランドバケットの価格メトリックを計算した上で、平均価格でバケットをソートします。

フィールド値ごとに1つ以上のエンティティを返すことで結果の多様性を高めることだけが目的であれば、代わりに Grouping Search を使用してください。

次の設定では、最大3つのブランドバケットを作成し、各バケットのメトリックを計算して、平均価格でバケットをソートします。

python
aggregation = SearchAggregation(
# Form one bucket for each distinct brand value.
fields=["brand"],
# Return up to three buckets at this aggregation level.
size=3,
# Calculate named metrics for every selected bucket.
metrics={
"product_count": {"count": "*"},
"avg_price": {"avg": "price"},
"min_price": {"min": "price"},
},
# Sort buckets by average price, highest first.
order=[
{"avg_price": "desc"},
# If average prices are equal, sort by bucket key in ascending order.
{"_key": "asc"},
],
)

このオブジェクトを MilvusClient.search()search_aggregation パラメーターに渡します。

python
result = client.search(
collection_name=collection_name,
data=[query_vector],
anns_field="embedding",
search_params=search_params,
output_fields=[
"name",
"brand",
"category",
"color",
"price",
"rating",
"in_stock",
],
search_aggregation=aggregation,
)

search_aggregation を設定すると、PyMilvus は result[0] に通常のエンティティヒットを返しません。代わりに result.agg_buckets[0] からバケットのレスポンスを取得してください。output_fields パラメーターは、返される各 AggregationHit.fields マッピングに含まれるスカラーフィールドを制御します。なお、Zilliz Cloud では、output_fields に含まれていないメトリックソースやソート用のフィールドも引き続き使用できます。

バケット出力の例を表示

以下の出力は上記のリクエストから取得したもので、可読性のために JSON 形式にシリアライズしています。PyMilvus は JSON ではなく AggregationBucket オブジェクトを返します。key の値は、fields にフィールドが1つしかない場合でも、常にキー構成要素の順序付きリストとなります。これにより、複合キーにおけるフィールドの順序が維持されます。

json
[
{
"key": [
{
"field_id": 103,
"field_name": "brand",
"value": "Brand B"
}
],
"count": 1,
"metrics": {
"product_count": 1,
"avg_price": 159.99,
"min_price": 159.99
},
"hits": [],
"sub_groups": []
},
{
"key": [
{
"field_id": 103,
"field_name": "brand",
"value": "Brand A"
}
],
"count": 1,
"metrics": {
"product_count": 1,
"avg_price": 129.99,
"min_price": 129.99
},
"hits": [],
"sub_groups": []
},
{
"key": [
{
"field_id": 103,
"field_name": "brand",
"value": "Brand C"
}
],
"count": 1,
"metrics": {
"product_count": 1,
"avg_price": 119.99,
"min_price": 119.99
},
"hits": [],
"sub_groups": []
}
]

このガイドで使用する単一のクエリベクトルでは、返されるトップレベルのバケットを result.agg_buckets[0] から取得します。各バケットには、順序付きのキー構成要素、保持された候補の count、計算済みの metrics、代表的な hits、およびネストされたバケットが sub_groups に含まれます。

設定の内容は以下のとおりです。

設定項目制御対象この例での値
fieldsZilliz Cloud によるバケットキーの生成方法一意の brand 値ごとにバケットを1つ作成します。
size返されるバケットの最大数ブランドバケットを最大3つ返します。
metrics各バケットに対して計算される統計情報商品数、平均価格、最低価格を計算します。
orderZilliz Cloud によるバケットのソート方法平均価格でソートし、同値の場合はバケットキーで順位を決定します。

search_aggregation が設定されている場合、Zilliz Cloud は limit を無視します。トップレベルのバケット数を制御するには、ルートの SearchAggregation.size 値を使用してください。

この設定により、Zilliz Cloud は Brand B、Brand A、Brand C のバケットを avg_price の降順で返します。_key によるソートは、バケットの平均価格が同じ場合にのみ適用されます。この設定では top_hits が定義されていないため、各バケットの hits リストは空となり、キーごとの候補バジェットは 1 となります。したがって、表示されるカウントとメトリクスは、各ブランドについて保持された候補が1つであることを示しています。アグリゲーションでキーごとのメトリクスウィンドウを広くする必要がある場合は、top_hits に大きな TopHits.size を設定してください。

メトリクスとソートのルール

SearchAggregation.metrics エントリは、ユーザー定義のエイリアスを演算とそのソースに対応付けます。

ソースサポートされる演算動作
JSON および動的フィールド以外のフィールドcountソースフィールドが NULL でない保持候補の数をカウントします。
整数または浮動小数点フィールドsumavgminmaxnull でない保持値に対して計算を実行します。
文字列または TIMESTAMPTZ フィールドminmaxnull でない保持値の最小値または最大値を選択します。
"*"countバケット内のすべての保持候補をカウントします。結果は bucket.count と一致します。
_scoresumavgminmax保持候補の ANN 類似度または距離の値を集計します。

SearchAggregation.order では以下のキーを使用できます。

ソートキー説明
メトリクスのエイリアス同じアグリゲーションレベルの metrics で計算された値(avg_price など)に基づいてソートします。
_count各バケット内の保持候補数に基づいてソートします。
_key_key という名前のコレクションフィールドではなく、バケットキーに基づいてソートします。

order エントリは、キーを "asc" または "desc" に対応付けます。Zilliz Cloud は複数のエントリを先頭から順に評価します。order を省略した場合、Zilliz Cloud は保持候補セットにおけるバケットの検出順序を維持します。

ベクトルのマッチ品質に基づいてバケットをソートするには、まず _score からバケットレベルのメトリクスを計算し、そのメトリクスのエイリアスを order で使用します。各バケットには複数のエンティティスコアが含まれる可能性があるため、_score をバケットのソートキーとして直接使用することはできません。たとえば、COSINEIP を使用する場合:

python
aggregation = SearchAggregation(
fields=["brand"],
size=3,
metrics={"max_score": {"max": "_score"}},
order=[{"max_score": "desc"}],
)

L2 を使用する場合、_score の最小値を計算し、メトリクスのエイリアスを昇順でソートすることで、距離が最も小さいバケットが先頭に来るようにします。

複合バケットキーの作成

複合バケットキーを作成するには、同じリスト内に複数のフィールド名を指定します。

python
aggregation = SearchAggregation(
# Combine brand and color to form a composite bucket key.
fields=["brand", "color"],
size=6,
)

この設定により、(Brand A, black)(Brand A, blue)(Brand B, white) などのキーが生成される可能性があります。2つのエンティティが同じバケットに属するのは、両方の値が一致する場合のみです。Zilliz Cloud はリストの順序を保持するため、brand が最初のキー構成要素、color が2番目の構成要素となります。order_key を使用すると、Zilliz Cloud は複合キーの構成要素を同じ順序で比較します。複数の文字列は1つのフラットなリストで渡してください。ネストされたリストはサポートされていません。

size=6 は、このアグリゲーションレベルで返される複合バケットの最大数です。サンプルデータにはブランドと色の組み合わせが5通り含まれているため、最大5つすべてを返すことができます。返されるエントリの制限 において、このリクエストは設定された結果エントリ 1 query vector × 6 buckets × 1 = 6 件分としてカウントされます。

1つの SearchAggregation.fields リストに複数のフィールドを指定すると、そのアグリゲーションレベルで複合バケットキーが作成されます。親子関係にあるバケット階層を作成するには、ネストされたアグリゲーション を使用してください。

以降の例では aggregation を再定義します。更新したオブジェクトを同じ search_aggregation パラメータに渡し、検索呼び出しを再実行してください。

各バケットの代表結果を表示する

アプリケーションで各バケットの実際の商品を表示する必要がある場合は、代表エンティティを含めます。この例では、Zilliz Cloud が各ブランドバケットから最大2件の商品を、rating の順、次にベクトルスコアの順で返します。

TopHits を次のように設定します。

python
aggregation = SearchAggregation(
fields=["brand"],
size=3,
# Return and sort representative entities for each selected bucket.
top_hits=TopHits(
# Return up to two entities per bucket.
size=2,
# Apply sort criteria in list order.
sort=[
{"rating": "desc"},
{"_score": "desc"},
],
),
)
代表ヒットを含むバケットを表示

以下の Brand A バケットは、上記のリクエストから取得したものを、読みやすく JSON 形式にシリアル化したものです。

json
{
"key": [
{
"field_id": 103,
"field_name": "brand",
"value": "Brand A"
}
],
"count": 2,
"metrics": {},
"hits": [
{
"pk": 1,
"score": 0.99976646900177,
"fields": {
"brand": "Brand A",
"category": "running_shoes",
"color": "black",
"in_stock": true,
"name": "Runner A1",
"price": 129.99,
"rating": 4.7
}
},
{
"pk": 2,
"score": 0.9997048377990723,
"fields": {
"brand": "Brand A",
"category": "running_shoes",
"color": "blue",
"in_stock": true,
"name": "Trail A2",
"price": 139.99,
"rating": 4.6
}
}
],
"sub_groups": []
}
パラメーター目的
top_hits任意です。この集計レベルにおける代表エンティティを設定します。省略した場合、bucket.hits は空になり、キーごとの候補バジェットはデフォルトの 1 になります。
TopHits.size選択された各バケットから最大2件の代表エンティティを返し、集計ツリー全体のキーごとの候補バジェットを 2 に設定します。
TopHits.sort指定された条件に基づいて、各バケット内のエンティティを並べ替えます。

アプリケーションで代表エンティティが必要な場合や、カウントおよびメトリクスに対してより広いキーごとの候補ウィンドウが必要な場合は、top_hits を設定します。TopHits.size を大きくすると、候補バジェットと制限における最大返却エントリ数の計算値の両方が増加します。

SearchAggregation.order はバケットを並べ替え、TopHits.sort は各バケット内に保持されたエンティティを並べ替えます。並べ替え順序は、count やメトリクス用に保持される候補に影響しません。TopHits.sort には、比較可能なスカラーフィールド名と、ANN の類似度または距離を表す組み込みフィールド _score を指定できます。Zilliz Cloud は sort のエントリを先頭から順に評価します。この例では、商品を rating の高い順に並べ、rating が等しい場合にのみ _score を使用します。設定で COSINE が使われているため、_score の降順により、より類似した商品が先頭に配置されます。

metricsTopHits.sort で使用するフィールドを output_fields に含める必要はありません。Zilliz Cloud がこれらのフィールドを内部的に取得しますが、返される各ヒットの fields マッピングに含まれるのは、output_fields に明示的に指定されたフィールドのみです。プライマリキーとベクトルスコアは、AggregationHit.pk および AggregationHit.score から引き続き取得できます。

返される各 AggregationHit には、pk にプライマリキー、score にベクトルスコア、fields に要求された出力フィールドが含まれます。

複数レベルでの結果のグループ化

バケット内にさらに別のレベルのバケットが必要な場合は、ネストされた集計を使用します。この例では、Zilliz Cloud がまずカテゴリバケットを作成し、その中にブランドバケットを作成します。

子集計には、親バケットに割り当てられたエンティティのみが渡されます。fields が各集計レベルのバケットキーを制御し、sub_aggregation が親子階層を形成します。

以下の設定では、キーが (running_shoes) のカテゴリバケットを作成します。この親バケット内で、子集計により (Brand A)(Brand B)(Brand C) などのキーを持つ個別のブランドバケットが作成されます。

plaintext
Parent bucket key:
(running_shoes)

Child bucket keys:
├── (Brand A)
├── (Brand B)
└── (Brand C)

各レベルでは複数のフィールドを独立して使用できます。たとえば、子集計で fields=["brand", "color"] を使用すると、(Brand A, black) のような複合子キーが作成されます。

以下の設定はこの階層を実装するものです。

python
aggregation = SearchAggregation(
fields=["category"],
size=2,
metrics={
"product_count": {"count": "*"},
"avg_price": {"avg": "price"},
},
order=[{"product_count": "desc"}],
# For each category bucket, group only its entities by brand.
sub_aggregation=SearchAggregation(
fields=["brand"],
size=3,
metrics={
"brand_count": {"count": "*"},
"avg_rating": {"avg": "rating"},
},
order=[{"avg_rating": "desc"}],
top_hits=TopHits(
size=2,
sort=[{"rating": "desc"}],
),
),
)
ネストされたバケット結果を表示

以下のシリアル化された抜粋は、running_shoes 親バケットとその子である Brand B バケットを示しています。Brand A および Brand C の子バケットは簡潔にするため省略しています。

json
{
"key": [
{
"field_id": 104,
"field_name": "category",
"value": "running_shoes"
}
],
"count": 4,
"metrics": {
"avg_price": 137.49,
"product_count": 4
},
"hits": [],
"sub_groups": [
{
"key": [
{
"field_id": 103,
"field_name": "brand",
"value": "Brand B"
}
],
"count": 1,
"metrics": {
"avg_rating": 4.8,
"brand_count": 1
},
"hits": [
{
"pk": 3,
"score": 0.9994542598724365,
"fields": {
"brand": "Brand B",
"category": "running_shoes",
"color": "white",
"in_stock": true,
"name": "Runner B1",
"price": 159.99,
"rating": 4.8
}
}
],
"sub_groups": []
}
]
}

表示されている結果は単一の複合バケットキー (running_shoes, Brand B) ではなく、バケットパス (running_shoes) → (Brand B) を表しています。

Zilliz Cloud はまず、product_count の順で最大2つのカテゴリバケットを選択します。次に、選択された各カテゴリ内で独立して sub_aggregation を実行し、avg_rating の順で最大3つのブランドバケットを返します。

上記の出力の内容は以下のとおりです。

  • ルートの running_shoes バケットには、子の複合キー全体で4件の保持候補が含まれています。その metrics には、ルートレベルの avg_priceproduct_count の値が含まれます。

  • ルートバケットの sub_groups リストには、子のブランドバケットが含まれています。表示されている Brand B バケットには1件の保持候補が含まれており、独自の avg_rating および brand_count の値を持っています。

  • ルートバケットの hits リストは空です。これはルート集計で top_hits が設定されていないためです。一方、Brand B の子バケットに代表ヒットが含まれているのは、sub_aggregationtop_hits が設定されているためです。

FAQ

バケット数とメトリクスの精度はどの程度ですか?

Search Aggregation は、保持された ANN 候補を集約するものであり、コレクション全体を対象とした集約は実行しません。

候補の保持には 2 つの近似段階があります。ANN 検索では関連するコレクション内のエンティティが省略される場合があり、グループ化段階では完全複合キーごとに最大 TopHits.size 件の候補のみが保持されます。どのレベルでも top_hits が設定されていない場合、このキーごとの上限は 1 です。

たとえば、コレクションに Brand A の商品が 5,000 件含まれており、その多くがベクトルクエリに関連しているとします。集約で TopHits(size=4) を使用する場合、Brand A バケットは完全複合キーごとに最大 4 件の候補しか保持できません。したがって、その count とメトリクスは保持された候補を表すものであり、関連するすべての Brand A 商品や、コレクション内の全 5,000 エンティティを表すものではありません。

近似の影響が最も大きくなるのは、order でメトリクスエイリアスを使用する場合です。検索再現率の変化によってメトリクス値が変わり、結果として SearchAggregation.size に収まるバケットが変化する可能性があります。ネストされた集約では、各子レベルが親バケット内で利用可能なエンティティに対して動作するため、この影響が増幅されることがあります。

一致するすべてのエンティティについて正確な統計が必要な場合は、Search Aggregation ではなく厳密なクエリ集約ワークフローを使用してください。

アプリケーションが主に必要とする結果形式に応じて使い分けます。

主な要件推奨使用するレスポンス
グループ化フィールドの重複値を抑えつつ、標準的なランク付け済みエンティティリストを返したいGrouping Search各クエリベクトルに対するフラットな検索ヒット
キー、件数、メトリクス、順序、代表ヒット、子バケットなどを持つバケットとしてグループを確認・比較したいSearch Aggregationresult.agg_buckets 内の AggregationBucket オブジェクト

Search Aggregation で top_hits を設定した場合でも、レスポンスの主構造はバケットツリーです。一方、Grouping Search は、アプリケーションが通常の検索ヒットをすでに処理しており、主に結果の多様性を確保したい場合に有効です。

これらの API は相互排他です。PyMilvus では、同一リクエスト内で search_aggregationgroup_by_field または group_by_fields と組み合わせると ParamError が送出されます。

Ctrl I