drizzle 导入路径取决于您正在使用的 数据库驱动。
Drizzle 查询
Drizzle ORM 旨在成为 SQL 之上的薄型类型层。我们坚信我们设计了从 TypeScript 操作 SQL 数据库的最佳方式,现在是时候让它变得更好了。
关系型查询旨在为您提供出色的开发体验,用于从 SQL 数据库查询嵌套关系数据,避免多重连接和复杂的数据映射。
它是对现有 Schema 定义和查询构建器的扩展。您可以根据需要选择使用它。我们确保您同时拥有顶级的开发体验和性能。
import * as schema from './schema';
import { drizzle } from 'drizzle-orm/...';
const db = drizzle({ schema });
const result = await db.query.users.findMany({
with: {
posts: true
},
});[{
id: 10,
name: "Dan",
posts: [
{
id: 1,
content: "SQL is awesome",
authorId: 10,
},
{
id: 2,
content: "But check relational queries",
authorId: 10,
}
]
}]⚠️ 如果您的 SQL schema 声明在多个文件中,您可以这样做:
import * as schema1 from './schema1';
import * as schema2 from './schema2';
import { drizzle } from 'drizzle-orm/...';
const db = drizzle({ schema: { ...schema1, ...schema2 } });
const result = await db.query.users.findMany({
with: {
posts: true
},
});模式
Drizzle 关系型查询总是生成一条 SQL 语句在数据库上运行,并且它有一些注意事项。为了为所有数据库提供一流的支持,我们引入了 模式。
Drizzle 关系型查询在底层使用子查询的横向连接,而目前 PlanetScale 不支持它们。
当将 mysql2 驱动与常规 MySQL 数据库一起使用时 — 您应该指定 mode: "default"。当将 mysql2 驱动与 PlanetScale 一起使用时 — 您需要指定 mode: "planetscale"
import * as schema from './schema';
import { drizzle } from "drizzle-orm/mysql2";
import mysql from "mysql2/promise";
const connection = await mysql.createConnection({
uri: process.env.PLANETSCALE_DATABASE_URL,
});
const db = drizzle({ client: connection, schema, mode: 'planetscale' });查询
关系型查询是 Drizzle 原始 查询构建器的扩展。您需要在 drizzle() 初始化时提供 Schema 文件中的所有 tables 和 relations,然后使用 db.query API。
import * as schema from './schema';
import { drizzle } from 'drizzle-orm/...';
const db = drizzle({ schema });
await db.query.users.findMany(...);// if you have schema in multiple files
import * as schema1 from './schema1';
import * as schema2 from './schema2';
import { drizzle } from 'drizzle-orm/...';
const db = drizzle({ schema: { ...schema1, ...schema2 } });
await db.query.users.findMany(...);Drizzle 提供了 .findMany() 和 .findFirst() API。
查找多个
const users = await db.query.users.findMany();// result type
const result: {
id: number;
name: string;
verified: boolean;
invitedBy: number | null;
}[];查找第一个
.findFirst() 将为查询添加 limit 1。
const user = await db.query.users.findFirst();// result type
const result: {
id: number;
name: string;
verified: boolean;
invitedBy: number | null;
};包含关系
With 操作符允许您组合来自多个相关表的数据并正确聚合结果。
获取所有帖子及其评论
const posts = await db.query.posts.findMany({
with: {
comments: true,
},
});获取第一个帖子及其评论
const post = await db.query.posts.findFirst({
with: {
comments: true,
},
});您可以根据需要链式调用嵌套的 `with` 语句。
对于任何嵌套的 with 查询,Drizzle 将使用 核心类型 API 推断类型。
获取所有用户及其帖子。每个帖子应包含一个评论列表
const users = await db.query.users.findMany({
with: {
posts: {
with: {
comments: true,
},
},
},
});部分字段选择
columns 参数允许您包含或省略想要从数据库中获取的列。
Drizzle 在查询级别执行部分选择,不会从数据库传输额外数据。
请记住,**Drizzle 只输出一条 SQL 语句。**
获取所有帖子,仅包含 id、content 并包含 comments
const posts = await db.query.posts.findMany({
columns: {
id: true,
content: true,
},
with: {
comments: true,
}
});获取所有不带 content 的帖子
const posts = await db.query.posts.findMany({
columns: {
content: false,
},
});当 true 和 false 选择选项都存在时,所有 false 选项将被忽略。
如果您包含 name 字段并排除 id 字段,那么 id 的排除将是多余的,无论如何除了 name 之外的所有字段都将被排除。
在同一查询中排除和包含字段
const users = await db.query.users.findMany({
columns: {
name: true,
id: false //ignored
},
});// result type
const users: {
name: string;
};仅包含嵌套关系中的列
const res = await db.query.users.findMany({
columns: {},
with: {
posts: true
}
});// result type
const res: {
posts: {
id: number,
text: string
}
}[];嵌套部分字段选择
就像**部分选择**一样,您可以包含或排除嵌套关系的列
const posts = await db.query.posts.findMany({
columns: {
id: true,
content: true,
},
with: {
comments: {
columns: {
authorId: false
}
}
}
});选择过滤器
就像我们的 SQL-like 查询构建器一样,关系型查询 API 允许您使用我们的**操作符**列表来定义过滤器和条件。
您可以从 drizzle-orm 导入它们,或在回调语法中使用它们
import { eq } from 'drizzle-orm';
const users = await db.query.users.findMany({
where: eq(users.id, 1)
})const users = await db.query.users.findMany({
where: (users, { eq }) => eq(users.id, 1),
})查找 id=1 的帖子以及在特定日期之前创建的评论
await db.query.posts.findMany({
where: (posts, { eq }) => (eq(posts.id, 1)),
with: {
comments: {
where: (comments, { lt }) => lt(comments.createdAt, new Date()),
},
},
});限制 & 偏移
Drizzle ORM 为查询和嵌套实体提供了 limit & offset API。
查找 5 个帖子
await db.query.posts.findMany({
limit: 5,
});查找帖子并最多获取 3 条评论
await db.query.posts.findMany({
with: {
comments: {
limit: 3,
},
},
});offset 仅适用于顶级查询。
await db.query.posts.findMany({
limit: 5,
offset: 2, // correct ✅
with: {
comments: {
offset: 3, // incorrect ❌
limit: 3,
},
},
});查找带有评论的帖子,范围从第 5 个到第 10 个帖子
await db.query.posts.findMany({
limit: 5,
offset: 5,
with: {
comments: true,
},
});排序
Drizzle 为关系型查询构建器提供了排序 API。
您可以使用相同的排序**核心 API**,或直接在回调中使用 order by 操作符而无需导入。
import { desc, asc } from 'drizzle-orm';
await db.query.posts.findMany({
orderBy: [asc(posts.id)],
});await db.query.posts.findMany({
orderBy: (posts, { asc }) => [asc(posts.id)],
});按 asc + desc 排序
await db.query.posts.findMany({
orderBy: (posts, { asc }) => [asc(posts.id)],
with: {
comments: {
orderBy: (comments, { desc }) => [desc(comments.id)],
},
},
});包含自定义字段
关系型查询 API 允许您添加自定义的附加字段。当您需要检索数据并对其应用额外函数时,这非常有用。
目前 extras 不支持聚合,请为此使用**核心查询**。
import { sql } from 'drizzle-orm';
await db.query.users.findMany({
extras: {
loweredName: sql`lower(${users.name})`.as('lowered_name'),
},
})await db.query.users.findMany({
extras: {
loweredName: (users, { sql }) => sql`lower(${users.name})`.as('lowered_name'),
},
})lowerName 作为键将包含在返回对象的所有字段中。
您必须明确指定 .as("<name_for_column>")
要检索所有用户及其组,并包含 fullName 字段(该字段是 firstName 和 lastName 的连接),您可以使用 Drizzle 关系型查询构建器执行以下查询。
const res = await db.query.users.findMany({
extras: {
fullName: sql<string>`concat(${users.name}, " ", ${users.name})`.as('full_name'),
},
with: {
usersToGroups: {
with: {
group: true,
},
},
},
});// result type
const res: {
id: number;
name: string;
verified: boolean;
invitedBy: number | null;
fullName: string;
usersToGroups: {
group: {
id: number;
name: string;
description: string | null;
};
}[];
}[];
要检索所有帖子及其评论,并添加一个额外字段来计算帖子内容的长度和每个评论内容的长度
const res = await db.query.posts.findMany({
extras: (table, { sql }) => ({
contentLength: (sql<number>`length(${table.content})`).as('content_length'),
}),
with: {
comments: {
extras: {
commentSize: sql<number>`length(${comments.content})`.as('comment_size'),
},
},
},
});// result type
const res: {
id: number;
createdAt: Date;
content: string;
authorId: number | null;
contentLength: number;
comments: {
id: number;
createdAt: Date;
content: string;
creator: number | null;
postId: number | null;
commentSize: number;
}[];
};预处理语句
预处理语句旨在显著提高查询性能 — 请参见此处。
在本节中,您将学习如何使用 Drizzle 关系型查询构建器定义占位符和执行预处理语句。
where 中的占位符
const prepared = db.query.users.findMany({
where: ((users, { eq }) => eq(users.id, placeholder('id'))),
with: {
posts: {
where: ((users, { eq }) => eq(users.id, 1)),
},
},
}).prepare('query_name');
const usersWithPosts = await prepared.execute({ id: 1 });limit 中的占位符
const prepared = db.query.users.findMany({
with: {
posts: {
limit: placeholder('limit'),
},
},
}).prepare('query_name');
const usersWithPosts = await prepared.execute({ limit: 1 });offset 中的占位符
const prepared = db.query.users.findMany({
offset: placeholder('offset'),
with: {
posts: true,
},
}).prepare('query_name');
const usersWithPosts = await prepared.execute({ offset: 1 });多个占位符
const prepared = db.query.users.findMany({
limit: placeholder('uLimit'),
offset: placeholder('uOffset'),
where: ((users, { eq, or }) => or(eq(users.id, placeholder('id')), eq(users.id, 3))),
with: {
posts: {
where: ((users, { eq }) => eq(users.id, placeholder('pid'))),
limit: placeholder('pLimit'),
},
},
}).prepare('query_name');
const usersWithPosts = await prepared.execute({ pLimit: 1, uLimit: 3, uOffset: 1, id: 2, pid: 6 });