Query Safety
Query Safety
KoutenDB does not execute SQL, so its query-safety surface is different from a SQL database.
The two important pieces are:
- prepared selections for projection;
- typed filter builders for read filters.
Both are designed to avoid string-concatenated query text while keeping
KoutenDB’s read path centered on a ring or stellar coordinate.
Prepared Selections
Selections are GraphQL-style projections over JSON payloads:
let fields = prepareSelection("{ title author { name } }")
let first = db.query(firstId, fields)
let second = db.query(secondId, fields)
prepareSelection validates and parses the selection once. Embedded reads reuse
the parsed tree directly. Cluster nodes also keep a bounded cache of validated
selection trees. The server cache is bounded by both entry count and the total
source bytes retained, and very large selection strings are rejected before they
can be cached.
The selection grammar contains field names only. Payload values are not interpolated into it.
Typed Filter Builders
Filters are still represented as JSON objects internally and on the wire. That keeps the CLI, C ABI, and existing drivers compatible.
For application code, KoutenDB also provides a builder so callers do not need to construct filter JSON by string concatenation:
let filter = koutenFilter().eq("status", "draft").eq("kind", "order")
let page = db.readRing("docs/japan", defaultReadOptions().withFilter(filter))
The same filter can be used with stellar reads:
let page = db.readStellar("commerce/order/A-001",
defaultStellarOptions().withFilter(koutenFilter().eq("kind", "shop")))
ID reads can also be built without manually stringifying the ID:
let page = db.readRing("users", defaultReadOptions().withFilter(
koutenFilter().id(userId)))
Supported builder values:
- strings;
- integers;
- floats;
- booleans;
- explicit
JsonNodevalues; - KoutenDB IDs through
id(id).
Scope Comes First
The filter builder is not intended to turn KoutenDB into an ad-hoc global query engine.
KoutenDB’s read model remains:
- choose a
ringorstellarcoordinate; - optionally narrow with
subring; - apply typed filters inside that local scope;
- project only the requested fields.
This keeps query safety aligned with the main KoutenDB idea: avoid unrelated working sets before downstream work begins.
Server-Side Cost Guards
koutend also enforces bounded network query work. RETRIEVE requests have a
maximum result budget and a maximum vector scan count. If a request crosses
those bounds, the server returns a stable ERR bad-request instead of keeping
the single-threaded server busy indefinitely.
The normal fix is not to raise the cap first. Prefer a ring, stellar, child
scope, or narrower retrieval plan so KoutenDB can reduce the candidate set before
scoring. This turns unsafe broad scans into an observable tuning problem instead
of hiding them behind a slow global query.
Compatibility
These two forms are equivalent:
let a = db.readRing("docs", KoutenReadOptions(
filter: %*{"status": "draft"},
selection: "{ title }"))
let b = db.readRing("docs", defaultReadOptions()
.withFilter(koutenFilter().eq("status", "draft")))
The builder-produced filter is a defensive JSON object copy. It can be passed
through the same API paths that already accept JsonNode filters.