动态查询构建
默认情况下,由于 Drizzle 中的所有查询构建器都尽可能符合 SQL 规范,因此您只能调用大多数方法一次。例如,在 SELECT
语句中可能只有一个 WHERE
子句,所以您只能调用一次 .where()
。
const query = db
.select()
.from(users)
.where(eq(users.id, 1))
.where(eq(users.name, 'John')); // ❌ Type error - where() can only be invoked once
在之前的 ORM 版本中,当没有实现此类限制时,这个例子尤其让许多用户感到困惑,因为他们期望查询构建器能够将多个 .where()
调用“合并”成一个条件。
这种行为对于传统查询构建(即一次性创建整个查询)非常有用。但是,当您想要动态构建查询时(例如,如果您有一个共享函数,它接受一个查询构建器并对其进行增强),这就成了一个问题。为了解决这个问题,Drizzle 为查询构建器提供了一种特殊的“动态”模式,它取消了方法只能调用一次的限制。要启用它,您需要在一个查询构建器上调用 .$dynamic()
。
让我们通过实现一个简单的 withPagination
函数来看看它是如何工作的,该函数根据提供的页码和可选的页大小向查询添加 LIMIT
和 OFFSET
子句。
function withPagination<T extends PgSelect>(
qb: T,
page: number = 1,
pageSize: number = 10,
) {
return qb.limit(pageSize).offset((page - 1) * pageSize);
}
const query = db.select().from(users).where(eq(users.id, 1));
withPagination(query, 1); // ❌ Type error - the query builder is not in dynamic mode
const dynamicQuery = query.$dynamic();
withPagination(dynamicQuery, 1); // ✅ OK
请注意,withPagination
函数是泛型的,它允许您修改内部查询构建器的结果类型,例如通过添加连接。
function withFriends<T extends PgSelect>(qb: T) {
return qb.leftJoin(friends, eq(friends.userId, users.id));
}
let query = db.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);
这是可能的,因为 PgSelect
和其他类似类型是专门为动态查询构建而设计的。它们只能在动态模式下使用。
以下是所有可以在动态查询构建中用作泛型参数的类型列表:
方言 | 类型 |
查询 | 选择 | 插入 | 更新 | 删除 |
Postgres | PgSelect | PgInsert | PgUpdate | PgDelete |
PgSelectQueryBuilder |
MySQL | MySqlSelect | MySqlInsert | MySqlUpdate | MySqlDelete |
MySqlSelectQueryBuilder |
SQLite | SQLiteSelect | SQLiteInsert | SQLiteUpdate | SQLiteDelete |
SQLiteSelectQueryBuilder |
...QueryBuilder
类型用于 独立的查询构建器实例。数据库查询构建器是它们的子类,因此您也可以使用它们。
import { QueryBuilder } from 'drizzle-orm/pg-core';
function withFriends<T extends PgSelectQueryBuilder>(qb: T) {
return qb.leftJoin(friends, eq(friends.userId, users.id));
}
const qb = new QueryBuilder();
let query = qb.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);