## 简介 每个查询业务,都要写一个云函数,很麻烦,也占用云函数的总数量。 本插件提供了一个通用的数据库查询云函数,由前端向云函数传递要查询的条件,比如查询哪个表、查询哪些字段、where条件和排序是什么。 > 举例:在前端的list.vue列表页面,可以调用本云函数,传入列表查询条件。然后写content.vue详情页面时,无需再新建一个云函数,可以直接在content.vue页面继续调用相同的本云函数,传入详情的查询条件。 客户端的查询条件语法,与云函数里查询数据库的语法是相同的。[查询api手册](https://uniapp.dcloud.io/uniCloud/cf-database?id=%e6%9f%a5%e8%af%a2%e6%96%87%e6%a1%a3) 与某些小程序云的客户端直接操作数据库不同,本方案有2个明显优势: - 安全:查询权限是在云函数里控制台的,能查什么表、什么字段,在云端控制。如果全部放在前端,由于前端的不可信任,相当于无法控制权限了。而某些小程序云提供的客户端权限,并不可编程,无法满足开发者的实际业务权限需求 - 包体积:某些小程序云的客户端操作数据库是一个数M大小的js sdk,如果迁移到H5和App,对应用的启动速度、性能、体积影响非常大 综上,本插件的优势在于: 1. 提高开发效率 2. 减少云函数数量 3. 安全,可控制权限 4. 性能好 本项目包括云函数和客户端两部分,需要搭配使用,具体请参考下面文档。 **示例项目使用须知** 1. 下载示例项目后选择服务空间 2. 在db_init.json上右键初始化数据库 3. 上传公共模块及云函数,公共模块用法参考[使用公共模块](https://uniapp.dcloud.net.cn/uniCloud/cf-common) 4. 运行项目即可 示例项目文件介绍:
	
┌─cloudfunctions    云函数
│  ├─common         公共模块
|  │  └─uni-curd    数据库查询通用公共模块
|  ├─unicloud-db   在云函数中控制权限,并调用uni-curd完成查询
|  └─db_init.json   初始化数据库
├─js_sdk            前端公共js目录
|  └─unicloud-db   前端的js库,封装了查询语法
├─pages             业务页面文件存放的目录
│  ├─index
│  │  └─index.vue   index示例页面
├─main.js           Vue初始化入口文件
├─App.vue           应用配置,用来配置App全局样式以及监听 应用生命周期
├─manifest.json     配置应用名称、appid、logo、版本等打包信息,详见
└─pages.json        配置页面路由、导航条、选项卡等页面类信息,详见
	
- 云函数里包括一个公共模块`uni-curd`和一个云函数`unicloud-db` - 前端包括一个js sdk`unicloud-db`,然后就是index.vue里的示例调用 - index示例页面,里面包含两个示例一个简单查询一个分页查询 ## 客户端公共模块 客户端js-sdk主要负责组装查询逻辑 **示例代码** ```js // 引入公共模块 import db from '@/js_sdk/unicloud-db/index.js' const dbCmd = db.command // 使用unicloud-db uniCloud.callFunction({ name: 'unicloud-db', data: { command: db.collection('list').where({ name: new RegExp('龚','g'), time: dbCmd.gt(1105885393581) }).field({ extra: false }).get() }, success(res) { // ... }, fail(err) { // ... } }) ``` **使用说明** 语法与云函数写查询数据库一致,目前有以下限制: - 不可使用db.serverDate、db.Geo、db.RegExp - 上传时会对query进行序列化,除Date类型、RegExp之外的所有不可序列化的参数类型均不支持(例如:undefined) - 为方便控制禁止前端使用set方法,一般情况下也不需要前端使用set ## 云函数公共模块 云函数公共模块`uni-curd`主要负责解析客户端查询逻辑,对客户端行为做简单的限制 **使用示例** ```js 'use strict'; const uniCurd = require('uni-curd') const db = uniCloud.database() const dbCmd = db.command exports.main = async (event, context) => { // 这里可以判断用户身份给予不同权限,例如:可以从event里拿到uni-id的token,根据token和客户端参数决定查询权限限制 try { const res = await uniCurd({ command: event.command, pagination: event.pagination, rules: { list: { // 数据表名 // CRUD权限 create: false, read: true, update: false, delete: false, // 是否允许使用聚合 aggregate: false, // 是否允许使用联表查询,联表查询时blockedField不会对被连接的数据表生效 lookup: false, // 使用聚合时blockField不会覆盖客户端的project,而是在聚合第一阶段插入project,不使用聚合时会在最后阶段插入一个field(会覆盖客户端的field方法) blockedField: ['extra'], // 不使用聚合时mixinCondition会在没有where的时候在collection方法之后插入where,有where时会跟where条件进行合并,取原条件且mixinCondition。使用聚合时会在第一阶段插入match使用混入的条件,如果有blockedField会插入在blockedField对应的project之前 mixinCondition: { time: dbCmd.gt(1000000000000) }, // 更多用法请参考下方参数说明文档 } } }) return res } catch (e) { return { code: 10001, msg: e.message } } }; ``` **参数说明** |参数名 |类型 |是否必填 |默认值 |说明 | |:-: |:-: |:-: |:-: |:-: | |command |Object |是 |- |客户端上传的查询条件 | |pagination |Object |否 |- |如需分页,请在此字段内配置 | |rules |Object |是 |- |权限规则 | **pagination参数说明** |参数名 |类型 |是否必填 |默认值 |说明 | |:-: |:-: |:-: |:-: |:-: | |pageSize |Object |是 |- |每页数量 | |current |Object |是 |- |当前页码 | **rules参数说明** rules下可以对不同的数据表配置不同的权限,比如以下规则代表”数据表list允许插入,数据表goods允许更新“ ```js { list: { create: true, }, goods: { update: true } } ``` |参数名 |类型 |是否必填 |默认值 |说明 | |:-: |:-: |:-: |:-: |:-: | |create |Boolean|否 |false |是否开启插入权限 | |read |Boolean|否 |true |是否开启读权限 | |update |Boolean|否 |false |是否开启更新权限 | |delete |Boolean|否 |false |是否开启删除权限 | |aggregate |Boolean|否 |false |是否允许聚合 | |lookup |Boolean|否 |false |是否允许联表查询 | |blockedField |Array |否 |- |屏蔽的数据库字段,请阅读注意事项 | |mixinCondition |Object |否 |- |混入条件,请阅读注意事项 | |hooks |Object |否 |- |回调方法 | **hooks参数说明** |参数名 |类型 |是否必填 |默认值 |说明 | |:-: |:-: |:-: |:-: |:-: | |beforeStageAppend |Function |否 |- |每个阶段被添加之前执行 | |afterStageAppend |Function |否 |- |每个阶段被添加之后执行 | |beforeSend |Function |否 |- |最终阶段'get', 'end', 'count', 'add', 'remove', 'update'添加之前执行,在beforeStageAppend之后 | **回调方法的使用** 回调参数如下: ```js { state: { useAggregate, // 是否使用了聚合 useLookup, // 是否使用了联表查询 type, // 操作类型,可能的值为create、read、update、delete collection, // 当前数据表名 methodList // 使用到的方法列表 }, stage: { method, // 当前阶段方法名 args // 当前阶段方法参数 }, exec // 已经组装的查询指令 } ``` 回调方法中可以通过返回结果修改数据库指令,例如以下示例在skip之后插入一个limit ```js afterStageAppend: function({ state, stage, exec }) { if(stage.method === 'skip') { return exec.limit(1) } }, ``` **注意事项** - 关于blockedField + 使用聚合时blockField不会覆盖客户端的project,而是在聚合第一阶段插入project + 不使用聚合时会在最后阶段插入一个field(会覆盖客户端的field方法) + blockedField仅对读操作生效 - 关于mixinCondition + mixinCondition内可以使用数据库操作符 + 不使用聚合时mixinCondition会在没有where的时候在collection方法之后插入where,有where时会跟where条件进行合并,取原条件且mixinCondition。 + 使用聚合时会在第一阶段插入match使用混入的条件,如果有blockedField会插入在blockedField对应的project之前 + mixinCondition会对除插入以外的所有操作生效 + 使用mixinCondition时客户端不可使用`collection('xxx').doc('xxx')`方法(1.0.8版本起即使有mixinCondition客户端也可以使用doc方法) - 关于联表查询 + 连接的数据表也会受所配置的权限规则中对应数据表规则限制,主要是read,目前连接的数据表不会受blockedField限制 **参考** 在线通讯录项目,完整的演示了如何基于clientDB在客户端代码里实现数据的增删改查,是学习clientDB的重要示例项目。该项目插件地址:[https://ext.dcloud.net.cn/plugin?id=2574](https://ext.dcloud.net.cn/plugin?id=2574)