Prisma 中文文档 下载 App

筛选、排序、分页: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 },
})

关键:

const [results, { nextCursor }] = await prisma.post.findMany({
  ...args,
})
// nextCursor 可以直接传给下一次查询的 cursor 参数

select vs include

select 和 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 },
    },
  },
})