When you run a vector search, you usually send one or more query vectors.
But sometimes your “query” is already stored in the same collection—for example:
- A user is viewing a product page and you want “similar products” for that product.
- A user is reading a document and you want “related documents” for that document.
- You want “neighbors” of a specific embedding you already inserted.
In these cases, it’s wasteful to:
- fetch the vector back from the database, then
- send it again as a query vector.
Primary-key search solves this by letting you search using the entity primary keys (ids). VBase fetches the vectors internally, then performs the similarity search.
Why this matters
Primary-key search helps you:
- Reduce round trips (no “get vector then search” flow)
- Avoid sending large vectors over the network
- Simplify app logic (especially in “similar items” experiences)
Primary keys are used only to locate the query vectors. They are not used as a filter.
Requirements
- Your target collection must have a primary key field.
- This capability depends on your backend version (Milvus 2.6.9+).
Limits and restrictions
Keep these rules in mind:
- You must provide either
idsorquery_vectors—not both. - If any ID does not exist (or is the wrong type/format), the request will error.
- Primary-key search works with most vector field types. It does not apply to sparse vector fields derived from text (for example BM25-style fields).
- You can combine primary-key search with filtered search, range search, and grouping search.
- It does not apply to hybrid search or search iterators.
- If you’re searching with embedding lists, you still need to provide query vectors (because the query is constructed from multiple embeddings).
Connect to VBase
If you already have a vbase connection in your app, you can skip this section.
from dodil import Client
from dodil.vbase import VBaseConfig
# Python 3.10+
c = Client(
service_account_id="...",
service_account_secret="...",
)
vbase = c.vbase.connect(
VBaseConfig(
host="vbase-db-<id>.infra.dodil.cloud",
port=443,
scheme="https",
db_name="db_<id>",
)
)Example 1: Basic primary-key search
This is the “similar items” case: pass the IDs of existing entities, and ask VBase for the nearest neighbors.
res = vbase.search(
collection_name="quick_setup",
anns_field="vector",
ids=[551, 296, 43],
limit=3,
search_params={"metric_type": "IP"},
)
for hits in res:
for hit in hits:
print(hit)What you get back
You’ll receive the same kind of search results you’d get from a normal vector search:
- matched entity IDs
- similarity score / distance
- optional
output_fields(if requested)
Example 2: Filtered search using primary keys
Use this when you want “similar items” and you want to constrain results with metadata.
res = vbase.search(
collection_name="my_collection",
ids=[551, 296, 43],
filter='color like "red%" and likes > 50',
output_fields=["color", "likes"],
limit=3,
)Example 3: Range search using primary keys
Range search returns results that fall within a specific distance/similarity window.
res = vbase.search(
collection_name="my_collection",
ids=[551, 296, 43],
limit=3,
search_params={
"params": {
"radius": 0.4,
"range_filter": 0.6,
}
},
)Example 4: Grouping search using primary keys
Grouping search is useful when many entities belong to the same “parent” and you want diversified results.
For example, if multiple chunks belong to the same document, you might want at most one hit per doc_id.
res = vbase.search(
collection_name="my_collection",
ids=[551, 296, 43],
limit=3,
group_by_field="docId",
output_fields=["docId"],
)Practical recommendations
- Use primary-key search for “similar to this item” experiences.
- Keep
idslists small and focused (it’s still a search request per query entity). - If you need to search “similar to a brand-new query” (text/image the user just typed/uploaded), you should embed it first and run a normal vector search.