Vectors, geo and RAG search

Index a collection's vector, geo, or sparse fields, then query them with k-nearest, bounding-box, and RAG search operators.

Docs menu
{"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 returns 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 returns 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, and rag.collection.delete (DELETE /v1/{collection}/_rag) removes it. rag.get (GET /v1/_rag) reads the app configuration. rag.embed (POST /v1/{collection}/_rag/embed) embeds records without a vector, while rag.reembed (POST /v1/{collection}/_rag/re-embed) embeds every record again after a provider or model change. rag.drift (GET /v1/{collection}/_rag/drift) reports records whose stored embedding no longer matches the current configuration.

$search searches the field configured by rag.collection.set. It supports vector, sparse, and hybrid modes; hybrid fuses dense, sparse, and text results:

{"$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.