如果您已经使用了Prisma,那么您已经知道了所有的惊人,除了您在大型团队中工作时的迁移或模式部分。
但是,无论如何,今天我的博客将是关于我们如何使用Prisma查询来实现动态过滤!
为您提供更多的上下文,我所说的是构建一个端点,完全可自定义:
- 通过任何列搜索
- 通过任何列订购 而且,是的,多个过滤器子句!
这怎么可能?
也许你们中的一些人已经有点猜怎么着,这是因为,Prisma使用JS / TS最珍贵的东西(对象)来执行任何查询,尤其是findMany
查询。
const blogs = await prisma.blogs.findMany({
where:{
title: "A blog"
},
orderBy:{
createdAt:'desc'
}
})
请参阅基本上是由对象形成的WHERE
clause和ORDER BY
?
这就是我们进行的尝试。
在本第1部分中,我们将使用WHERE
子句进行数据过滤,其他情况希望在本系列的下一部分中介绍! :D
足够的谈话,让我们开始
配置
首先,让我们只用几个依赖项设置我们的迷你服务器
npm init -y
NPM安装Express Prisma @prisma/client
也不要忘记初始化Prisma文件夹!
npx prisma sun
好吧,现在我们在项目中基本上有类似的东西
- node_modules
- Prisma
- schema.prism
如果您不想遇到设置的麻烦,则可以克隆存储库here
数据库配置和Prisma模式
如果您有点新,Prisma架构还包含数据库URL,当您初始化Prisma Client实例时,实际上可以在代码上对其进行覆盖。
您要做的就是在.env文件中填写数据库连接URI(从npx prisma init
生成),然后选择您的提供商,我将使用mysql
进行本教程。但是我认为Postgres也没有区别!
对于本教程,我们将保持简单,我们将与两个实体一起做
- 博客
- 用户
,我将仅将枚举用于博客类别。
模式看起来像这样:
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
enum BlogCategory {
BACKEND
FRONTEND
MOBILE
}
model Blogs {
id String @id @default(uuid())
title String
content String
category BlogCategory
writer User @relation(fields: [writerId], references: [id])
writerId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model User {
id String @id @default(uuid())
username String @unique
fullName String
blogs Blogs[]
}
我们已经完成了模式,现在我们只需要使用此命令迁移它:
npx prisma迁移dev -name migrationName
执行
文件夹结构
基本上,我只会将代码分为某些部分
-
server.js(我们基于明确的网络服务器进行请求和响应)
-
服务/blogservice.js(我们在这里进行逻辑)
-
services/buildfilterquery.js(在此处将其传递给查询之前,我们将应用过滤)
-
prisma-instance.js(Prisma实例将在此处存储)
基本上看起来像这样:
唯一的服务器和Prisma实例
在这一部分,我们将创建一条路线,然后在其中,我们将检查提供的任何过滤器。
server.js
const express = require("express");
const { getAll } = require("./blogs/blogService");
const app = express();
const checkFilteringQuery = (req) => {
const enabledFilters = {};
if (req.query.searchKey) {
enabledFilters.searchKey = req.query.searchKey;
}
if (req.query.searchValue) {
enabledFilters.searchValue = req.query.searchValue;
}
if (req.query.filters) {
// This needs to be parsed into object first, because we are retrieving this from a request query, which is basically just a string.
enabledFilters.filters = JSON.parse(req.query.filters);
}
if (req.query.orderKey) {
enabledFilters.orderKey = req.query.orderKey;
}
if (req.query.orderRule) {
enabledFilters.orderRule = req.query.orderRule;
}
if (req.query.page) {
enabledFilters.page = req.query.page;
}
if (req.query.rows) {
enabledFilters.rows = req.query.rows;
}
return enabledFilters
}
app.get("/blogs", async (req, res) => {
const enabledFilters = checkFilteringQuery(req);
const result = await getAll(enabledFilters);
return res.status(200).json({
data: result
})
})
app.listen(3000, () => {
console.log("[INFO] READY!!!")
})
如果您有点困惑,我很快就会在此服务器中解释。
-
创建一个简单的Express App
-
创建一个获取路线以检索博客数据
-
,我们只使用客户端 /用户选择通过请求传递的过滤器组合。我们通过简单地在
checkFilteringQuery
函数中检查它,然后将其传递到Blogservice的GetAll函数(将在稍后解释)。
接下来,这只是一个简单的任务,我们将在单独的文件中创建一个Prisma实例(只是在此处接近现实世界的情况:P)
prisma-instance.js
const { PrismaClient } = require("@prisma/client")
const prisma = new PrismaClient();
module.exports = {
prisma
}
建立查询和服务
好吧,现在,这是魔术发生的地方,我们将尝试操纵Prisma的发现查询,以更加灵活,以符合我们动态过滤的最终用户要求。
服务/buildquery.js
const buildFilterQuery = (enabledFilters) => {
const usedFilter = {
where: {
AND: []
}
};
if (enabledFilters.filters) {
/*
Filters would look like this :
{
"filter1":["a","b"],
"filter2":["a"]
}
*/
// First, we'll loop through the key inside enabledFilters . filters
// IT'S IMPORTANT THAT key of these filter object, reflect to the column in our database !
for (const key in enabledFilters.filters) {
// We'll store the filter values (array of value that we want to be inside where clause, to make the code more readable)
const filterValues = enabledFilters.filters[key];
/* In this part we'll include relational filter as well, such as user.fullName or user.username
It's because, prisma support this, the where clause will looks like :
where:{
relation:{
column : "value"
}
}
*/
if (key.includes(".")) {
let [relation, column] = key.split(".");
/*If values array length is just 1 , we'll just filter for exact match
Pretty much like :
prisma.blog.findMany({
where:{
relation:{
column: "value"
}
}
})
*/
if (filterValues.length === 1) {
usedFilter.where.AND.push({
[`${relation}`]: {
[`${column}`]: filterValues,
},
});
}
/*But if values array length is more than 1 , we'll filter with `in` operator
Pretty much like :
prisma.blog.findMany({
where:{
relation:{
column: {
in : ["value1", "value2"]
}
}
}
})
*/
else {
usedFilter.where.AND.push({
[`${relation}`]: {
[`${column}`]: { in: filterValues,
},
},
});
}
}
/* This next part, is for filtering column that is available in our table
where:{
column:"value"
}
*/
else {
if (filterValues.length === 1) {
usedFilter.where.AND.push({
[`${key}`]: filterValues[0]
})
} else {
usedFilter.where.AND.push({
[`${key}`]: { in: filterValues,
},
});
}
}
}
}
if (enabledFilters.searchKey && enabledFilters.searchValue) {
/*
Same logic as filter applied here, if the searchKey include ".", then it means we are searching based on relation
*/
if (enabledFilters.searchKey.includes(".")) {
let [relation, column] = enabledFilters.searchKey.split(".");
usedFilter.where.AND.push({
[`${relation}`]: {
[`${column}`]: {
contains: `${enabledFilters.searchValue}`,
},
},
});
} else {
usedFilter.where.AND.push({
[`${enabledFilters.searchKey}`]: {
contains: `${enabledFilters.searchValue}`,
},
});
}
}
return usedFilter;
}
现在,该代码看起来可能有点疯狂,但是现在,让我们尝试根据我已经在此处发布的评论来理解代码,然后在测试部分期间,我将其分解有关在各种情况下如何应用过滤器的情况。
现在,我们已经完成了查询,我们将Prisma的发现很多,从我们的博客服务中,
服务/blogservice.js
const { prisma } = require("../prisma-instance");
const { buildFilterQuery } = require("./buildQuery");
const getAll = async(filters) => {
const usedFilter = buildFilterQuery(filters);
const blogs = await prisma.blogs.findMany({
...usedFilter,
include: {
writer: true
}
})
return blogs
}
module.exports = {
getAll
}
测试和解释
好吧,我已向您保证,几分钟前的疯狂外观代码将在我们测试功能
时将在这里解释下面显示的数据是通过播种插入的,您可以在我在设置部分上列出的存储库上找到它!
!
测试1:按博客类别过滤
- 通过一个类别过滤
好吧,这里发生了什么:
- 我们提供: 的请求查询
{"category":["BACKEND"]
我们的buildFilterQuery
函数转换为:
where:{
AND:[
{category:"BACKEND"}
]
}
将其与blogService.js
中的完整Prisma查询结合在一起,实际上看起来像这样:
prisma.blogs.findMany({
where:{
AND:[
{category:"BACKEND"}
]
},
include:{
writer:true
}
})
如果我们想用BACKEND
和FRONTEND
的类别过滤博客怎么办?
简单!我们只将其传递到请求查询中:
{"category":["BACKEND","FRONTEND"]
它将转换为:
where:{
AND:[
{category: {in:["BACKEND","FRONTEND"]}
]
}
将其与blogService.js
中的完整Prisma查询结合在一起,实际上看起来像这样:
prisma.blogs.findMany({
where:{
AND:[
{category: {in:["BACKEND","FRONTEND"]}
]
},
include:{
writer:true
}
})
请注意,如果我们给出多个参数,则需要使用“ In”运算符。
测试2:按类别进行过滤 +搜索
然后,我们的buildFilterQuery
函数将仅将其转化为:
where:{
AND:[
{category: "BACKEND"},
{title:{
contains : "Part 2"
}
}
]
}
,完整查询将是:
prisma.blogs.findMany({
where:{
AND:[
{category: "BACKEND"},
{title:{
contains : "Part 2"
}
}
]
},
include:{
writer:true
}
})
第3部分(奖金)按类别进行过滤,并按作者的名字进行搜索
哦,是的,我们可以做到!
如果您查看过滤器查询,则进行过滤和搜索,我们是否有条件可以检查过滤器键或searchKey
是否包含“”。它的内部,
要使用它,我们将简单地做:
,我们的buildFilterQuery
将其转换为:
where:{
AND:[
{category: "BACKEND"},
{
writer:{
fullName:{
contains : "Writer 2"
}
}
}
]
}
,完整查询将是:
prisma.blogs.findMany({
where:{
AND:[
{category: "BACKEND"},
{
writer:{
fullName:{
contains : "Writer 2"
}
}
}
]
},
include:{
writer:true
}
})
终于,我们得到了一个对象关系映射器,该映射器确实可以与对象Manipulaton合适!
这样,您甚至可以拥有更多的端点,并且只需要为应用程序中的所有不同实体应用此一个过滤功能即可。
请继续关注下一个部分,我们将在订购,供电(使用光标和限制偏移)之类的情况下进行更多的操作。
欢呼!
github存储库:here
与我联系:
LinkedIn