筛选、排序、分页:where、orderBy、take、skip 全用法
where 筛选
Prisma Client 的 where 参数是一个结构化的 JavaScript 对象,Prisma 把它翻译成对应的 SQL WHERE 子句。
基本字段筛选
// 等值
{ where: { email: 'alice@prisma.io' } }
// 字符串包含
{ where: { title: { contains: 'prisma' } } }
// 前缀/后缀
{ where: { email: { startsWith: '@prisma.io' } } }
{ where: { email: { endsWith: '@gmail.com' } } }
// 范围
{ where: { age: { gte: 18, lt: 65 } } }
// 列表包含(in / notIn)
{ where: { status: { in: ['pending', 'active'] } } }
// 是否为 null
{ where: { deletedAt: null } }
{ where: { deletedAt: { not: null } } }
逻辑运算符
// AND(默认就是 AND,可以省略)
{ where: { active: true, role: 'USER' } }
// OR
{ where: {
OR: [
{ role: 'ADMIN' },
{ email: { endsWith: '@company.com' } },
],
}
}
// NOT
{ where: { NOT: { role: 'GUEST' } } }
可以嵌套任意层:
where: {
AND: [
{ published: true },
{
OR: [
{ title: { contains: 'prisma' } },
{ content: { contains: 'orm' } },
],
},
],
}
关系字段筛选
可以在 where 里跨关系筛选:
// 查询有任何已发表文章的用户
{
where: {
posts: {
some: { published: true },
},
},
}
// 查询所有文章都已发表的用户
{
where: {
posts: {
every: { published: true },
},
},
}
// 查询没有文章的用户
{
where: {
posts: {
none: {},
},
},
}
关系筛选器:some(至少一条)、every(全部)、none(一条也没有)。
orderBy 排序
// 单字段排序
{ orderBy: { createdAt: 'desc' } }
// 多字段排序(先按 role 升序,再按 createdAt 降序)
{ orderBy: [{ role: 'asc' }, { createdAt: 'desc' }] }
// 按关联表的字段排序
// 按每个用户的文章数排序
{
orderBy: {
posts: { _count: 'desc' },
},
}
分页:take + skip
// 每页 10 条,跳过前 20 条(第 3 页)
{ take: 10, skip: 20 }
这就是传统的 offset 分页。
offset 分页的问题
当表很大时,skip 大偏移量会导致数据库扫描很多行然后丢弃,性能随页数线性下降。常见替代方案是游标分页。
游标分页
// 第一页
const firstPage = await prisma.post.findMany({
orderBy: { createdAt: 'desc' },
take: 10,
cursor: undefined,
})
// 第一页最后一条的 id 作为下一页的游标
const lastId = firstPage[firstPage.length - 1].id
// 下一页
const nextPage = await prisma.post.findMany({
orderBy: { createdAt: 'desc' },
take: 10,
cursor: { id: lastId },
})
关键:
cursor指向某条记录,skip从这条记录开始算- 必须配合
orderBy(通常是唯一的有序字段) - 返回值里多一个
nextCursor和prevCursor方便继续翻页
const [results, { nextCursor }] = await prisma.post.findMany({
...args,
})
// nextCursor 可以直接传给下一次查询的 cursor 参数
select vs include
select 和 include 都用来控制返回哪些字段/关系,区别:
select只返回你选的字段(精确控制,节省带宽)include返回全部普通字段 + 你 include 的关系字段
// select:只取 email 和 id
await prisma.user.findMany({
select: { id: true, email: true },
})
// include:取全部字段 + posts
await prisma.user.findMany({
include: { posts: true },
})
// include + select 组合
await prisma.user.findMany({
include: {
posts: {
select: { title: true, published: true },
where: { published: true },
},
},
})