qql-cli
CLI + REPL for QQL: remote Qdrant (REST/gRPC), convert, dump, migrate, doctor, optional edge.
Install
The CLI command is always qql, available in two installation editions:
- Standard: Lightweight (~15MB), remote Qdrant daily driver with REST, gRPC, built-in transparent traffic recorder (
qql record), AST converter (qql convert), collection migrator (qql migrate), dump, REPL, formatter, and linter. - Full: Includes all Standard features plus local client-side FastEmbed ONNX inference and the in-process
qdrant-edgeembedded database (--features full). (On GitHub Releases, the prebuilt archives are namedqql-*.tar.gzandqql-full-*.tar.gz; both install the singleqqlexecutable).
Quick Install (Prebuilt Binaries)
# Linux / macOS (Standard)
|
# Linux / macOS (Full: with ONNX embeddings & embedded edge)
|
# Windows PowerShell (Standard)
|
# Windows PowerShell (Full: with ONNX embeddings & embedded edge)
& ()))
Install from crates.io
# Standard: REST + gRPC + record + convert + migrate (matches qql release archives)
# Full: Standard + local FastEmbed ONNX embeddings + in-process qdrant-edge (matches qql-full-* archives; archive contains qql exe)
# REST only (smallest footprint)
# Local edge backend only, no server (`--edge`, `qql edge`, `qql config edge`)
# Local zero-server ONNX embeddings only (for remote Qdrant and migrations)
# From a local checkout instead
Check what's installed: qql version reports the edition ("standard" or "full") and enabled features array.
Binary: target/release/qql (or ~/.cargo/bin/qql for installs).
Commands
| Command | Role |
|---|---|
qql lint [path] |
Offline syntax + plan check, --fix autofix, --json for CI |
qql run "…" |
One statement (--json, --quiet, --param, --params-file) |
qql run file.qql |
Script (--stop-on-error) |
qql explain "…" |
Plan without Qdrant |
qql repl |
REPL (alias: connect) |
qql doctor ["…"] |
Health + embed host snapshot, or 5-stage query triage (alias: check) |
qql setup |
Connection wizard → ~/.qql/config.json (0600) |
qql config … |
Persistent settings (show, get, set, path, edge) |
qql convert [file.json] |
REST JSON → QQL |
qql dump <coll> out.qql |
Export collection as QQL (custom-sharded collections emit CREATE SHARD KEY + SHARD-routed batches; replay with qql run) |
qql migrate <coll> --to <name> |
Version-agnostic collection migration (schema + points) |
qql record |
Transparent REST recorder → JSONL + QQL (built-in) |
qql --edge … |
Use configured local edge backend |
qql edge optimize <coll> |
Run qdrant-edge optimizers (merge segments, build HNSW/sparse indexes) |
qql edge bootstrap <coll> --from <url> |
Seed a local edge collection from a remote shard snapshot |
qql version |
Version |
# Guide: /docs/operations/cluster-migration/ (migrate)
# Guide: /docs/operations/backup-restore/ (dump)
# Cross-version / cross-cluster migrate (schema + points, not snapshots)
# Cluster quotas (Qdrant ≥ 1.19, REST only — use :6333, not gRPC :6334)
Configuration
Remote HTTP Embedder (Environment Variables)
| Variable | Default | Role |
|---|---|---|
QDRANT_URL |
http://localhost:6333 |
REST/gRPC URL (:6334 selects gRPC when enabled) |
QDRANT_API_KEY |
— | Auth |
EMBED_URL |
— | OpenAI-compatible embeddings endpoint |
EMBED_KEY |
— | Bearer token for the embedding endpoint |
EMBED_MODEL |
all-minilm:l6-v2 |
Remote embedding model ID |
EMBED_DIM |
384 |
Remote embedding vector dimension |
MULTI_EMBED_URL / MULTI_EMBED_KEY / MULTI_EMBED_MODEL / MULTI_EMBED_DIM |
— | Multi/ColBERT embedding endpoint |
IMAGE_EMBED_URL / IMAGE_EMBED_KEY / IMAGE_EMBED_MODEL / IMAGE_EMBED_DIM |
— | Image/CLIP embedding endpoint |
RERANK_URL / RERANK_KEY / RERANK_MODEL |
— | Cross-encoder reranking endpoint |
Sparse document encoding is always local (qdrant/bm25-compatible), even on
the remote path. Tune it in ~/.qql/config.json via bm25_k1 / bm25_b /
bm25_avg_len (defaults 1.2 / 0.75 / 256; write-path only; invalid values
fail closed with QQL-VALIDATION-CONFIG). It applies only when an HTTP embedder
is configured (EMBED_URL), since TEXT resolution requires an embedder.
Local Edge Backend (qql config edge)
Edge-specific variables start with QQL_EDGE_; the EMBED_*, MULTI_EMBED_*, and
IMAGE_EMBED_* variables above are shared with the HTTP embedder.
| Variable | Flag | Default | Role |
|---|---|---|---|
QQL_EDGE_DATA_DIR |
--data-dir |
~/.qql/edge-data |
Directory for persistent edge data |
QQL_EDGE_EMBEDDER |
--embedder |
fastembed |
Embedder engine (fastembed or http) |
QQL_EDGE_MODEL |
--model |
BGESmallENV15 |
Dense FastEmbed model ID/alias |
QQL_EDGE_SPARSE_MODEL |
--sparse-model |
— | Offline sparse model (e.g. splade) |
QQL_EDGE_MULTI_MODEL |
--multi-model |
— | Offline multi/ColBERT model (e.g. bge-m3) |
QQL_EDGE_IMAGE_MODEL |
--image-model |
— | Offline CLIP vision model (e.g. clip-vision) |
QQL_EDGE_RERANKER_MODEL |
--reranker-model |
— | Offline cross-encoder (also falls back to RERANK_MODEL) |
QQL_EDGE_CACHE_DIR |
--cache-dir |
— | Model download cache directory |
QQL_EDGE_ON_DISK |
--in-memory |
true |
true/false/1/0 — payloads on disk |
QQL_EDGE_WAL_SEGMENT_MB |
--wal-segment-mb |
qdrant-edge 32 MiB | WAL segment capacity in MiB (> 0; seeds shards without a persisted capacity, a persisted value wins; also exposed by the Python/Node edge SDKs) |
QQL_EDGE_BM25_K1 |
--bm25-k1 |
1.2 |
Client-side BM25 k1 for the local sparse document encoder (write-path only; malformed values fail closed) |
QQL_EDGE_BM25_B |
--bm25-b |
0.75 |
Client-side BM25 b length normalization in [0, 1] |
QQL_EDGE_BM25_AVG_LEN |
--bm25-avg-len |
256 |
Client-side BM25 expected average document length in tokens |
EMBED_URL |
--embed-url |
— | HTTP embedding endpoint |
EMBED_KEY |
--embed-key |
— | HTTP Bearer token |
EMBED_MODEL |
--embed-model |
nomic-embed-text |
HTTP embedding model ID |
EMBED_DIM |
--embed-dim |
768 |
HTTP embedding vector dimension |
MULTI_EMBED_URL / MULTI_EMBED_KEY / MULTI_EMBED_MODEL / MULTI_EMBED_DIM |
--multi-embed-* |
— | Multi/ColBERT HTTP endpoint |
IMAGE_EMBED_URL / IMAGE_EMBED_KEY / IMAGE_EMBED_MODEL / IMAGE_EMBED_DIM |
--image-embed-* |
— | Image/CLIP HTTP endpoint |
Global: qql --url http://host:6333 run "…".
Edge
Config: ~/.qql/edge.json. Edge does not support custom SHARD / CREATE SHARD KEY or
SHOW QUOTAS / SET QUOTA — use remote Qdrant (REST) for those. GROUP BY (without
LOOKUP FROM), sparse PARAMS (idf = …), and ACORN are available offline (qdrant-edge 0.8+).
Edge → edge migrate is rejected before any executor starts; publish edge data to a remote
target with qql --edge migrate <coll> --target-url <url> (or --source-edge), and seed other
devices with qql edge bootstrap. Continuous sync is not provided (dual-write + partial
snapshots per the edge sync guide). Run qql edge optimize after bulk writes — the engine has
no background optimizer.
Multitenancy examples
CREATE SHARD KEY 'acme' ON COLLECTION docs WITH (shards_number = 2);
QUERY TEXT 'risks' FROM docs USING dense
WHERE tenant_id = 'acme'
SHARD 'acme'
LIMIT 10;
Recorder (qql record, built-in)
Zero-code-change capture for migration: Qdrant keeps its address, point the
app at the recorder instead, change nothing else. Every request is forwarded
to --target (status, headers, body — auth included; repeated headers are
preserved); collection and quota routes are appended as wrapped
{"method","path","query"?,"body"?} JSONL for later qql convert use.
Bodyless SHOW / DROP routes are recorded with no body.
qql record is built directly into the CLI:
# Qdrant stays on :6333, the app now points at the recorder on :6334:
# ... run the app ...
Bare qql record uses those defaults. Notes: query strings are forwarded
upstream and recorded as a "query" object (wait, timeout,
consistency) so qql convert recovers WAIT and PARAMS; a trailing /
is stripped from the recorded path only; non-JSON bodies are forwarded, not
recorded; bodies are buffered in RAM (multi-hundred-MB single upserts sit in
memory — fine for ColBERT-size batches); --qql-out converts at record time
and failures become -- ERROR <file:line> <error> comments (valid QQL
comments, so the capture replays without hand-editing). Ctrl-C stops
the recorder; files are fsynced per line so nothing is lost.
Script format
Semicolon-separated statements. -- comments OK.
CREATE COLLECTION docs (
dense VECTOR(384, COSINE)
WITH VECTOR (memory = 'cached', datatype = 'float16')
) WITH HNSW (memory = 'cold')
WITH PARAMS (payload_memory = 'cached');
CREATE INDEX ON COLLECTION docs FOR title TYPE keyword
WITH (prefix = true, memory = 'cached');
UPSERT INTO docs VALUES {id: 1, text: 'first document'}
USING DENSE MODEL 'all-minilm:l6-v2';
QUERY TEXT 'search' FROM docs USING dense
WHERE title MATCH PREFIX 'fir'
LIMIT 10;
Features
| Feature | Default | Role |
|---|---|---|
rest |
yes | REST |
grpc |
yes | gRPC |
fastembed |
no | Local zero-server ONNX embeddings (for remote Qdrant and migrations) |
edge |
no | In-process embedded Qdrant edge database |
full |
no | Convenience alias for fastembed,edge |
Docs
- Syntax · Install skill · Gaps