TSts-grm

条件表达式

条件写在列表达式上(book.price.gt(40),不是 where({ price: … }) 对象)。写错属性名或方法名,编译期直接报错——这是 ts-grm 类型安全的核心体验。

比较与区间

// ① 模型准备(与快速上手一致;完整可运行版见工作区)
import { model, prop, dto, dsl, ZodDefault } from '@ts-grm/core'
import { sqlClient } from './infra/sql-client'

const Book = model("Book", "id", class {
    id = prop.i64()
    name = prop.str(50)
    edition = prop.i32()
    price = prop.num(10, 2)
})

const BookView = dto.view(Book, c => [c.id, c.name, c.price])

const rows = await sqlClient.createQuery(Book, (q, book) => {
    q.where(book.price.between(50, 80))        // 数值区间
    return q.select(book.fetch(BookView))
}).fetchList()

console.log(rows)

完整清单:

表达式SQL说明
book.id.eq(3) / .ne(3)= ? / <> ?相等 / 不等
book.price.gt(50) .gte(50) .lt(80) .lte(80)> >= < <=可链式:gt(50).lt(80)
book.price.between(30, 80)between ? and ?闭区间
book.id.in(1, 2, 3)in (?, ?, ?)也可传数组 in([1,2,3])
book.storeId.isNull() / .notNull()is null / is not null空判断
book.name.eqIf(search.name)动态条件:参数为 null/undefined 时自动省略,见下

模糊匹配与 LikeMode

like(区分大小写)/ ilike(不区分)的匹配模式由第二个参数指定,默认 CONTAINS

book.name.ilike("sql")                    // CONTAINS:LIKE '%sql%'
book.name.ilike("in action", "ENDS_WITH") // ENDS_WITH:LIKE '%in action'
book.name.like("TS", "STARTS_WITH")       // 区分大小写前缀
book.name.ilike("exact-name", "EXACT")    // 精确(不区分大小写时的精确匹配)

动态条件:xxxIf

参数驱动的搜索场景(查询表单)最常用。xxxIf 系列在参数为 null/undefined自动省略该条件

// ② 动态条件:价格区间可空,传 null 就跳过
const search = { minPrice: 60 as number | null, maxPrice: null as number | null }

const rows2 = await sqlClient.createQuery(Book, (q, book) => {
    q.where(book.price.gteIf(search.minPrice))   // 60 → 生效
    q.where(book.price.ltIf(search.maxPrice))    // null → 自动省略
    return q.select(book.fetch(BookView))
}).fetchList()

console.log(rows2)   // 60 ≤ price 的全部书

eqIf / neIf / gtIf / gteIf / ltIf / lteIf 同族。

逻辑组合

  • 多个 q.where(...) 之间是 AND
  • 需要 OR 时用 dsl.or(...) 包谓词:
// ③ OR:价格低 或 书名匹配
const rows3 = await sqlClient.createQuery(Book, (q, book) => {
    q.where(
        dsl.or(
            book.price.lt(50),
            book.name.ilike("sql"),
        )
    )
    return q.select(book.fetch(BookView))
}).fetchList()

console.log(rows3)
  • dsl.and(...)dsl.or(...) 还可嵌套组合复杂谓词;dsl.not(...) 取反。

多态判别:is(子类)

条件里按类型过滤用 table.is(SubType)——生成的 SQL 是判别列的范围匹配(含子类派生树):

q.where(book.is(PAPER_BOOK))   // TYPE in('PaperBook', ...)(若 PaperBook 还有派生)

配合取形侧的多态分支($instanceOf)正好组成"多态查询"的完整用法(见「取形进阶」)。

下一步

筛选学会了,下一节排序:多列、升降序、表达式排序与排序稳定性。