qql-core 0.4.1

Parser, typed AST, validation, and transformations for the Qdrant Query Language
Documentation

qql-core

Transport-free QQL frontend: lexer, parser, typed AST, validation, inject_filter, and explain. No I/O, no Qdrant JSON.

Canonical grammar: language/v1/grammar.pestqql-grammar-gen → checked-in grammar/ (do not edit by hand).

Proposition

Own the language surface for every SDK. Hosts and agents parse here, inject policy here, then hand Stmt to qql-plan / runtime.

Statement surface

Kind Examples
Query QUERY (nearest, hybrid, formula, recommend, RERANK, CROSS RERANK, …), CTEs
Retrieval SCROLL, COUNT, FACET
DML UPSERT, DELETE, payload/vector updates (DELETE PAYLOAD, …)
DDL CREATE/ALTER/DROP COLLECTION, indexes, CREATE/DROP/SHOW SHARD KEY
Meta SHOW COLLECTIONS / SHOW COLLECTION
Quotas SHOW QUOTAS / SET QUOTA (…) (Qdrant ≥ 1.19; REST at execute time)

Clause order (QUERY)

QUERY <expr> FROM <coll>
  [USING HYBRID … | USING <vec> [AS DENSE|SPARSE|MULTI]]
  [PREFETCH (…)] [WHERE …] [SHARD '…'] [PARAMS (…)]
  [SCORE THRESHOLD n] [GROUP BY …] [WITH PAYLOAD|VECTOR …]
  [LIMIT n] [OFFSET n]

SHARD KEY vs SHARD

Form Role
CREATE SHARD KEY 'acme' ON COLLECTION c DDL — define custom partition
… SHARD 'acme' DML — route this request
… SHARD 101 DML — route to a numeric partition (never coerced to "101")

Routing field after parse: stmt.set_shard_key(Some("acme".into()))
(same AST field; no inject_shard_key).

PARAMS (selected)

Body search params: hnsw_ef, exact, acorn, max_selectivity, quantization, idf ('global' or WHERE <filter> for sparse IDF, Qdrant ≥ 1.19), …
Request-level (REST query string / gRPC fields): timeout, consistency.

Filters (selected, Qdrant ≥ 1.19)

WHERE title MATCH PREFIX 'Comp'   -- keyword prefix (needs prefix=true index)
WHERE SLICE (4, 1)                -- hash(id) % total == index; total≥1, index<total

Typed config enums

Type Values Use
MemoryPlacement cold / cached / pinned `WITH HNSW
VectorDatatype float32 / float16 / uint8 / turbo4 dense WITH VECTOR (datatype = …); sparse rejects turbo4
CREATE COLLECTION docs (
  dense VECTOR(384, COSINE)
    WITH VECTOR (memory = 'cached', datatype = 'turbo4')
) WITH HNSW (memory = 'cold')
  WITH PARAMS (payload_memory = 'cold');

CREATE INDEX ON COLLECTION docs FOR title TYPE keyword
  WITH (prefix = true, memory = 'cached');

SHOW QUOTAS;
SET QUOTA (enabled = true, max_resident_memory_percent = 80) WAIT true;

API

use qql_core::ast::{inject_filter, ComparisonOp, Stmt, Value};
use qql_core::parser::Parser;

let mut stmt = Parser::parse(
    "QUERY TEXT 'hello' FROM docs USING dense LIMIT 5"
)?;

// Isolation (recurses CTEs / prefetches). Fail-closed on DDL / SHOW /
// UPDATE VECTOR, and on UPSERT unless Eq on a non-id payload field.
// ComparisonOp::parse_inject_op is ASCII-case-insensitive (`EQ`, ` Gt `).
inject_filter(
    &mut stmt,
    "tenant_id",
    ComparisonOp::Eq,
    Value::Str("org_99".into()),
)?;

// Optional routing (custom sharding)
stmt.set_shard_key(Some("org_99".into()));
// Prefer authoring: ... SHARD 'org_99' LIMIT 5
Feature Role
serde AST serialize
json Value from/to JSON
std std::error::Error

Docs

Test

cargo test -p qql-core