Query Builder
Build complex database queries with a fluent, chainable API. The QueryBuilder lets you compose filters, sorting, pagination, aggregations, and eager loading step by step.
Query Builder Chaining
Chain methods to build complex queries. You can start a chain with .where(), .order(), or .limit():
# Start with .where()
let results = User
.where("age >= @age", { "age": 18 })
.where("active == @active", { "active": true })
.order("created_at", "desc")
.limit(10)
.offset(20)
.all
# Start with .order() — no filter needed
let recent = User.order("created_at", "desc").limit(5).all
# Start with .limit()
let sample = User.limit(3).all
# Get first result only
let first = User.where("email == @email", { "email": "[email protected]" }).first
# Count with conditions
let count = User.where("role == @role", { "role": "admin" }).count
QueryBuilder Methods
All chainable methods available on the QueryBuilder. Methods like .where(), .order(), .limit(), and .includes() return a new QueryBuilder, while .all, .first, .count execute the query.
| Method | Description |
|---|---|
.where(filter, bind_vars) |
Add filter condition (ANDed) |
.order(field, direction) |
Set sort order ("asc"/"desc") |
.limit(n) |
Limit results to n documents |
.offset(n) |
Skip first n documents |
.all |
Execute, return all results |
.first |
Execute, return first result |
.count |
Execute, return count |
.exists |
Set exists mode (chain with .first to execute, .to_query to inspect) |
.pluck(field, ...) |
Set pluck mode for specified fields (chain with .all to execute) |
.sum(field) |
Set sum aggregation (chain with .first to execute) |
.avg(field) |
Set average aggregation (chain with .first to execute) |
.min(field) |
Set minimum aggregation (chain with .first to execute) |
.max(field) |
Set maximum aggregation (chain with .first to execute) |
.group_by(field, func, agg_field) |
Set group-by aggregation (chain with .all to execute) |
.includes(rel, ...) |
Eager load relations via subqueries |
.includes(rel, filter, binds) |
Eager load with filter and optional "fields" key |
.includes({ rel: [fields] }) |
Eager load with field projection |
.select(field, ...) |
Select specific fields on the main collection |
.fields(field, ...) |
Alias for .select() |
.join(rel, filter?, binds?) |
Filter by existence of related records |
.to_query |
Return the generated SDBQL string (for debugging) |
Query Generation (SDBQL)
Under the hood, QueryBuilder methods generate SDBQL (SoliDB Query Language) queries. Use .to_query to inspect the generated output:
| Soli Code | Generated SDBQL |
|---|---|
User.all |
FOR doc IN users RETURN doc |
User.where("age >= @age", {"age": 18}) |
FOR doc IN users FILTER doc.age >= @age RETURN doc |
User.order("name", "asc").all |
FOR doc IN users SORT doc.name ASC RETURN doc |
User.order("name", "asc").limit(10).all |
FOR doc IN users SORT doc.name ASC LIMIT 10 RETURN doc |
.limit(10).offset(20) |
... LIMIT 20, 10 RETURN doc |
User.count |
FOR doc IN users COLLECT WITH COUNT INTO count RETURN count |
User.includes("posts") |
FOR doc IN users LET _rel_posts = (FOR rel IN posts FILTER rel.user_id == doc._key RETURN rel) RETURN MERGE(doc, {posts: _rel_posts}) |
.includes("posts", "published = @p", {"p": true}) |
... FILTER rel.user_id == doc._key AND rel.published == @p RETURN rel ... |
.includes({"posts": ["title"]}) |
... RETURN {title: rel.title} ... |
User.select("name", "email") |
FOR doc IN users RETURN {name: doc.name, email: doc.email, _key: doc._key} |
User.join("posts") |
FOR doc IN users FILTER LENGTH(FOR rel IN posts FILTER rel.user_id == doc._key LIMIT 1 RETURN 1) > 0 RETURN doc |
.exists.to_query |
FOR doc IN users ... LIMIT 1 RETURN true |
.pluck("name").to_query |
FOR doc IN users ... RETURN doc.name |
.pluck("name", "email").to_query |
FOR doc IN users ... RETURN {name: doc.name, email: doc.email} |
.sum("balance").to_query |
FOR doc IN users ... RETURN SUM(doc.balance) |
.group_by("country", "sum", "balance").to_query |
FOR doc IN users ... COLLECT group = doc.country AGGREGATE result = SUM(doc.balance) RETURN {group: group, result: result} |
SDBQL Syntax
FOR doc IN collectioninstead ofSELECT * FROMFILTER expressioninstead ofWHERESORT doc.field ASC/DESCinstead ofORDER BY@variablesyntax for bind parametersLETsubqueries +MERGEfor eager loading