Reference — Models, Config, Project Structure, Errors
Reference — Models, Config, Project Structure, Errors
Embedding Models
QQL uses Fastembed to convert text into vectors locally — no external API call is needed.
Dense embedding (default)
sentence-transformers/all-MiniLM-L6-v2
- Vector dimensions: 384
- Size: ~90 MB (downloaded on first use, cached locally)
- Good balance of speed and quality for English text
Sparse embedding (hybrid mode default)
Qdrant/bm25
- Classic BM25 with IDF weighting
- Indices and values are generated as a sparse vector; no fixed dimensions
- Uses asymmetric encoding:
embed()for documents,query_embed()for queries
Specifying models
-- Dense only with custom model
INSERT INTO docs VALUES {'text': 'hello'} USING MODEL 'BAAI/bge-small-en-v1.5'
SEARCH docs SIMILAR TO 'hello' LIMIT 5 USING MODEL 'BAAI/bge-small-en-v1.5'
-- Hybrid with custom dense model
SEARCH docs SIMILAR TO 'hello' LIMIT 5 USING HYBRID DENSE MODEL 'BAAI/bge-base-en-v1.5'
-- Hybrid with explicit fusion strategy
SEARCH docs SIMILAR TO 'hello' LIMIT 5 USING HYBRID FUSION 'dbsf'
-- Hybrid with both custom
SEARCH docs SIMILAR TO 'hello' LIMIT 5
USING HYBRID DENSE MODEL 'BAAI/bge-base-en-v1.5' SPARSE MODEL 'prithivida/Splade_PP_en_v1'
Commonly available dense models (Fastembed)
| Model | Dimensions | Notes |
|---|---|---|
sentence-transformers/all-MiniLM-L6-v2 |
384 | Default. Fast, good general quality |
BAAI/bge-small-en-v1.5 |
384 | Strong English retrieval |
BAAI/bge-base-en-v1.5 |
768 | Higher quality, larger size |
BAAI/bge-large-en-v1.5 |
1024 | Best quality, slowest |
sentence-transformers/all-mpnet-base-v2 |
768 | Strong semantic similarity |
Commonly available sparse models (Fastembed)
| Model | Notes |
|---|---|
Qdrant/bm25 |
Default sparse model. Classic BM25 + IDF |
prithivida/Splade_PP_en_v1 |
SPLADE++ — strong keyword + semantic overlap |
Qdrant/Unicoil |
UniCOIL sparse encoder |
Cross-encoder reranking (RERANK default)
cross-encoder/ms-marco-MiniLM-L-6-v2
| Model | Notes |
|---|---|
cross-encoder/ms-marco-MiniLM-L-6-v2 |
Default. Fast passage reranker |
cross-encoder/ms-marco-MiniLM-L-12-v2 |
Larger, higher quality |
BAAI/bge-reranker-base |
Strong general-purpose reranker |
BAAI/bge-reranker-large |
Highest quality, slower |
Models are downloaded automatically on first use and cached by Fastembed.
Model consistency rule
Every collection is created with a fixed vector size determined by the model used on first INSERT. If you try to INSERT using a different model that produces different dimensions, QQL raises:
Error: Vector dimension mismatch: collection 'docs' expects 384 dims,
but model produces 768 dims. Specify a compatible model with USING MODEL '<model>'.
Value Types in Dictionaries
| Type | Example | Notes |
|---|---|---|
| String | 'hello' or "hello" |
Single or double quotes |
| Integer | 42, -7 |
Whole numbers, negative allowed |
| Float | 3.14, -0.5 |
Decimal numbers |
| Boolean | true, false |
Case-insensitive |
| Null | null |
Case-insensitive |
| Nested dict | {'key': 'val'} |
Arbitrary nesting |
| List | ['a', 'b', 1] |
Mixed types allowed |
Trailing commas in dicts and lists are allowed.
Configuration File
The connection config is stored at ~/.qql/config.json:
{
"url": "http://localhost:6333",
"secret": null,
"default_model": "sentence-transformers/all-MiniLM-L6-v2"
}
| Field | Description |
|---|---|
url |
Qdrant instance URL |
secret |
API key (null if not required) |
default_model |
Dense embedding model used when no USING MODEL clause is given |
You can edit this file directly to change the default model without reconnecting.
Project Structure
qql/
├── pyproject.toml # Package config; installs the `qql` CLI command
├── src/
│ └── qql/
│ ├── __init__.py # Public API: run_query()
│ ├── cli.py # CLI entry point: connect, disconnect, execute, dump, REPL
│ ├── config.py # QQLConfig dataclass + ~/.qql/config.json I/O
│ ├── exceptions.py # QQLError, QQLSyntaxError, QQLRuntimeError
│ ├── lexer.py # Tokenizer: string → List[Token]
│ ├── ast_nodes.py # Frozen dataclasses for each statement and filter type
│ ├── parser.py # Recursive descent parser: tokens → AST node
│ ├── embedder.py # Embedder (dense) + SparseEmbedder (BM25) + CrossEncoderEmbedder (rerank)
│ ├── executor.py # AST node → Qdrant client call + filter + hybrid search
│ ├── script.py # Script runner: parse and execute .qql files statement by statement
│ └── dumper.py # Collection exporter: scroll all points → .qql INSERT BULK script
└── tests/
├── test_lexer.py # Tokenizer unit tests
├── test_parser.py # Parser unit tests
├── test_executor.py # Executor unit tests (mocked Qdrant client)
├── test_script.py # Script runner unit tests
└── test_dumper.py # Dumper unit tests
Running Tests
Tests do not require a running Qdrant instance — the Qdrant client is mocked.
pytest tests/ -v
Expected output: 405 tests passing.
Error Reference
| Error | Cause | Fix |
|---|---|---|
Not connected. Run: qql connect --url <url> |
No ~/.qql/config.json found |
Run qql connect --url <url> first |
Connection failed: ... |
Qdrant unreachable at given URL | Check that Qdrant is running and the URL is correct |
INSERT requires a 'text' field in VALUES |
text key missing from the VALUES dict |
Add 'text': '...' to your dict |
Vector dimension mismatch: collection '...' expects X dims, but model produces Y dims |
Model used in INSERT differs from the one used to create the collection | Use USING MODEL to specify the same model as the collection was created with |
Collection '...' does not exist |
SEARCH / SCROLL / SELECT / DROP / DELETE on a non-existent collection | Check name spelling or run SHOW COLLECTIONS |
Unexpected token '...'; expected a QQL statement keyword |
Unrecognized statement | Check the query syntax and supported statement list |
SELECT requires a string or integer point id, got '...' |
SELECT used with a non-ID filter value |
Use SELECT * FROM <collection> WHERE id = '<id>' or an integer ID |
Unterminated string literal (at position N) |
A string is missing its closing quote | Close the string with a matching ' or " |
Unexpected character '@' (at position N) |
A character not part of QQL syntax | Remove or quote the offending character |
Expected a filter operator after field '...' |
Unknown operator in WHERE clause | Use one of: =, !=, >, >=, <, <=, IN, NOT IN, BETWEEN, IS NULL, IS NOT NULL, IS EMPTY, IS NOT EMPTY, MATCH |
Expected ')' ... |
Unclosed parenthesis in WHERE clause | Add the missing ) to close the group |
Qdrant error during SEARCH: ... |
Hybrid search on a non-hybrid collection, or wrong vector names | Ensure the collection was created with HYBRID before using USING HYBRID in INSERT/SEARCH |
Qdrant error during SCROLL: ... |
Qdrant rejected scroll request | Verify collection state, filter, and cursor (AFTER) value |
Unknown index type '...' |
Invalid schema type in CREATE INDEX | Use one of: keyword, integer, float, bool, text, geo, datetime |
Qdrant error during CREATE INDEX: ... |
Qdrant rejected the index creation | Check field name and collection state |