Search — API Reference
HTTP-only, on the control plane. There is no Search RPC anywhere on the K3 proto surface — it retired with the Milvus read path, and the package that used to carry it (dodil.data.vector.v1) no longer exists. The route is a thin dispatcher over the per-modality *_embedding_search Scriptum templates: each template embeds the query, searches the matching collections, and k3-api RRF-merges the ranked lists across collection groups.
The request has no proto message. The response does — dodil.data.common.v1.SearchResponse, carrying SearchResult and CollectionSearchStatus.
| Route | Content-Type | Body limit |
|---|---|---|
POST /:bucket/search/vector | application/json — text query | 1 MiB |
POST /:bucket/search/vector | multipart/form-data — file query (image / audio / video) | 50 MiB |
Retrieval and reranking are owned by the search template, not the caller — there are no caller-selectable search modes and no rerank flag that changes behavior. The searchMode, rerank and minScore fields are still parsed for wire compatibility but do not feed the search or filter the results.
Searching by a pre-embedded vector? That’s a data-plane operation — see Pre-embedded KNN below. Sending a
vectorfield to this route returnsUNIMPLEMENTED: pre-embedded vector search retired with Milvus (#83) — pending the tabled KNN read-path.
Known limitation — this route returns no results today. The
vector_store_searchScriptum native tool still targets the decommissioned Milvus backend and has not been repointed at the tabled data plane. The dispatch shape is correct and the route answers200, but every participating collection comes back with a non-emptyfailReasonat the search step until that tool moves. Tracked indodil-k3/docs/vector_search_readpath_followup.md. Until then, use the data-plane KNN paths.
Text query (JSON)
dodil data
dodil data search "what is multi-head attention" \
-b kb-prod \
--table docs \
--top-k 10--table (alias --collection) is repeatable and maps onto the collectionNames wire field. -b/--bucket is required.
| Field | Type | Default | Description |
|---|---|---|---|
text | string | — | The query. Required for JSON requests (or send vector, which returns UNIMPLEMENTED). |
collectionNames | string[] | [] | Pin the search to these collections. Empty = all eligible collections in the bucket. Names that match nothing return NOT_FOUND. (Legacy singular collectionName also accepted; snake_case spellings are accepted too.) |
topK | int | 10 | Result count after the merge. <= 0 is coerced to 10. |
minScore | float | 0 | Parsed but not applied. Kept for wire compatibility; no score floor is enforced today. |
File query (multipart)
For collections built from a visual_embedding_index / face_embedding_index / object_embedding_index template, search by uploading a file — K3 stages it at a temp S3 key, the search template embeds it, then searches:
curl -sS -X POST "https://api.data.dodil.io/kb-prod/search/vector" \
-H "Authorization: Bearer $DODIL_TOKEN" \
-F "file=@./example-bag.jpg;type=image/jpeg" \
-F "top_k=20" \
-F "collection_name=product-images"Multipart fields: file (required), text (optional accompanying text, used for reranking), top_k, collection_name / collection_names (comma-separated), plus the inert search_mode / rerank. The file’s content type routes it to the right modality (image/*, audio/*, video/* → visual; text/x-* or application/javascript → code; else text). Max upload: 50 MiB.
The upload is staged at .k3-tmp/search/{uuid}/{filename} in the bucket, handed to the template as a 5-minute presigned SigV4 URL, and reaped by a drop guard when the search returns.
How dispatch works
- K3 loads the bucket’s
kind='vector'destinations and filters by request shape — explicitcollectionNameswin; a file query auto-filters to its modality; else all collections participate. - Collections that are not
active, or that have no bound search pipeline (store_entities.search_pipeline_id), are dropped with awarnings[]entry. - The survivors are grouped by
(embed_model, dimensions, embedding_type)— one search-template thread per group embeds the query once and fans out to every member. - Ranked lists from the groups are RRF-merged (k=60) into the final
topK. A single group is a pass-through, trimmed totopK— no RRF rescoring happens.
The template owns everything inside a group — retrieval and any reranking are the template’s business, not request knobs. There is no metadata pre-filter: k3-api sends the template a hardcoded empty filter.
Hybrid is not live. Every shipping
*_embedding_*template pinsenable_bm25 = falseand notes that hybrid is unavailable on tabled — the ingest tool hard-errors if it is set true. The plane has no BM25 implementation at all, and while an RRF primitive (k=60) exists indodil-tables, nothing calls it. Searches today are dense-only. The RRF merge described above is k3-api’s own, across collection groups, and is unrelated.
Response
{
"results": [
{
"object": { "bucket": "kb-prod", "key": "papers/attention.pdf" },
"score": 0.0163,
"metadata": { "collection": "docs" },
"chunkId": "papers/attention.pdf#c4",
"chunkIndex": 4,
"source": "SEARCH_SOURCE_VECTOR",
"content": "Multi-head attention lets the model..."
}
],
"pagination": null,
"tookMs": "812",
"searchModeUsed": "vector",
"warnings": [],
"collectionStatuses": [
{ "collection": "docs", "embeddingCompleted": true, "searchCompleted": true, "failReason": "" }
]
}results[]— merged across groups, ranked by RRF score.objectpoints back to the source S3 object; the key is derived from the chunk’sartifact_id({source_short}:{object_key}).metadatacarries the originatingcollectionname.chunkIndexis parsed off the#c<n>suffix ofchunkId.sourceis alwaysSEARCH_SOURCE_VECTOR, andsearchModeUsedis always the literal"vector"— neither reflects whether the template ran hybrid retrieval internally.collectionStatuses[]— one entry per participating collection; a non-emptyfailReasonexplains why a collection contributed nothing. This is the first place to look when a search returns fewer results than expected.warnings[]— non-fatal issues (e.g. a skipped collection with no search pipeline).paginationis never populated by this route.
Pre-embedded KNN — data plane
The control-plane route will not take a vector. KNN over vectors you already hold is a tabled data-plane operation, reached with a stock client:
dodil data
dodil data vsearch \
-b kb-prod \
-t chunks \
--column embedding \
--vector "0.12,0.03,…" \
--metric cosine \
--top-k 10--text embeds client-side via dodil ignite models embed first (default model arctic-embed-m-v2) and then runs the same KNN. --metric accepts cosine | euclidean | dot. There is no -c shorthand — --collection is a long alias for --table.
Endpoints, credentials and the Qdrant / Pinecone SDK forms: Connect & wire adapters.
When the request fails
| Symptom | Cause | Fix |
|---|---|---|
INVALID_ARGUMENT: either 'text' or 'vector' field is required | Empty JSON body | Send text (or a multipart file) |
UNIMPLEMENTED: pre-embedded vector search retired with Milvus (#83) | Pre-embedded search is not served by this route | Use Pre-embedded KNN on the data plane |
NOT_FOUND: no collections matched: … | Every name in collectionNames is absent from the bucket | Check dodil data vector collection list -b $BUCKET |
RESOURCE_EXHAUSTED: too many concurrent search requests | Per-pod concurrency limit protecting Scriptum | Back off and retry |
FAILED_PRECONDITION: bucket SA: … | The bucket’s service account was never provisioned (IAM was down at bucket create) | Retry — the search path self-heals the provisioning on the next call |
Empty results + failReason on every collection | Expected today — vector_store_search still targets Milvus (see the limitation note above) | None on the caller’s side; use the data-plane KNN paths |
Empty results + a warnings[] entry naming a collection | That collection has no bound search pipeline | Re-create it from a *_embedding_index template so the *_embedding_search counterpart is spawned |
See also
- Collections — the collections you search
- Connect & wire adapters — KNN by vector, and all vector writes, on the data plane
- CLI →
dodil data vsearch— embed-then-KNN from the CLI - Recipes → Hybrid Search — template-owned hybrid + RRF end-to-end
- Recipes → Multimodal Search — file queries end-to-end