Repository navigation
The query builder is complete! This release rounds out SELECT with joins, aggregations, grouping, CTEs, unions, derived tables, and row locking β plus a cleaner UPDATE, raw queries, TRUNCATE, and schema-qualified tables.
β¨ What's New
Joins
Join(INNER),LeftJoin/LeftOuterJoin,RightJoin/RightOuterJoin,FullOuterJoin(PostgreSQL-only, capability-checked), andCrossJoin- Two ON forms β
Join(table, left, right)for equality,Join(table, left, op, right)for anything else β plusJoinRawfor compound conditions with?-bound args - Qualified identifiers everywhere:
users.idnow quotes as"users"."id"in every clause, andusers.*works in column lists - Table aliasing with
"orders as o"in joins andFrom()β self-joins included:
db.Select("users.name", "o.total").From("users").
Join("orders as o", "users.id", "o.user_id").
Where("o.total", ">", 100).
Exec()
// SELECT "users"."name", "o"."total" FROM "users"
// INNER JOIN "orders" AS "o" ON "users"."id" = "o"."user_id" WHERE "o"."total" > $1Aggregations & Grouping
Count(),Sum(),Avg(),Min(),Max()β single-value queries honoring your WHERE conditionsGroupBy/GroupByRawand the full HAVING family:Having,HavingRaw,HavingIn/HavingNotIn,HavingNull/HavingNotNull,HavingBetween/HavingNotBetween,HavingExists/HavingNotExistsDistinct()and PostgreSQL'sDistinctOn()(capability-checked β a clear error on MySQL, not broken SQL)All()β every matching row as[]map[string]any, no*sql.Rowsboilerplate
Subqueries: Derived Tables, CTEs, Unions
- Name any builder with
As("t")and select from it:From(sub)compilesFROM (SELECT ...) AS "t" With(name, subquery)/WithRecursive(...)for common table expressions β builder or raw SQL bodiesUnion/UnionAllwith other builders or raw queries;OrderBy/Limitapply to the combined result- Placeholder numbering stays correct through any nesting β CTEs, FROM subqueries, joins, WHERE, HAVING, unions all continue the same sequence
totals := jone.Select("user_id").From("orders").GroupBy("user_id")
db.Select("*").From("totals").With("totals", totals).Exec()
// WITH "totals" AS (SELECT "user_id" FROM "orders" GROUP BY "user_id") SELECT * FROM "totals"Row Locking
ForUpdate()/ForShare()withSkipLocked()/NoWait()β the classic job-queue claim is now one chain:
tx.Select("*").From("jobs").Where("status", "queued").
OrderBy("created_at").ForUpdate().SkipLocked().First()More WHERE Conditions
On Select, Update, and Delete β and inside condition groups β each with Or variants:
WhereNotfor negated conditions and groups (NOT "col" = $1,NOT (...))WhereBetween/WhereNotBetweenWhereExists/WhereNotExistswith builder or raw subqueriesWhereLike/WhereILikeβ case sensitivity handled per dialect (PGLIKE/ILIKE, MySQLLIKE BINARY/LIKE)Orvariants completed for the existing family:OrWhereIn,OrWhereNotIn,OrWhereNull,OrWhereNotNull,OrWhereRaw
UPDATE
Set() is gone β pass the data straight to Update:
db.Update("users", map[string]any{"name": "Alice", "updated_at": jone.Fn.Now()}).Where("id", 1).Exec()
db.Update("users", user).Where("id", 1).Exec() // struct with `db` tags
db.Update("books", "title", "New Title").Where("id", 42).Exec() // single pair
db.Update("counters").Increment("views").Exec() // new: Increment / DecrementRaw Queries, Truncate, WithSchema
db.RawQuery("SELECT * FROM users WHERE age > ?", 21).All()β raw SQL with rows back as maps;?rebinds to$non PostgreSQL. (s.Raw()in migrations is unchanged.)db.Truncate("sessions").Exec()WithSchema("analytics")on every builder for schema-qualified tables β on SELECT it covers joined tables too
β οΈ Breaking Changes
Set()is removed β pass data directly:Update(table, map),Update(table, struct), orUpdate(table, column, value)- Custom dialects:
SelectSQLnow takes a singleSubSelectstruct, and theDialectinterface gainedAggregateSQL,RawSQL,TruncateSQL,SupportsDistinctOn, andSupportsFullOuterJoin QuoteIdentifiertreats dots as qualification βusers.idquotes as two identifiers now
π¦ Installation
go install github.com/Grandbusta/jone/cmd/jone@v0.4.0