OOro

Conditions

Field, JSON, full-text, group, and subquery condition reference.

Oro conditions are designed to be readable at the call site while preserving SQL expressiveness.

Where forms

Where("Price", 100)             // Price = 100
Where("Price", ">=", 100)      // Price >= 100
Where(oro.Field("Price").Gte(100))

Where does not accept callback functions. Use WhereGroup, OrWhereGroup, or WhereWhen for callbacks.

Model queries use Go field names. Table queries use database column names.

Operator allowlist

The three-argument form Where(field, op, value) validates op against a fixed allowlist. An unknown operator returns ErrInvalidArgument instead of being interpolated into SQL.

Value conditions allow (case-insensitive, whitespace-normalized):

= != <> < <= > >= like not like ilike not ilike is is not

Column conditions (WhereColumn) allow: = != <> < <= > >=.

ilike / not ilike only render natively on PostgreSQL.

Field expressions

query.Where(
    oro.Field("Status").Eq("active"),
    oro.Field("Price").Gte(100),
)

Value comparison methods:

Method SQL
Eq(value) =
NotEq(value) !=
Gt(value) >
Gte(value) >=
Lt(value) <
Lte(value) <=
Like(value) LIKE
NotLike(value) NOT LIKE
In(values...) IN
NotIn(values...) NOT IN
Between(start, end) BETWEEN
NotBetween(start, end) NOT (BETWEEN)
IsNull() IS NULL
IsNotNull() IS NOT NULL

Column comparison methods:

Method SQL
EqCol(right) left = right
NotEqCol(right) left != right
GtCol(right) left > right
GteCol(right) left >= right
LtCol(right) left < right
LteCol(right) left <= right

Literal LIKE helpers

Like(value) and NotLike(value) use the value as-is, so % and _ stay wildcards. When the input is user data that must be matched literally, use the helpers that escape it for you:

Method LIKE pattern
Contains(value) %value%
StartsWith(value) value%
EndsWith(value) %value
oro.Field("Name").Contains("100%")   // matches the literal substring "100%"
oro.Field("Code").StartsWith("VIP")
oro.Field("Code").Like("P%")          // % is still a wildcard (unchanged)

These escape \, %, and _ in the input and emit LIKE ? ESCAPE '\' on SQLite, MySQL, and PostgreSQL, so characters like % or _ are matched literally.

To build a custom pattern with Like while keeping user input literal, escape it with oro.EscapeLike:

oro.Field("Name").Like("%" + oro.EscapeLike(userInput) + "%")  // manual escaping

Time and date ranges

Use oro.Time(field) when the condition is about a time column. Explicit boundary methods keep their SQL meaning, while calendar buckets are compiled as half-open ranges: field >= start AND field < end. This keeps indexes usable and works the same on SQLite, MySQL, and PostgreSQL.

orders, err := db.Use[Order]().
    Where(oro.Time("CreatedAt").InRange(start, end)).
    Get(ctx)

orders, err = db.Use[Order]().
    Where(oro.Time("CreatedAt").OnDate(dayInUserLocation)).
    Get(ctx)

Boundary methods:

Method SQL meaning
Between(start, end) closed BETWEEN
NotBetween(start, end) NOT (BETWEEN)
After(value) >
Before(value) <
From(value) >=
Until(value) <
InRange(start, end) half-open [start, end)

Calendar bucket methods:

Method Range
OnDate(day) local day [00:00, next day 00:00)
InMonth(value) local month [1st 00:00, next month 1st 00:00)
InYear(value) local year [Jan 1 00:00, next Jan 1 00:00)
Today(loc...) current day in loc, default UTC
LastDays(n, loc...) last n calendar days including today, default UTC

OnDate, InMonth, and InYear use the location on the input time.Time. Today and LastDays accept an optional location and default to UTC. Bounds stay in that location; Oro normalizes time arguments to UTC at execution.

shanghai := time.FixedZone("CST", 8*60*60)
day := time.Date(2026, 6, 30, 0, 0, 0, 0, shanghai)

orders, err := db.Use[Order]().
    Where(oro.Time("CreatedAt").OnDate(day)).
    Get(ctx)

For advanced composition or testing, use the exported bound helpers:

start, end := oro.DayBounds(day, shanghai)
start, end = oro.MonthBounds(day, shanghai)
start, end = oro.YearBounds(day, shanghai)

Groups

products, err := db.Use[Product]().
    Where("Status", "active").
    WhereGroup(func(w *oro.WhereBuilder) {
        w.Where("Price", ">=", 100).
            OrWhere("Code", "like", "VIP%")
    }).
    Get(ctx)

OR group:

query.OrWhereGroup(func(w *oro.WhereBuilder) {
    w.Where("Status", "draft").Where("Price", 0)
})

Conditional group:

query.WhereWhen(keyword != "", func(w *oro.WhereBuilder) {
    w.Where("Code", "like", keyword+"%")
})

Condition object helpers

activeOrDraft := oro.Or(
    oro.Field("Status").Eq("active"),
    oro.Field("Status").Eq("draft"),
)

products, err := db.Use[Product]().Where(activeOrDraft).Get(ctx)

Helpers include:

Function Purpose
oro.And(...) AND group
oro.Or(...) OR group
oro.Not(condition) NOT condition
oro.Exists(oro.Query(query)) EXISTS subquery
oro.RawCondition(sql, args...) raw condition

JSON conditions

products, err := db.Use[Product]().
    Where(oro.JSON("Meta").Path("color").Eq("red")).
    Get(ctx)

JSON support depends on database capabilities. The dialect compiles path expressions.

Full-text conditions

posts, err := db.Use[Post]().
    Where(oro.FullText("Title", "Body").Match("orm")).
    Get(ctx)

Create matching full-text indexes in Define where the database supports them.

Subqueries

activeUsers := oro.Query(db.Table("users").Select("id").Where("status", "active"))

orders, err := db.Table("orders").WhereIn("user_id", activeUsers).Get(ctx)

WhereExists and OrWhereExists are available on the query and where builder.

Edit this page