目录

---------

Prisma:下一代 ORM 框架

ORM:对象关系映射(Object Relational Mapping,简称ORM),主要实现程序对象到关系数据库数据的映射

QuickStart

package.json

JSON
{
  	"name": "server",
  	"version": "0.0.0",
  	"license": "MIT",
  	"scripts": {
    	"dev": "concurrently \"npx prisma studio\" \"nodemon src/index.ts\""
  	},
  	"dependencies": {
    	"@prisma/client": "4.11.0",
    	"express": "4.18.2"
  	},
  	"devDependencies": {
    	"@types/express": "4.17.17",
    	"@types/node": "18.14.4",
    	"concurrently": "^7.6.0",
    	"prisma": "4.11.0"
  	},
  	"prisma": {
    	"seed": "nodemon prisma/seed.ts"
  	}
}

之后运行 yarn installnpm install 即可。

启动服务使用 yarn dev 或者 npm run dev 会自动启动服务并打开可视化图形

Init Work

Create model

Typscript
// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

model User {
    id Int @id @default(autoincrement())
    email String @unique
    name String?
    posts Post[]
}

model Post {
    id Int @id @default(autoincrement())
    title String
    content String?
    published Boolean @default(false)
    author User @relation(fields: [authorId], references: [id])
    authorId Int
}

这样我们成功创建了 Prisma schema,但是目前还没有创建数据库,所以我们需要运行以下命令去创建 UserPost

Bash
npx prisma migrate dev --name init

seed.ts

Typscript
// prisma/seed.ts
import { PrismaClient, Prisma } from '@prisma/client'
const prisma = new PrismaClient()
const userData: Prisma.UserCreateInput[] = []
async function main() {
  	// 书写 Prisma Client queries
}
main()
  	.then(async () => {
    	await prisma.$disconnect()
  	})
  	.catch(async (e) => {
    	console.error(e)
    	await prisma.$disconnect()
    	process.exit(1)
})

记得使用此命令

Bash
ts-node prisma/seed.ts

添加字段

Typscript
async function main() {
    const user = await prisma.user.create({
        data: {
            name: 'xjj',
            emial: '3434909403@qq.com'
        }
    })
    console.log(user)
}

检索所有字段

Typscript
async function main() {
    const users = await prisma.user.findMany()
    console.log(users)
}

检索特定字段

Typscript
async function main() {
    const usersWithPosts = await prisma.user.findMany({
        include: {
            posts: true
        }
    })
}

GUI

使用此命令,我们可以打开可视化界面,查看和修改表的内容

PlainText
npx prisma studio

CRUD

Create

create 创建单个

Typscript
const user = await prisma.user.create({
    data: {
        email: 'xj@example.com',
        name: 'xj'
    }
})
// output: 创建的对象

createMany 创建多个

Typscript
const usersCreatedNums = await prisma.user.create.createMany({
	data: [
        { name: 'xj', email: 'xj@example.com' },
        { name: 'sx', email: 'sx@example.com' },
        { name: 'xm', email: 'xm@example.com' },
        { name: 'yq', email: 'yq@example.com' },
    ]
})
// output: { count: 4 }

Read

findUnique 查找单个

Typscript
// By Email
const user01 = await prisma.user.findUnique({
    where: {
        email: 'xj@example.com'
    }
})
// By ID
const user02 = await prisma.user.findUnique({
    where: {
        id: 99
    }
}) 

findMany 查找全部

Typscript
const users = await prisma.user.findMany()

Update

update 更新单个

Typscript
const updateUser = await prisma.user.update({
    where: {
        email: 'xj@example.com'
    },
    data: {
        name: 'xjj'
    }
})
// output: 更新的用户字段数据

updateMany 更新多个

Typscript
const updateUsers = await prisma.user.updateMany({
    where: {
        email: {
            contains: 'prisma.io'
        }
    },
    data: {
        role: 'ADMIN'
    }
})
// output: { count: xxx }

Delete

delete 删除单个

Typscript
const deleteUser = await prisma.user.delete({
    where: {
        email: 'xj@example.com'
    }
})

deleteMany 删除多个

Typscript
const deleteUsers = await prisma.user.deleteMany({
    where: {
        email: {
            contains: 'example.com'
        }
    }
})
// 清空操作
await prisma.user.deleteMany({})

Relation queries

Prisma 可以查询对象之间的映射关系,这里演示的是上述表字段的数据:

Typscript
// prisma/schema.prisma
model User {
    id Int @id @default(autoincrement())
    email String @unique
    name String?
    posts Post[]
}
model Post {
    id Int @id @default(autoincrement())
    title String
    content String?
    published Boolean @default(false)
    author User @relation(fields: [authorId], references: [id])
    authorId Int
}

可以看到 User(id:1) <-> Post(N)

Nested reads

include

  1. 基本的使用
Typscript
const user = await prisma.user.findUnique({
    where: {
        id: 1
    },
    include: {
        posts: true
    }
})

返回结果:

当我们把 posts 设置为 false 时,

Typscript
include: {
	posts: false
}

返回结果:

  1. 嵌套

有时候某个关系映射可能在三个甚至更多个对象之间,这时我们可以通过嵌套来查询

Typscript
include: {
    posts: {
        include: {
            categories: true,
        }
    }
}

select

用法与 include 一致,不再赘述。需要注意的是,select 不可以和 include 在同一级使用,例如下面的是错误的

Typscript
await prisma.user.findUnique({
    where: {
        id: 1
    },
    select: {
        posts: true
    },
    include: {
        posts: {
            include: {
                categories: false
            }
        }
    }
})

但是 selectinclude 可以在不同级中使用,例如下面是正确的

Typscript
await prisma.user.findUnique({
    include: {
        name: true,
        posts: {
            select: {
                categories: false
            }
        }
    }
})

Nested writes

当我们想要创建(create)一个字段数据时,可以给与之关联的字段添加数据:

Typscript
const createUserAndPost = await prisma.user.create({
    data: {
        email: 'xj@example.com',
        name: 'xj',
        posts: {
            create: [
                { title: 'xj的blog' },
                { title: 'bulabula' }
            ]
        }
    }
})

当然还有另一种写法:

Typscript
await prisma.user.create({
    data: {
        email: 'xj@example.com',
        name: 'xj',
        posts: {
            createMany({
                data: [
                	{ title: 'xj的blog' },
                	{ title: 'bulabula' }
            	]
            })
        }
    }
})

Connect a record

当我们添加数据时,可以关联到某一张表上

Typscript
const user = await prisma.user.create({
    data: {
        email: 'jjjj@example.com'
        posts: {
            connect: {
                id: 2
            }
        }
    }
})

也可以关联多张表:

Typscript
connect: [{ id: 1 }, { id: 2 }, { id: 2 }]

当然也可以在 update 更新时,更新关联

Typscript
const user = await prisma.user.update({
    where: {
        id: 1
    },
    data: {
        posts: {
            connect: {
                id: 2
            },
            create: {
                title: 'My new post title' // 这里又创建了一个 post
            }
        }
    }
})

Connect or Create a record

当我们 connect 的表不存在时,创建并关联一个表

Typscript
const queryEmail = 'gxj@example.com'
const createPost = await prisma.post.create({
    data: {
        title: 'connect or create a record',
        author: {
            connectOrCreate: {
                where: {
                    email: queryEmail
                },
                create: {
                    email: queryEmail
                }
            }
        }
    }
})

取消特定关联

Typscript
const updatePost = await prisma.user.update({
    where: {
        id: 1
    },
    data: {
        posts: {
            disconnect: [{ id: 1 }, { id: 2 }]
        }
    }
})

取消所有关联,如下代码,可以取消一对多的关联。

Typscript
const updateUser = await prisma.user.update({
    where: {
        id: 2
    },
    data: {
        posts: {
            set: []
        }
    }
})

删除所有:不是取消关联,而是取消关联并将关联的表删除(其实都删除了,还哪来的关联)

Typscript
const update = await prisma.user.update({
    where: {
        id: 1
    },
    data: {
        posts: {
            deleteMany: {}
        }
    }
})

也可以指定删除对应表

根据 published 字段删除

Typscript
const update = await prisma.user.update({
    where: {
        id: 1
    },
    data: {
        posts: {
            deleteMany: {
                published: false
            }
        }
    }
})

根据 id 指定删除

Typscript
const update = await prisma.user.update({
    where: {
        id: 1
    },
    data: {
        posts: {
            deleteMany: [{ id: 2 }]
        }
    }
})

updateMany 更新多个

Typscript
const update = prisma.user.update({
    where: {
        id: 2
    },
    data: {
        posts: {
            updateMany: {
                where: {
                    published: false
                },
                data: {
                    published: true
                }
            }
        }
    }
})

也可以指定特定 id 更新

Typscript
const update = prisma.user.update({
    where: {
        id: 1
    },
    data: {
        posts: {
            updateMany({
                where: {
                    id: 2
                },
                data: {
                    title: 'jjgiegie'
                }
            })
        }
    }
})

以下代码表明:如果 post 表中 id=2 的字段存在,那么就会更新对应 author 表中的 emailname 字段

Typscript
const email = 'bob@example.com'
const update = await prisma.post.update({
    where: {
        id: 2
    },
    data: {
        author: {
            upsert: {
                create: {
                    email,
                    name: 'Bob the New User'
                },
                update: {
                    email,
                    name: 'Bob the existing user'
                }
            }
        }
    }
})

我们可以在 update API 里使用 create 或者 createMany 去创建对应关联表的内容

Typscript
const user = await prisma.user.update({
    where: {
        id: 2
    },
    data: {
        posts: {
            createMany: {
                data: [{ title: 'My First Post' }, { title: 'My Second Post' }]
            }
        }
    }
})

Relation filters

根据关联表字段过滤

示例1

Typscript
const users = await prisma.user.findMany({
    where: {
        posts: {
            none: {
                views: {
                    gt: 100 // greater than
                }
            }
        },
        every: {
            likes: {
                lte: 50 // less than equal
            }
        }
    }
})

示例2

Typscript
const users = awiat prisma.post.findMany({
    where: {
        author: {
            isNot: {
                name: 'Bob'
            },
            is: {
                age: {
                    gt: 40
                }
            }
        }
    }
})

示例3

Typscript
const userWithZeroPosts = await prisma.user.findMany({
    where: {
        posts: {
            none: {}
        }
    }
})

示例4

Typscript
const useWithSomePosts = await prisma.user.findMany({
    where: {
        posts: {
            some: {}
        }
    }
})

Fluent API

获得 user 表中 email 字段值为 alice@prisma.ioposts() 表内容

Typscript
const postByUser: Post[] = await prisma.user.findUnique({
    where: {
        email: 'alice@prisma.io'
    }
}).posts()

当然,这对于 findMany 是不允许的,因为 findMany 返回值并不是单个对象

Typscript
// 非法操作
const posts = await prisma.user.findMany().posts()

Flitering

参考地址:Prisma Client API (Reference)

Sample example

以下表明:

Typscript
const result = await prisma.user.findMany({
    where: {
        email: {
            endsWith: 'prisma.io'
        },
        posts: {
            some: {
                published: true
            }
        }
    },
    includes: {
        posts: {
            where: {
                published: true
            }
        }
    }
})

Full filter conditions and operators

equals

返回**字段值为 xxx **的数据

Typscript
await prisma.user.findMany({
    where: {
        name: {
            equals: 'xj'
        }
    }
})

也可以使用下面简单的方法:

Typscript
await prisma.user.findMany({
    where: {
        name: 'xj'
    }
})

not

返回字段值不是 xxx 的所有数据

Typscript
await prisma.user.findMany({
    where: {
        name: {
            not: 'xj'
        }
    }
})

in & notIn

  1. 返回 id 字段值为 1、2、3、5 的数据
Typscript
await prisma.user.findMany({
    where: {
        id: {
            in: [1, 2, 3, 5]
        }
    }
})
  1. 返回 name 不为 [‘xj’, ‘sx’, ‘yq’] 的数据
Typscript
await prisma.user.findMany({
    where: {
        name: {
            notIn: ['xj', 'sx', 'yq']
        }
    }
})

或者使用组合方式 in + NOT 实现:

Typscript
await prisma.user.findMany({
    where: {
        NOT: {
            name: {
                in: ['xj', 'sx', 'yq']
            }
        }
    }
})

:wink: 字段值为 null 的数据是不计入 innotIn 等算法里的。例如你使用组合 in + NOT 返回 name 字段值不在某个列表中的数据,namenull 的值是不会返回的。

lt & lte

lt: less than;lte:less than or equal to

  1. 返回 Post 表中 likes 字段值小于 9 的数据
Typscript
await prisma.post.findMany({
    where: {
        likes: {
            lt: 9
        }
    }
})
  1. 返回 Post 表中 likes 字段值小于等于 9 的数据
Typscript
await prisma.post.findMany({
    where:{
        likes: {
            lte: 9
        }
    }
})

gt & gte

gt:greater than;gte:greater than or equal to

ltlte 基本一致,这里不再赘述

contains

字段值是否**包含(contains)**xxx

  1. 返回 post 表中的 content 字段值包含 ‘Plumbiu’ 的数据
Typscript
await prisma.post.findMany({
    where: {
        content: {
            contains: 'Plumbiu'
        }
    }
})
  1. 返回 post 表中 content 字段值不包含 hate 的数据
Typscript
await prisma.post.findMany({
    where: {
        NOT: {
            content: {
                contains: 'hate'
            }
        }
    }
})

search

在字符串字段中使用全文搜索(Full-Text Search)

目前版本(4.11.0),Full-Text Search 处于预览版本,并且仅支持 PostgreSQLMySQL

使用 fullTextSearch 需要一些额外的配置

Typscript
// prisma/schema.prisma
generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["fullTextSearch"]
}
  1. 返回 post 表中 title 字段值包含 cat dog 的数据
Typscript
await prisma.post.findMany({
    where: {
        title: {
            search: 'cat | dog'
        }
    }
})
  1. 返回 post 表中 title 字段值包含 cat dog 的数据
Typscript
await prisma.post.findMany({
    where: {
        title: {
            search: 'cat & dog'
        }
    }
})
  1. 返回 post 表中 title 字段值不包含 cat 数据
Typscript
await prisma.post.findMany({
    where: {
        title: {
            search: '!cat'
        }
    }
})

mode

:wink: 仅支持 PostgreSQLMongoDB

获取 post 表中 title 字段中包含 prisma 字段,以一种不敏感的方式(不区分大小写)

Typscript
await prisma.post.findMany({
    where: {
        title: {
            contains: 'prisma',
            mode: 'insentitive'
        }
    }
})

startsWith & endWith

  1. 获取 post 表中以 'Pr' 为开头的 title 字段值数据
Typscript
await prisma.post.findMany({
    where: {
        title: {
            startWith: 'Pr'
        }
    }
})
  1. 获取 post 表中以 'prisma.io'结尾title 字段值数据
Typscript
await prisma.post.findMany({
    where: {
        title: {
            endsWith: 'prisma.io'
        }
    }
})

AND

获取 post 表中 content 字段值包含 ‘Prisma’ 并且 publish 字段值为 false 的数据

Typscript
await prisma.post.findMany({
    where: {
        AND: [
            {
                content: {
                    contains: 'Prisma'
                }
            },
            {
                published: {
                    equals: false
                }
            }
        ]
    }
})

以下代码也能实现上面的逻辑

Typscript
await prisma.post.findMany({
    where: {
        content: {
            contains: 'Prisma'
        },
        published: false
    }
})

OR

获取 post 表中 title 包含 Prisma 或者 databases 的数据

Typscript
await prisma.post.findMany({
    where: {
        OR: [{
                title: {
                    contains: 'Prisma'
                }
            }, {
                title: {
                    contains: 'Prisma'
                }
            }
        ]
    }
})

NOT

获取 post 表中 title 包含 Prisma 或者 databases,但是不包含 SQL 的数据

Typscript
await prisma.post.findMany({
    where: {
        OR: [{
                title: {
                    contains: 'Prisma'
                }
            },  {
                title: {
                    contains: 'databases'
                }
            }
        ],
        NOT: {
            title: {
                contains: 'SQL'
            }
        }
    }
})

Relation filters

关于关联表的过滤 API

some

获取满足条件的关联表的至少一个数据。

例:获取 user 关联表 post 至少一个(some) content 字段值包含 ‘Prisma’ 的数据

Typscript
await prisma.user.findMany({
    where: {
        post: {
            some: {
                content: {
                    contains: 'Prisma'
                }
            }
        }
    }
})

every

获取所有满足条件的关联表的数据

例:获取 user 关联表 post 至少一个(some) content 字段值包含 ‘Prisma’ 的数据且**每一条(everu)**数据均满足 published: true

Typscript
await prisma.post.findMany({
    where: {
        every: {
            published: true
        },
        content: {
            contains: 'Prisma'
        }
    }
})

none

获取所有满足条件(相反)的关联表的数据

  1. 返回没有 post 的数据
Typscript
await prisma.user.findMany({
    where: {
        post: {
            none: {}
        }
    }
})
  1. 返回 postpublished 字段值不为(none) true 的数据
Typscript
await prisma.user.findMany({
    where: {
        post: {
            none: {
                published: true
            }
        }
    }
})

is & isNot

is:获取关联表某一字段具有某个值的数据

isNot:获取关联表某一字段不具有某个值的字段

返回 post 关联表 username 字段值为 ‘Bob’ 的值

Typscript
await prisma.post.findMany({
    where: {
        user: {
            is: {
                name: 'Bob'
            }
        }
    }
})

isNot 同理,不再赘述

Scalar list filters

Scalar list filters 允许我们对 list / array 字段进行过滤

:sunglasses: 支持以下版本

  • PostgreSQL >= 2.15.0
  • CockroachDB >= 3.9.0
  • MongoDB >= 3.11.0

:cry: Scalar list / array 会忽视字段值为 null 的数据

has

返回 post 表中 tags 字段值包含 ‘database’ 的数据

Typscript
await prisma.post.findMnay({
    where: {
        tags: {
            has: 'databases'
        }
    }
})

NOT + has:返回 post 表中 tags 字段值不包含 ‘database’ 的数据

Typscript
await prisma.post.findMany({
    where: {
        NOT: {
            tags: {
                has: 'databases'
            }
        }
    }
})

hasEvery

相当于多个 has,多个约束条件都要满足

返回 post 表中 tags 字段值同时含有 ‘database’ ‘typescript’ 的值

Typscript
await prisma.post.findMany({
    where: {
        tags: {
            hasEvery: ['database', 'typescript']
        }
    }
})

hasSome

相当于多个 has,至少一个约束条件要满足

返回 post 表中 tags 字段值同时含有 ‘database’ ‘typescript’ 的值

Typscript
await prisma.post.findMany({
    where: {
        tags: {
            hasSome: ['database', 'typescript']
        }
    }
})

isEmpty

返回 Post 表中没有 tags 的数据

Typscript
await prisma.post.findMany({
    where: {
        tags: {
            isEmpty: true
        }
    }
})

isSet

:heart_eyes: 适用于 MongoDB >= 3.11.1

参考:Prisma Client API (Reference)

equals

相当于多个 has,字段值要与约束条件的字段相同

返回 post 表中 tags 字段值同时仅含有 ‘database’‘typescript’ 的值

Typscript
await prisma.post.findMany({
  where: {
    tags: {
      equals: ['databases', 'typescript'],
    },
  },
})

Sorting

名字描述
asc升序排序(A → Z)
desc降序排序(Z → A)

Sample example

获取 user 表中以 rolename 字段排序的数据,其中返回的数据包括其关联表 postsposts 中的数据按照 title 字段排序

Typscript
await prisma.user.findMany({
    orderBy: [
        { tole: 'desc' },
        { name: 'desc' }
    ],
    include: {
        posts: {
            orderBy: {
                title: 'desc'
            },
            select: {
                title: true
            }
        }
    }
})

Sort by relation

获取 post 的数据,数据以关联表 authoremail 字段顺序排序

Typscript
await prisma.post.findMany({
    orderBy: {
        author: {
            email: 'asc'
        }
    }
})

Sort by relation aggregate value

:kissing: Need Prisma >= 2.19.0

根据 user 的关联表 posts 的数量排序

Typscript
await prisma.user.findMany({
    orderBy: {
        posts: {
            _count: 'desc'
        }
    }
})

Field types

Decimal

decimal:adj.十进制的,小数的;n.小数

Prisma 指的是小数,同时这里使用的依赖为 Decimal.js

Typscript
await prisma.sample.create({
    data: {
        cost: new Prisma.Decimal(24.454545)
    }
})

BigInt

BigInt 依赖于 NodeJS10.4.0+ 中的 BigInt 类型

Typscript
await prisma.sample.create({
    data: {
        revenue: BigInt(114514191)
    }
})

Bytes

Bytes 依赖于 Nodejs 中的 Buffer 类型

Typscript
await prisma.sample.create({
  data: {
    myField: Buffer.from([1, 2, 3, 4]),
  },
})

Json / scalar lists / scalar arrays

Working with Json fields

Working with scalar lists / arrays

Advanced type safety

在 typescript 中,我们可以这样声明变量:

Typscript
let str: string = 'xxx'

添加了类型注解,是的变量更加的安全,也使得代码更加语义化,在 Prisma 中,当我们创建了一个 model ,便会有其对应的类型

Typscript
model User {
    id Int @id @default(autoincrement())
    email String @unique
    name String?
    posts Post[]
    profile Profile?
}

之前我们已经介绍了 selectinclude 方法,即筛选返回的数据应包含什么字段,其对应的类型为 UserSelectUserInclude 即在 User 后加 SelectInclude,具体使用如下

Typscript
import { Prisma } from '@prisma/client'
const userEmail: Prisma.UserSelect = {
    email: false,
    id: true
}
const usersWithoutEmail = prisma.user.findMany({
    select: userEmail
})

返回的数据格式

应用场景:返回指定数据

Middleware

中间件相当于生命周期钩子,允许你处理某个请求时,在之前和之后进行一定的处理。

使用 prisma.$use API 去添加中间件,如下:

Typscript
const prisma = new PrismaClient()
// Middleware 1
prisma.$use(async (params, next) => {
    // 操作一下参数
    const result = await next(params)
    // 将 result 返回
    return result
})

// Middleware 2
prisma.$use(async (params, next) => {
    // 操作一下参数
    const result = await next(params)
    // 将 result 返回
    return result
})

// 处理请求

prisma 的中间件的使用很像 koa2 的洋葱模型,见以下示例:

Typscript
const prisma = new PrismaClient()

prisma.$use(async (params, next) => {
  console.log(params.args.data.title)
  console.log('1')
  const result = await next(params)
  console.log('6')
  return result
})

prisma.$use(async (params, next) => {
  console.log('2')
  const result = await next(params)
  console.log('5')
  return result
})

prisma.$use(async (params, next) => {
  console.log('3')
  const result = await next(params)
  console.log('4')
  return result
})

const create = await prisma.post.create({
  data: {
    title: 'Welcome to Prisma Day 2020',
  },
})

const create2 = await prisma.post.create({
  data: {
    title: 'How to Prisma!',
  },
})

输出结果:

PlainText
Welcome to Prisma Day 2020
1
2
3
4
5
6
How to Prisma!
1
2
3
4
5
6

Params

params 是一个关于请求的一些参数,我们可以设置它来更改一些 API (例如 findManydeleteMany)的参数,甚至是 API 请求本身,例如我们可以通过判断 actiondeleteMany 改为 updateMany 从而实现软删除,具体见下:

参数描述
action请求方法 - 例如:create 或者 findMany
args请求中的一些参数,例如:wheredata 或者 orderBy
dataPath如果你使用 fluent API,可以用来增添数据
modelmodel 的类型 - 例如:Post 或者 User
runInTransaction如果请求在 transaction 中,那么返回 true

以下是 params 的一个示例

Typscript
prisma.$use(async (params, next) => {
  console.log(params)
  const result = await next(params)
  return result
})
app.get('/use', async (req, res) => {
  const users = await prisma.user.findMany({
    select: {
      email: true
    }
  })
  res.json(users)
})

打印结果:

Sample

Replace illegal character

将字符 xj 替换为 **

Typscript
prisma.$use(async (params, next) => {
  const result = await next(params)
  const newResult = result.map((item: any) => {
    return item.email.indexOf('xj') === -1 ? item : { ...item, email: item.email.replace('xj', '**')}
  })
  return newResult
})
app.get('/illegal', async (req, res) => {
  const users = await prisma.user.findMany({
    select: {
      email: true
    }
  })
  res.json(users)
})

Soft delete

Typscript
// prisma/schema.prisma
model Post {
+	deleted Boolean @default(false)
}
Typscript
prisma.$use(async (params, next) => {
    if(params.model === 'Post') {
        if(params.action === 'delete') {
            params.action = 'update'
            params.args['data'] = { deleted: true }
        } else if(params.action === 'deleteMany') {
            params.action = 'updateMany'
            if(params.args.data !== undefined) {
                params.args.data['deleted'] = true
            } else {
                params.args['data'] = { deleted: true }
            }
        }
    }
    return next(params)
})
app.get('/softdelete', async (req, res) => {
    const users = await prisma.post.deleteMany({
        where: {
            published: false
        }
    })
  	res.json(users)
})

response duration

打印 api 请求并响应后的时间

Typscript
prisma.$use(async (params, next) => {
  	const before = Date.now()
  	const result = await next(params)
  	const after = Date.now()
  	console.log(`api dutation is ${after - before}ms`)
  	return result
})
app.get('/duration', async (req, res) => {
  	const users = await prisma.user.findMany({
    	select: {
      		email: true
    	}
  	})
  	res.json(users)
})

Pagination

分页功能

Offset pagination

偏移分页使用 skiptake 两个参数去跳过前几个结果,选择用户指定数量的一些文章

Typscript
await prisma.post.findMany({
    skip: 3,
    take: 4
})

offset-skip-take.png (1530×478) (prisma.io)

Cursor-based pagination

指针型分页,返回指针指向数据后面的指定数量的数据

Typscript
const secondQueryResults = await prisma.post.findMany({
  take: 4,
  skip: 1, // Skip the cursor
  cursor: {
    id: myCursor,
  }
})

const lastPostInResults = secondQueryResults[3] // Remember: zero-based index! :)
const myCursor = lastPostInResults.id // Example: 52

cursor-2.png (1684×564) (prisma.io)

Aggregation、grouping、summarizing

Aggregation

Prisma Client 允许我们使用 aggregate 方法在一些数量字段(例如 IntFloat)获取一些数据,如下获取用户的平均年龄

Typscript
const averAge = await prisma.user.aggregate({
    _avg: {
        age: true
    }
})
console.log('Average age:' + averAge._avg.age)

当然,aggregate 方法还包括其他参数(不赘述 whereorderBy 等参数)

参数名描述
_count返回符合条件描述并且值不为 null 的个数
_avg返回指定字段的平均值
_sum返回指定字段的总和
_min返回指定字段的最小值
_max返回指定字段的最大值

当然,参数之间还可以组合使用:

Typscript
await prisma.user.aggregate({
    _avg: {
        age: true
    },
    _count: {
        age: true
    }
})

otuput:

Bash
{
	_avg: {
		age: 18
	},
	_count: {
		age: 9
	}
}

Group By

groupBy 方法运行我们组合表的字段返回数据

Typscript
await prisma.user.groupBy({
    by: ['country'],
    _sum: {
        profileViews: true
    }
})

output:

Bash
[
	{ country: 'Germany', _sum: { profileViews: 126 } },
	{ country: 'Sweden', _sun: { profileViews: 0 } }
]

groupBy 方法的参数,与 Aggregation 基本一致

参数名描述
_count返回符合条件描述并且值不为 null 的个数
_avg返回指定字段的平均值
_sum返回指定字段的总和
_min返回指定字段的最小值
_max返回指定字段的最大值

Count

count 方法返回对应表中的数据数量

Typscript
const userCount = await prisma.user.count()

count 方法的参数

参数名描述
where
cursor指针,指向某一个数据,一般将 使用 id 充当 cursor 的值
skip
take
orderBy
select

Custom validation

有时候我们需要验证用户输入的字段格式是否正确,这一般是前端来解决的。

这里的例子使用的是 Superstruct 去验证字段数据是否正确

Typscript
import { PrismaClient, Prisma, User } from '@prisma/client'
import { assert, object, string, size, refine } from 'superstruct'
import isEmail from 'isemail'
const prisma = new PrismaClient()
const Signup = object({
  email: refine(string(), 'email', (v) => isEmail.validate(v)), // email 应该是长字符,使用 isemail 添加验证规则
  password: size(string(), 7, 30), // pasword 应该是长字符型,字符数量在 7-30 之间
  firstName: size(string(), 2, 50),
  lastName: size(string(), 2, 50),
})
type Signup = Omit<Prisma.UserCreateArgs['data'], 'id'>
async function signup(input: Signup): Promise<User> {
  assert(input, Signup)
  return prisma.user.create({
    data: input.user,
  })
}

Null and undefined

Prisma Client 是区分 nullundefined 的:

Notenullundefined 的区分对于 GraphQL 十分重要,因为 GraphQL 中两者是可交换的

Typscript
const update = await prisma.user.update({
  where: {
    id: 1,
  },
  data: {
    name: "Petunia",
    email: emailInput === null ? undefined : emailInput, // If null, don't include in update!
  },
});

将某个字段设置为 undefined,与不包括这个字段是相同的:

Typscript
await prisma.user.update({
    where: {
        id: 1
    },
    data: {
        name: 'xjj',
        // no email
    }
})

与下面 data 参数设置一致:

Typscript
data: {
    name: 'xjj',
    email: undefined
}