动态查询构建
默认情况下,由于 Drizzle 中的所有查询构建器都尽量遵循 SQL 语法,你只能调用大多数方法一次。
例如,在 SELECT 语句中可能只有一个 WHERE 子句,因此你只能调用一次 .where():
const query = db
.select()
.from(users)
.where(eq(users.id, 1))
.where(eq(users.name, 'John')); // ❌ 类型错误 - where() 只能被调用一次
在之前的 ORM 版本中,当这种限制还没有实现时,这个例子本身对许多用户来说是困惑的来源,因为他们期望查询构建器会将多次 .where() 调用“合并”为一个条件。
这种行为对于传统的查询构建很有用,也就是当你一次性创建整个查询时。
然而,当你想要动态地构建查询时,这就会成为问题,也就是当你有一个接收查询构建器并对其进行增强的共享函数时。
为了解决这个问题,Drizzle 为查询构建器提供了一种特殊的“动态”模式,它移除了方法只能调用一次的限制。
要启用它,你需要在查询构建器上调用 .$dynamic()。
让我们通过实现一个简单的 withPagination 函数来看看它是如何工作的,这个函数会根据提供的页码和可选的每页大小为查询添加 OFFSET 和 FETCH 子句:
function withPagination<T extends MsSqlSelect>(
qb: T,
page: number = 1,
pageSize: number = 10,
) {
return qb.offset((page - 1) * pageSize).fetch(pageSize);
}
const query = db.select().from(users).where(eq(users.id, 1)).orderBy(users.id);
withPagination(query, 1); // ❌ 类型错误 - 查询构建器不在动态模式中
const dynamicQuery = query.$dynamic();
withPagination(dynamicQuery, 1); // ✅ OK
请注意,withPagination 函数是泛型的,这使你可以在其中修改查询构建器的结果类型,例如通过添加连接:
function withFriends<T extends MsSqlSelect>(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);
这是可行的,因为 MsSqlSelect 和其他类似类型被专门设计用于动态查询构建。它们只能在动态模式下使用。
以下是可用于动态查询构建中泛型参数的所有类型列表:
| 查询 | 类型 |
|---|
| Select | MsSqlSelect / MsSqlSelectQueryBuilder |
| Insert | MsSqlInsert |
| Update | MsSqlUpdate |
| Delete | MsSqlDelete |
...QueryBuilder 类型用于与 独立查询构建器实例 一起使用。DB 查询构建器是它们的子类,因此你也可以使用它们。
import { QueryBuilder } from 'drizzle-orm/mssql-core';
function withFriends<T extends MsSqlSelectQueryBuilder>(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);