{"where": {"embedding": {"$knn": {"vector": [0.1, 0.2, 0.3], "k": 10}}}}
Indexing a vector field
Declare a vector index before querying it, with index.create (kind: "vector", POST /v1/_indexes):
| Field | Type | Default | Description |
|---|---|---|---|
collection |
string | required | The collection |
field |
string | required | The vector field |
dims |
integer | required (17-4096) | The vectors' dimensions, fixed once set |
metric |
string | l2 |
l2 (squared Euclidean) or cosine |
bands |
array of 3 integers | chosen by dims |
Dims coded at 3, 2 and 1 levels |
route |
integer | 128 (at most dims) |
Dims the index routes clusters on |
clusters |
integer | grows with the vectors | A fixed cluster count |
curl -s -X POST $SUBSTRIO_URL/v1/_indexes -H "Authorization: Bearer $SUBSTRIO_KEY" \
-H 'content-type: application/json' \
-d '{"kind":"vector","collection":"docs","field":"embedding","dims":384,"metric":"cosine"}'
A vector of other dimensions is refused once the index is set.
Vector queries
{"where": {"embedding": {"$knn": {"vector": [0.1, 0.2, 0.3], "k": 10}}}}
{"where": {"embedding": {"$box": {"min": [0.0, 0.0, 0.0], "max": [1.0, 1.0, 1.0]}}}}
$knn answers the k nearest vectors to vector (default k 10) by the
field's configured metric. $box matches vectors inside the per-dimension
min/max bounds. For a sparse vector field, $sparseKnn takes the same
k, with vector as {"indices": [...], "values": [...]}.
Geo queries
{"where": {"location": {"$near": {"lat": 40.7128, "lng": -74.0060, "k": 10}}}}
{"where": {"location": {"$geoWithin": {"$sphere": {"center": {"lat": 40.7128, "lng": -74.0060}, "radius": 5000}}}}}
$near answers the k nearest documents to a lat/lng point.
$geoWithin with $box (sw/ne corners) or $sphere (center and
radius in meters) matches points inside a region. See
query-language.md for the full operator set.
RAG configuration
rag.collection.set (PUT /v1/{collection}/_rag) declares how a collection
embeds and searches:
| Field | Type | Default | Description |
|---|---|---|---|
source_field |
string | required | The text field to embed |
provider.type |
string | required | openai, anthropic, ollama, groq, vllm, jina or hash |
provider.model |
string | provider default | The embedding model |
provider.endpoint |
string | provider default | Override the provider's URL |
provider.api_key_ref |
string | none | A secret reference holding the API key |
provider.dimensions |
integer | provider default | Embedding size |
vector_field |
string | source_field + _vector |
Where the embedding is stored |
search_mode |
string | vector |
vector, sparse or hybrid |
auto_chunk |
boolean | false |
Split long text before embedding |
chunk_size, chunk_overlap |
integer | provider default | Chunk sizing when auto_chunk is on |
hash is a deterministic embedding with no semantic meaning: useful for
repeatable tests, not for production relevance.
curl -s -X PUT $SUBSTRIO_URL/v1/docs/_rag -H "Authorization: Bearer $SUBSTRIO_KEY" \
-H 'content-type: application/json' \
-d '{"source_field":"body","provider":{"type":"openai","model":"text-embedding-3-small"}}'
rag.collection.get (GET /v1/{collection}/_rag) reads the configuration
back; rag.collection.delete (DELETE /v1/{collection}/_rag) removes it;
rag.get (GET /v1/_rag) reads the app's own configuration. rag.embed
(POST /v1/{collection}/_rag/embed) embeds records that have no vector yet;
rag.reembed (POST /v1/{collection}/_rag/re-embed) embeds every record
again, for a changed provider or model. rag.drift (GET /v1/{collection}/_rag/drift) reports records whose stored embedding no
longer matches the current configuration.
RAG search
$search runs vector, sparse and text search together and fuses the
results, scoped to the field rag.collection.set configured:
{"$search": {"query": "refund policy", "mode": "hybrid", "k": 20}}
| Field | Type | Default | Description |
|---|---|---|---|
query |
string | required | The search text |
mode |
string | vector |
vector/dense (embedding only), sparse (sparse-only), anything else (hybrid: dense, sparse and text fused) |
k |
integer | 50 |
Candidates considered before limit/skip apply |
decay |
object | none | Scores recent documents higher by a configured field |
strict_model |
boolean | false |
Only match vectors from the current embedding model |
boost_feedback |
boolean or object | false |
Re-rank using recorded search feedback |
$search can take a bare string ({"$search": "refund policy"}) in place
of the object form, and composes with a regular where clause.
ai.cross_search (POST /v1/{collection}/_cross_search) runs one query
across several vector fields of the same collection and merges the
results, k candidates per field:
curl -s -X POST $SUBSTRIO_URL/v1/docs/_cross_search -H "Authorization: Bearer $SUBSTRIO_KEY" \
-H 'content-type: application/json' \
-d '{"query":"refund policy","fields":["title_vector","body_vector"],"k":10}'
search.analytics (GET /v1/{collection}/_analytics/search) reads query
volume and result counts for a collection's search traffic.