Skip to main content

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)
ctx
context.Context
required
Context for query execution (supports cancellation and timeouts)
QueryResult
*QueryResult
Query results containing rows and eager-loaded relations
QueryResult fields:
Entity
string
Name of the entity queried
Rows
[]Row
Main query results (slice of Row maps)
Relations
map[string][]Row
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
string
required
Field name or relation path (e.g., “email”, “posts.published”)
op
string
required
Filter operator: "eq", "neq", "gt", "gte", "lt", "lte", "like", "in"
value
interface{}
required
Value to compare against (string, int, float64, bool)
Supported operators:
OperatorSQL EquivalentDescription
"eq"=Equal to
"neq"!=Not equal to
"gt">Greater than
"gte">=Greater than or equal
"lt"<Less than
"lte"<=Less than or equal
"like"LIKEPattern matching (use % wildcards)
"in"INValue 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
fields
...string
required
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
path
string
required
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
field
string
required
Field name to sort by
direction
string
required
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

Pagination

Limit()

Limits the number of results returned.
func (qb *QueryBuilder) Limit(n uint64) *QueryBuilder
n
uint64
required
Maximum number of rows to return

Offset()

Skips the first N results.
func (qb *QueryBuilder) Offset(n uint64) *QueryBuilder
n
uint64
required
Number of rows to skip
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:
Count()
int
Returns the number of rows in the main result
IsEmpty()
bool
Returns true if no rows were returned

Row

type Row map[string]interface{}
Methods:
Get(field)
interface{}
Returns the raw value of a field
String(field)
string
Returns the string value of a field (or "" if not found)
Int(field)
int64
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