Production multitenancy has distinct security, routing, layout, and lifecycle concerns.
| Concern | QQL or host API | Effect |
|---|---|---|
| Isolation | WHERE / inject_filter | Qdrant Filter |
| Routing | SHARD / stmt.shard_key | ShardKeySelector |
| Layout | index option is_tenant = true | tenant-aware index organization |
| Partition lifecycle | CREATE/DROP SHARD KEY | custom shard administration |
CREATE COLLECTION documents ( dense VECTOR(384, COSINE)) WITH PARAMS ( shard_number = 8, sharding_method = 'custom', shard_keys = ['acme', 'globex']);
CREATE INDEX ON COLLECTION documents FOR tenant_idTYPE keyword WITH (is_tenant = true);QUERY TEXT 'risk factors' FROM documentsUSING denseWHERE tenant_id = 'acme'SHARD 'acme'LIMIT 10;Host-side enforcement
Section titled “Host-side enforcement”For untrusted user or agent input:
- Parse the complete statement or script.
- Call
inject_filterwith the trusted tenant value. - Optionally assign the trusted shard key.
- Inspect or explain the rewritten AST.
- Execute the rewritten statement, never the original source.
inject_filter is statement-aware. It recurses into CTE and prefetch branches for queries, wraps the point selector for mutations so they can only touch the caller's tenant, stamps the tenant value onto payloads for UPSERT, and fails closed for DDL, SHOW, and vector updates. See the Filter injection guide for the full behavior.
from pyqql import Client, parse
client = Client("http://localhost:6333")stmt = parse(query)[0]stmt.inject_filter("tenant_id", "=", "acme")stmt.shard_key = "acme"
report = client.execute(stmt)