Overview
The Query Builder provides a chainable API for constructing queries with filtering, relation loading, sorting, pagination, and field selection.
Basic Usage
Query()
Starts a new query for the given entity.
func (e *Engine) Query(entity string) *QueryBuilder
Example:
result, err := eng.Query("User").Execute(ctx)
if err != nil {
log.Fatal(err)
}
for _, user := range result.Rows {
fmt.Printf("User: %s\n", user["name"])
}
Execute()
Executes the query and returns results.
func (qb *QueryBuilder) Execute(ctx context.Context) (*QueryResult, error)
Context for query execution (supports cancellation and timeouts)
Query results containing rows and eager-loaded relations
QueryResult fields:
Name of the entity queried
Main query results (slice of Row maps)
Eager-loaded relations (relation name → rows)
Filtering
Filter()
Adds a filter condition to the query.
func (qb *QueryBuilder) Filter(field string, op string, value interface{}) *QueryBuilder
Field name or relation path (e.g., “email”, “posts.published”)
Filter operator: "eq", "neq", "gt", "gte", "lt", "lte", "like", "in"
Value to compare against (string, int, float64, bool)
Supported operators:
| Operator | SQL Equivalent | Description |
|---|
"eq" | = | Equal to |
"neq" | != | Not equal to |
"gt" | > | Greater than |
"gte" | >= | Greater than or equal |
"lt" | < | Less than |
"lte" | <= | Less than or equal |
"like" | LIKE | Pattern matching (use % wildcards) |
"in" | IN | Value in list |
Examples:
// Exact match
eng.Query("User").
Filter("email", "eq", "ana@mail.com").
Execute(ctx)
// Pattern matching
eng.Query("User").
Filter("name", "like", "%Garcia%").
Execute(ctx)
// Numeric comparison
eng.Query("User").
Filter("age", "gte", 18).
Execute(ctx)
// Multiple filters (AND logic)
eng.Query("Post").
Filter("published", "eq", true).
Filter("views", "gt", 1000).
Execute(ctx)
// SQL: WHERE published = true AND views > 1000
Field Selection
Select()
Specifies which fields to retrieve (partial selection).
func (qb *QueryBuilder) Select(fields ...string) *QueryBuilder
Field names to select (variadic)
Behavior:
- If not called, defaults to
SELECT * (all fields)
- Reduces data transfer and improves performance
- Selected fields are available in
result.Rows
Examples:
// Select specific fields
users, _ := eng.Query("User").
Select("id", "name", "email").
Execute(ctx)
for _, user := range users.Rows {
// Only id, name, email are available
fmt.Printf("%s <%s>\n", user["name"], user["email"])
}
// Select with filtering
eng.Query("Post").
Select("id", "title", "created_at").
Filter("published", "eq", true).
Execute(ctx)
// SQL: SELECT id, title, created_at FROM posts WHERE published = true
Eager Loading
Include()
Eager loads related entities (prevents N+1 queries).
func (qb *QueryBuilder) Include(path string) *QueryBuilder
Relation name or nested path (e.g., “posts”, “posts.comments”)
Behavior:
- Loads relations in separate queries
- Results available in
result.Relations["relation_name"]
- Supports nested includes with dot notation
- Uses IdentityMap for automatic deduplication
Examples:
// Single relation
result, _ := eng.Query("User").
Include("posts").
Execute(ctx)
for _, user := range result.Rows {
fmt.Printf("User: %s\n", user["name"])
// Access related posts
if posts, ok := result.Relations["posts"]; ok {
fmt.Printf(" Posts: %d\n", len(posts))
for _, post := range posts {
fmt.Printf(" - %s\n", post["title"])
}
}
}
// Multiple relations
eng.Query("User").
Include("posts").
Include("comments").
Execute(ctx)
// Nested relations
eng.Query("User").
Include("posts.comments").
Execute(ctx)
// Loads users, their posts, and comments on those posts
Sorting
OrderBy()
Adds a sort clause to the query.
func (qb *QueryBuilder) OrderBy(field string, direction string) *QueryBuilder
Sort direction: "asc" or "desc"
Examples:
// Sort ascending
eng.Query("User").
OrderBy("name", "asc").
Execute(ctx)
// Sort descending
eng.Query("Post").
OrderBy("created_at", "desc").
Execute(ctx)
// Multiple sort fields
eng.Query("User").
OrderBy("last_name", "asc").
OrderBy("first_name", "asc").
Execute(ctx)
// SQL: ORDER BY last_name ASC, first_name ASC
Limit()
Limits the number of results returned.
func (qb *QueryBuilder) Limit(n uint64) *QueryBuilder
Maximum number of rows to return
Offset()
Skips the first N results.
func (qb *QueryBuilder) Offset(n uint64) *QueryBuilder
Examples:
// First 10 users
eng.Query("User").
Limit(10).
Execute(ctx)
// Page 2 (rows 11-20)
eng.Query("User").
Limit(10).
Offset(10).
Execute(ctx)
// Pagination helper
func getPage(eng *engine.Engine, page, pageSize int) (*engine.QueryResult, error) {
offset := (page - 1) * pageSize
return eng.Query("User").
OrderBy("created_at", "desc").
Limit(uint64(pageSize)).
Offset(uint64(offset)).
Execute(context.Background())
}
Debug Mode
Debug()
Enables SQL debug output for this query.
func (qb *QueryBuilder) Debug() *QueryBuilder
Output:
- Generated SQL query
- Does not show execution time or row counts (use
DebugTrace() for that)
Example:
result, _ := eng.Query("User").
Filter("email", "like", "%@example.com").
Debug().
Execute(ctx)
// Output:
// [SQL]
// SELECT * FROM users WHERE email LIKE '%@example.com'
DebugTrace()
Enables full trace output (SQL + timing + row counts).
func (qb *QueryBuilder) DebugTrace() *QueryBuilder
Example:
eng.Query("User").
Select("id", "name").
DebugTrace().
Execute(ctx)
// Output:
// ┌─────────────────────────────────────
// │ Query Trace
// ├─────────────────────────────────────
// │ SQL:
// │ SELECT id, name FROM users
// │ Duration: 2.3ms
// │ Rows: 5
// └─────────────────────────────────────
Advanced Usage
Combining All Features
result, err := eng.Query("Post").
Select("id", "title", "published", "created_at").
Filter("published", "eq", true).
Filter("views", "gte", 100).
Include("author").
Include("comments").
OrderBy("created_at", "desc").
Limit(20).
Offset(0).
Debug().
Execute(ctx)
if err != nil {
log.Fatal(err)
}
// Process results
for _, post := range result.Rows {
fmt.Printf("Post: %s (views: %d)\n",
post["title"], post["views"])
// Access author
if authors, ok := result.Relations["author"]; ok {
if len(authors) > 0 {
fmt.Printf(" Author: %s\n", authors[0]["name"])
}
}
// Access comments
if comments, ok := result.Relations["comments"]; ok {
fmt.Printf(" Comments: %d\n", len(comments))
}
}
Working with Results
result, _ := eng.Query("User").Execute(ctx)
// Check if empty
if result.IsEmpty() {
fmt.Println("No users found")
return
}
// Count results
fmt.Printf("Found %d users\n", result.Count())
// Iterate rows
for i, user := range result.Rows {
// Access fields
id := user["id"]
name := user["name"]
email := user["email"]
fmt.Printf("%d. %s <%s> (id: %v)\n", i+1, name, email, id)
}
// Type-safe field access (Row helpers)
firstUser := result.Rows[0]
name := firstUser.String("name") // Returns "" if not found
age := firstUser.Int("age") // Returns 0 if not found
value := firstUser.Get("created_at") // Returns interface{}
Query Result Reference
QueryResult
type QueryResult struct {
Entity string // "User"
Rows []Row // Main results
Relations map[string][]Row // Eager-loaded relations
}
Methods:
Returns the number of rows in the main result
Returns true if no rows were returned
Row
type Row map[string]interface{}
Methods:
Returns the raw value of a field
Returns the string value of a field (or "" if not found)
Returns the int64 value of a field (or 0 if not found)
Complete Example
package main
import (
"context"
"fmt"
"log"
"github.com/chameleon-db/chameleondb/chameleon/pkg/engine"
)
func main() {
ctx := context.Background()
eng, _ := engine.NewEngine()
defer eng.Close()
// ... connect to database ...
// Complex query with all features
result, err := eng.Query("User").
Select("id", "name", "email", "created_at").
Filter("status", "eq", "active").
Filter("created_at", "gte", "2024-01-01").
Include("posts").
OrderBy("created_at", "desc").
Limit(50).
Debug().
Execute(ctx)
if err != nil {
log.Fatal(err)
}
// Process results
fmt.Printf("Active users: %d\n\n", result.Count())
for _, user := range result.Rows {
fmt.Printf("User: %s <%s>\n",
user["name"], user["email"])
if posts, ok := result.Relations["posts"]; ok {
fmt.Printf(" Posts: %d\n", len(posts))
}
}
}
See Also