Query Builder
April 23, 2026 ยท View on GitHub
import "github.com/CaliLuke/go-typeql/gotype" -- pkg.go.dev
Query[T] provides a chainable builder for constructing TypeQL match-fetch queries with filters, sorting, pagination, aggregations, grouping, and bulk updates.
Creating Queries
Start from a Manager[T]:
persons := gotype.MustNewManager[Person](db)
q := persons.Query()
Filters
All filters implement the Filter interface. They generate TypeQL pattern strings injected into the match clause.
Variable scoping gotcha: Variable names use the format $e__attr_name (double underscore separator) to avoid TypeQL implicit equality semantics. Hyphens in attribute names are replaced with underscores in variable names.
Comparison Filters
gotype.Eq("name", "Alice") // ==
gotype.Neq("status", "inactive") // !=
gotype.Gt("age", 18) // >
gotype.Gte("score", 90) // >=
gotype.Lt("price", 100.0) // <
gotype.Lte("priority", 3) // <=
String Filters
gotype.Contains("email", "@example.com") // contains
gotype.Like("name", "Ali.*") // like (regex)
gotype.Regex("email", "^[^@]+@.+") // like (regex)
gotype.Startswith("name", "Al") // like (prefix)
Set Membership
gotype.In("status", []any{"active", "pending"}) // or block with equality per value
gotype.NotIn("status", []any{"banned", "deleted"}) // wrapped in not block
Range
gotype.Range("age", 18, 65) // >= and <= combined
Existence
gotype.HasAttr("phone") // attribute exists
gotype.NotHasAttr("phone") // attribute does not exist
IID and Role Player
gotype.ByIID("0x123") // match by TypeDB internal ID
// Match multiple IIDs in a single OR query
gotype.IIDIn("0x123", "0x456", "0x789")
// For relations: filter by role player attributes
gotype.RolePlayer("employee", gotype.Eq("name", "Alice"))
Computed Expressions
Use Computed with ArithmeticExpr and BuiltinFuncExpr to filter on computed values:
// Filter where price * quantity > 100
gotype.Computed("total",
gotype.ArithmeticExpr("e", "price", "*", "quantity"),
">", 100.0)
// Filter where abs(balance) > 1000
gotype.Computed("abs_bal",
gotype.BuiltinFuncExpr("abs", "$e__balance"),
">", 1000.0)
ArithmeticExpr supports operators: +, -, *, /, %, ^.
BuiltinFuncExpr wraps TypeQL built-in functions: abs, ceil, floor, round, length, max, min.
Boolean Composition
gotype.And(filter1, filter2) // logical AND (nested ANDs are flattened)
gotype.Or(filter1, filter2) // TypeQL disjunction { ... } or { ... }
gotype.Not(filter) // not { ... } block
Multiple calls to Filter() on the same query are ANDed together.
Sorting, Pagination
q.OrderAsc("name") // sort ascending
q.OrderDesc("age") // sort descending
q.Limit(25)
q.Offset(50)
Sort attributes automatically get has patterns added to the match clause.
Terminal Operations
results, err := q.Execute(ctx) // run query, return all matches
results, err := q.All(ctx) // alias for Execute
first, err := q.First(ctx) // limit 1, return first (nil if none)
count, err := q.Count(ctx) // count of matches
exists, err := q.Exists(ctx) // true if any match exists
deleted, err := q.Delete(ctx) // delete all matches, return count
Functional Update (UpdateWith)
Fetches all matches, applies a function to each, then writes all changes back in a single transaction:
updated, err := persons.Query().
Filter(gotype.Gt("age", 60)).
UpdateWith(ctx, func(p *Person) {
newAge := *p.Age + 1
p.Age = &newAge
})
Bulk Attribute Update
Updates specific attributes on all matching instances using per-attribute delete-old/insert-new:
count, err := persons.Query().
Filter(gotype.Eq("status", "pending")).
Update(ctx, map[string]any{"status": "active"})
Aggregations
Aggregation methods return *AggregateQuery[T] with its own Execute returning float64:
avgAge, _ := persons.Query().
Filter(gotype.Gt("age", 0)).
Avg("age").
Execute(ctx)
Available: Sum, Avg, Min, Max, Median, Std, Variance.
TypeDB gotcha: TypeDB uses mean (not avg) for average aggregation. The Avg method handles this mapping for you.
Multi-Aggregation
Compute multiple aggregations in a single query:
results, _ := persons.Query().
Aggregate(ctx,
gotype.AggregateSpec{Fn: "sum", Attr: "age"},
gotype.AggregateSpec{Fn: "mean", Attr: "age"},
)
// results["sum_age"] = 450.0, results["mean_age"] = 30.0
GroupBy
Group results by an attribute and compute aggregations per group:
results, _ := persons.Query().
GroupBy("status").
Aggregate(ctx,
gotype.AggregateSpec{Fn: "count", Attr: "name"},
)
// results["active"]["count_name"] = 5.0
// results["inactive"]["count_name"] = 2.0
Function Queries
Call TypeDB schema functions (defined with fun) using FunctionQuery:
fq := gotype.NewFunctionQuery(db, "get_user_score").
Arg("Alice").
Arg(42)
// Build the TypeQL string
query := fq.Build()
// let $result = get_user_score("Alice", 42);
// return $result;
// Or execute directly
results, err := fq.Execute(ctx)
Use ArgRaw for pre-formatted expressions like variable references:
fq := gotype.NewFunctionQuery(db, "compute_total").
ArgRaw("$x").
Arg(1.5)
Complete Example
persons := gotype.MustNewManager[Person](db)
ctx := context.Background()
// Complex query with filters, sorting, pagination
results, err := persons.Query().
Filter(gotype.And(
gotype.Gte("age", 18),
gotype.Or(
gotype.Contains("email", "@acme.com"),
gotype.Contains("email", "@example.com"),
),
)).
OrderAsc("name").
Limit(25).
Offset(0).
Execute(ctx)
// Functional update on filtered set
updated, err := persons.Query().
Filter(gotype.Eq("status", "trial")).
UpdateWith(ctx, func(p *Person) {
s := "active"
p.Status = &s
})
// Aggregation with grouping
grouped, err := persons.Query().
GroupBy("department").
Aggregate(ctx,
gotype.AggregateSpec{Fn: "mean", Attr: "age"},
gotype.AggregateSpec{Fn: "count", Attr: "name"},
)