Skip to content

Multitenancy

Production multitenancy has distinct security, routing, layout, and lifecycle concerns.

ConcernQQL or host APIEffect
IsolationWHERE / inject_filterQdrant Filter
RoutingSHARD / stmt.shard_keyShardKeySelector
Layoutindex option is_tenant = truetenant-aware index organization
Partition lifecycleCREATE/DROP SHARD KEYcustom shard administration
QQLTenant-aware schemaTry in playground
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_id
TYPE keyword WITH (is_tenant = true);
QQLIsolation and routing togetherTry in playground
QUERY TEXT 'risk factors' FROM documents
USING dense
WHERE tenant_id = 'acme'
SHARD 'acme'
LIMIT 10;

For untrusted user or agent input:

  1. Parse the complete statement or script.
  2. Call inject_filter with the trusted tenant value.
  3. Optionally assign the trusted shard key.
  4. Inspect or explain the rewritten AST.
  5. 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)