A standard vector search returns the globally top-k most similar vectors — but when multiple vectors come from the same source (e.g., multiple chunks of the same document, or multiple images of the same product), those top-k slots can be dominated by a single source. Grouped vector search fixes this by returning the best-matching results from distinct groups, so each group contributes at most group_topk results and at most group_count groups appear in total.
Use cases
Retrieval-augmented generation (RAG): Documents are split into segments, each vectorized and stored in DashVector. Without grouping, multiple chunks from the same document may fill the top results. Set
group_by_fieldto the document ID field to ensure each group represents a distinct source document.Product image retrieval: Multiple images represent a single product, each stored as a separate vector. Set
group_by_fieldto the product ID field to return results from different products rather than multiple images of the same one.
Prerequisites
Before you begin, ensure that you have:
A cluster — Create a cluster
An API key — Manage API keys
The latest DashVector SDK installed — Install DashVector SDK
Run a grouped vector search
The following example uses a RAG scenario. Six document segments from three papers are stored in a collection. The search returns the two most similar papers (groups), with up to two segments per paper.
The collection uses fields_schema to define document_id (string) and chunk_id (integer). The group_by_field parameter must reference a field defined in fields_schema — schema-free fields are not supported for grouped search.
Step 1: Create a collection and insert documents
Replace YOUR_API_KEY and YOUR_CLUSTER_ENDPOINT with your actual values.
import dashvector
import numpy as np
client = dashvector.Client(
api_key='YOUR_API_KEY',
endpoint='YOUR_CLUSTER_ENDPOINT'
)
# Create a collection with schema-defined fields
ret = client.create(
name='group_by_demo',
dimension=4,
fields_schema={'document_id': str, 'chunk_id': int}
)
assert ret
collection = client.get(name='group_by_demo')
# Insert six segments across three documents
ret = collection.insert([
('1', np.random.rand(4), {'document_id': 'paper-01', 'chunk_id': 1, 'content': 'xxxA'}),
('2', np.random.rand(4), {'document_id': 'paper-01', 'chunk_id': 2, 'content': 'xxxB'}),
('3', np.random.rand(4), {'document_id': 'paper-02', 'chunk_id': 1, 'content': 'xxxC'}),
('4', np.random.rand(4), {'document_id': 'paper-02', 'chunk_id': 2, 'content': 'xxxD'}),
('5', np.random.rand(4), {'document_id': 'paper-02', 'chunk_id': 3, 'content': 'xxxE'}),
('6', np.random.rand(4), {'document_id': 'paper-03', 'chunk_id': 1, 'content': 'xxxF'}),
])
assert retStep 2: Query with grouping
ret = collection.query_group_by(
vector=[0.1, 0.2, 0.3, 0.4],
group_by_field='document_id', # Group results by document
group_count=2, # Return at most 2 groups
group_topk=2, # Return at most 2 segments per group
)
if ret:
print('query_group_by success')
print(len(ret))
print('------------------------')
for group in ret:
print('group key:', group.group_id)
for doc in group.docs:
print(' -', doc)The output groups results by document_id. Each group lists its segments in descending score order:
query_group_by success
4
------------------------
group key: paper-01
- {"id": "2", "fields": {"document_id": "paper-01", "chunk_id": 2, "content": "xxxB"}, "score": 0.6807}
- {"id": "1", "fields": {"document_id": "paper-01", "chunk_id": 1, "content": "xxxA"}, "score": 0.4289}
group key: paper-02
- {"id": "3", "fields": {"document_id": "paper-02", "chunk_id": 1, "content": "xxxC"}, "score": 0.6553}
- {"id": "5", "fields": {"document_id": "paper-02", "chunk_id": 3, "content": "xxxE"}, "score": 0.4401}Two groups are returned (paper-01 and paper-02), each with up to two segments. paper-03 is excluded because group_count=2 limits the total number of groups returned.
Limitations
| Constraint | Details |
|---|---|
group_by_field field type | Must reference a field defined in fields_schema at collection creation. Schema-free fields are not supported. See Create a collection and Schema-free. |
group_count and group_topk are best-effort | The actual number of groups or segments returned may be fewer than the values you set. DashVector prioritizes group_count — it fills as many groups as possible first, then fills segments within each group up to group_topk. |
| Maximum values | group_count max: 64. group_topk max: 16. Higher values increase index scan workload and extend API response time. |
What's next
Grouped document search — full API reference for
query_group_byCreate a collection — define
fields_schemafor grouped searchSchema-free — understand schema-defined vs. schema-free fields