Skip to content

QQL query expressions

QUERY is the universal retrieval statement. Pick the expression that matches the input and result shape you need, then apply the same tail clauses for filters, routing, payload selection, and pagination. A top-level query always names its collection with FROM.

Use text when an embedder resolves user language at execution time. Use a vector when your application already generated an embedding. QUERY POINT uses a stored point as the similarity input; QUERY POINTS retrieves exact records and does not accept search clauses such as WHERE or LIMIT.

QQLText, vector, and stored-point inputTry in playground
QUERY 'vector database' FROM docs USING dense LIMIT 10;
-- Explicit vector input
QUERY VECTOR [0.1, 0.2, 0.3] FROM docs USING dense LIMIT 10;
-- Implicit vector input (VECTOR keyword is optional for array literals)
QUERY [0.1, 0.2, 0.3] FROM docs USING dense LIMIT 10;
QUERY POINT 42 FROM docs USING dense LIMIT 10;
QUERY POINTS (1, 2, 'point-a') FROM docs;

TEXT and IMAGE accept an optional MODEL and an opaque OPTIONS dict that passes through to the inference service. OBJECT is the custom inference payload with the same options. Parenthesized OBJECT ({...}) parses but formats to the bare object.

QQLInference options and object inputTry in playground
QUERY TEXT 'hi' MODEL 'm' OPTIONS {temperature: 0.5} FROM docs USING dense LIMIT 1;
QUERY IMAGE 'https://x/y.jpg' MODEL 'clip' OPTIONS {size: 512} FROM docs USING img LIMIT 1;
QUERY OBJECT {a: 1} MODEL 'm' OPTIONS {k: true} FROM docs USING dense LIMIT 5;
NeedExpressionWhy
Similar documents from one inputQUERY TEXT, VECTOR, or POINTStandard nearest-neighbor search
Combine semantic and lexical candidatesQUERY HYBRID or USING HYBRIDDense and sparse result sets are fused
Bias results toward examplesQUERY RECOMMENDPositive and negative point IDs define the intent
Diversify similar resultsQUERY MMRRe-ranks a candidate set by relevance and diversity
Join multiple retrieval stagesCTEs + QUERY FUSIONExplicit prefetch and fusion pipeline
Group results by a payload keyGROUP BYReturns bounded groups rather than a flat list

Use the front form when you want the dense and sparse vector names explicit. Use the tail form when schema defaults resolve the names. Both forms are part of the v1 conformance corpus.

QQLHybrid dense and sparse fusionTry in playground
QUERY HYBRID TEXT 'vector database' DENSE dense SPARSE sparse FUSION RRF
FROM docs
LIMIT 10;
QUERY HYBRID TEXT 'search query' FUSION RRF FROM docs LIMIT 10;

RRF is a safe default when candidate score scales differ. Choose DBSF when you want distribution-based score normalization. See Hybrid retrieval for multi-stage tuning.

USING <name> answers which vector; the optional AS <kind> answers what kind of input. The name and the role are independent:

USING <name> [AS DENSE | AS SPARSE | AS MULTI | AS MULTIVECTOR]

AS DENSE and AS SPARSE declare the role of the query target: text is then embedded into that role, and a structural vector input must agree with the declaration. AS MULTI and AS MULTIVECTOR are synonyms that mark a dense multivector target (ColBERT-style late interaction) — multivector is not a third kind beside dense and sparse; the role stays dense and text is embedded into [[f32, ...], ...] multi-dense shape. When AS is omitted, the role is resolved from the collection schema at plan time. An explicit structural vector input that contradicts a declared role fails with QQL-PLAN-VECTOR-KIND.

CTEs make candidate generation visible. A CTE can omit FROM and inherit the outer collection. PREFETCH names must resolve to CTEs.

QQLDense and sparse candidates fused with RRFTry in playground
WITH
dense_candidates AS (QUERY 'database internals' USING dense LIMIT 100),
sparse_candidates AS (QUERY 'database internals' USING sparse LIMIT 100)
QUERY FUSION RRF
FROM docs
PREFETCH (dense_candidates, sparse_candidates)
LIMIT 10;
QQLRecommendation and diversified searchTry in playground
QUERY RECOMMEND POSITIVE (1, 2) NEGATIVE (3) STRATEGY best_score
FROM products
USING dense
LIMIT 10;
QUERY MMR TEXT 'vector database' DIVERSITY 0.7 CANDIDATES 100
FROM docs
USING dense
LIMIT 10;

DIVERSITY must be between 0 and 1; CANDIDATES must be positive. Use grouping when the UI needs several results per payload category.

QQLGrouped results with lookupTry in playground
QUERY 'incident'
FROM runbooks
USING dense
GROUP BY service SIZE 3 LOOKUP FROM services
WITH PAYLOAD INCLUDE (title, service)
LIMIT 10;

LOOKUP FROM on GROUP BY carries payload and vector selectors for the joined group data. Prefetch LOOKUP FROM additionally routes one leg with an optional vector and shard key.

QQLLookup selectors and prefetch routingTry in playground
QUERY 'news'
FROM docs
GROUP BY topic SIZE 5 LOOKUP FROM topics WITH PAYLOAD INCLUDE (title) WITH VECTOR (dense)
LIMIT 20;
WITH a AS (QUERY TEXT 'x' FROM docs USING dense LIMIT 50)
QUERY FUSION RRF
FROM docs
PREFETCH (a LOOKUP FROM docs2 VECTOR dense SHARD 'acme')
LIMIT 10;

QUERY CONTEXT pairs positive and negative inputs that define a region; QUERY DISCOVER adds a TARGET and shifts results toward it. Each positive/negative/target item is a full query input — use POINT for a point ID, TEXT or VECTOR otherwise.

QQLContext pairs and a discover targetTry in playground
QUERY CONTEXT (POSITIVE POINT 1 NEGATIVE POINT 2, POSITIVE POINT 3 NEGATIVE POINT 4)
FROM docs
LIMIT 10;
QUERY DISCOVER TARGET 'search' CONTEXT (POSITIVE POINT 1 NEGATIVE POINT 2)
FROM docs
LIMIT 10;

QUERY RELEVANCE FEEDBACK refines a TARGET with scored feedback pairs and the NAIVE strategy, whose a, b, and c coefficients shape the adjusted query vector. Feedback items pair a query input with a relevance weight.

QQLNaive relevance feedbackTry in playground
QUERY RELEVANCE FEEDBACK TARGET 'search' FEEDBACK ((VECTOR [0.1, 0.2], 1.0), (VECTOR [0.3, 0.4], -1.0)) STRATEGY NAIVE (a = 1.0, b = 0.5, c = 0.2)
FROM docs
LIMIT 10;

QUERY ORDER BY sorts by a payload field (ASC or DESC), and QUERY SAMPLE RANDOM draws a random page. Neither expression accepts USING or PREFETCH — there is no similarity input to resolve. ORDER BY accepts an optional START FROM paging origin: an integer, float, datetime string, or placeholder that resumes ordering from that payload value.

QQLOrdered and random accessTry in playground
QUERY ORDER BY price ASC FROM products WHERE category = 'electronics' LIMIT 10;
QUERY ORDER BY created_at DESC START FROM '2024-01-01T00:00:00Z' FROM docs LIMIT 20;
QUERY SAMPLE RANDOM FROM docs WITH VECTOR false LIMIT 10;

QUERY POINTS retrieves exact records by ID and is deliberately minimal: only SHARD, WITH PAYLOAD, and WITH VECTOR clauses are allowed. Filtering, scoring, and paging clauses are invalid — add a WHERE on a QUERY TEXT instead when you need a filtered search.

QUERY RERANK is late-interaction MaxSim reranking. It always requires USING (a dense or multivector target), a MODEL, and a non-empty PREFETCH.

QQLLate-interaction rerank of dense candidatesTry in playground
WITH
candidates AS (QUERY 'vector database' USING dense LIMIT 100)
QUERY RERANK TEXT 'vector database' MODEL 'reranker-v1'
FROM docs
USING colbert AS DENSE
PREFETCH (candidates)
LIMIT 10;

QUERY CROSS RERANK (v1.2) instead scores (query, document text) pairs with a cross-encoder. ON FIELD names the payload key holding document text (default text). It takes no USING vector and requires a host embedder with pair scoring; the scoring runs client-side and reorders the prefetched candidates.

QQLCross-encoder reranking on a payload fieldTry in playground
WITH
candidates AS (QUERY 'vector database' USING dense LIMIT 100)
QUERY CROSS RERANK TEXT 'vector database' MODEL 'bge-reranker-base' ON FIELD abstract
FROM docs
PREFETCH (candidates)
LIMIT 10;

Query-level PARAMS accept search controls such as hnsw_ef, exact, acorn, quantization, timeout, and consistency. For sparse retrieval on Qdrant 1.19+, set a per-query IDF corpus:

FormMeaning
idf = 'global'Use the collection-wide IDF statistics
idf = WHERE <filter>Restrict IDF to points matching that QQL filter

A malformed idf value fails validation with QQL-VALIDATION-IDF. Isolation stays on statement WHERE / host inject_filter. IDF only scopes BM25 rarity.

QQLPer-query sparse IDF corpusTry in playground
QUERY 'search'
FROM docs
USING sparse
PARAMS (idf = 'global')
LIMIT 10;
QUERY 'search'
FROM docs
USING sparse
PARAMS (idf = WHERE status = 'active')
LIMIT 10;
QUERY 'search'
FROM docs
USING sparse
WHERE tenant_id = 'acme'
SHARD 'acme'
PARAMS (idf = WHERE tenant_id = 'acme')
LIMIT 10;
QUERY 'search'
FROM docs
USING sparse
WHERE tenant_id = 101
SHARD 101
PARAMS (idf = WHERE tenant_id = 101)
LIMIT 10;

Shard keys bind like any other placeholder: SHARD :tenant takes its value from SDK params, with strings routed as keywords and non-negative integers as numbers.

QQL queries support parameterized templates with type-safe client-side binding:

  • Named Placeholders: :name (e.g. :category, :lim)
  • Positional Placeholders: ? (mapped sequentially to argument lists)

A template like QUERY TEXT :q FROM docs WHERE category = :cat LIMIT :lim is inert until values are bound — unbound placeholders fail at the plan gate (QQL-BIND-MISSING-PARAM) before any request leaves the client, on every binding path. Here is the same statement with values bound:

QQLA bound parameterized queryTry in playground
QUERY TEXT 'cardiology'
FROM docs
WHERE category = 'medical'
LIMIT 10;

Bind client-side with the Python bind / Stmt.bind, Node bind / Stmt.bind, or WASM bind helpers, or pass params straight to Client.execute — see the Python, Node.js, and WebAssembly SDK pages. Rust uses the typed bind_named / bind_positional twins in qql_core::params.

When parameters are bound at runtime via the SDKs (bind or Client.execute(..., params=...)), the template evaluates to a fully-formed, executable QQL statement:

QQLBound query executionTry in playground
QUERY 'chest pain'
FROM docs
WHERE category = 'cardiology' AND rating >= 4.5
LIMIT 10;

Nested dictionaries expand to dotted keys: {"loc": {"lat": 1.0, "lon": 2.0}} binds :loc.lat and :loc.lon, and flat dotted keys work identically. For multi-statement batches, pass params as a list with one entry per statement (the length must match the statement count). Substitution failures carry stable QQL-BIND-* codes — mixed placeholder styles (QQL-BIND-MIXED-STYLE), missing values (QQL-BIND-MISSING-PARAM), extra positional values (QQL-BIND-UNUSED-PARAMS), and type mismatches (QQL-BIND-TYPE-MISMATCH) — see the error code reference.

Colons in compact dictionary syntax (e.g. {a:b}, {'a':b}) are preserved as key-value separators and not treated as placeholders.

For formula scoring and decay functions, continue with Formula scoring.