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 });