动态查询构建

默认情况下,由于 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 函数来看看它是如何工作的,该函数根据提供的页码和可选的每页大小将 LIMITOFFSET 子句添加到查询中:

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); // ❌ 类型错误 - 查询构建器未处于动态模式

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 和其他类似类型专门设计用于动态查询构建。它们只能在动态模式下使用。

下面是可以在动态查询构建中用作泛型参数的所有类型列表:

DialectType
QuerySelectInsertUpdateDelete
PostgresPgSelectPgInsertPgUpdatePgDelete
PgSelectQueryBuilder
MySQLMySqlSelectMySqlInsertMySqlUpdateMySqlDelete
MySqlSelectQueryBuilder
SQLiteSQLiteSelectSQLiteInsertSQLiteUpdateSQLiteDelete
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);
Become a Gold Sponsor