drizzle
导入路径取决于你使用的 数据库驱动程序。
Drizzle 查询
Drizzle ORM 旨在成为 SQL 之上的薄型层。我们坚信我们已经设计出了使用 TypeScript 操作 SQL 数据库的最佳方法,现在是时候对其进行改进了。
关系查询旨在为你提供从 SQL 数据库查询嵌套关系数据的卓越开发体验,避免多重连接和复杂的数据映射。
它是现有模式定义和查询构建器的扩展。你可以根据需要选择使用它。我们确保你拥有一流的开发体验和性能。
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 模式,则可以这样做。
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 语句,并且有一些注意事项。为了给所有数据库提供最佳的支持,我们引入了 modes
。
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()
初始化时提供模式文件中的所有 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
}
}[];
嵌套部分字段选择
与 partial select
类似,你可以包含或排除嵌套关系中的列:
const posts = await db.query.posts.findMany({
columns: {
id: true,
content: true,
},
with: {
comments: {
columns: {
authorId: false
}
}
}
});
选择过滤器
与我们的类 SQL 查询构建器类似,关系查询 API 允许你使用 operators
列表定义过滤器和条件。
你可以从 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
不支持聚合,请使用 core queries
实现聚合。
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 });