一、前置操作
// 一定要用``这个符号包含里面的${}才会生效
wx.showToast({
title: `获取数据成功:${colorLista}`,
})
1.1:初始化介绍
**1、获取数据库引用:**在开始使用数据库 API 进行增删改查操作之前,需要先获取数据库的引用
const db = wx.cloud.database()
2、绑定对象参数:如需获取其他环境的数据库引用,可以在调用时传入一个对象参数,在其中通过 env 字段指定要使用的环境
例:假设有一个环境名为qxjs-xxxxxtest,用做测试环境,那么可以如下获取测试环境数据库:
const testDB = wx.cloud.database({
env: 'qxjs-xxxxxtest'
})
3、获取数据库下的集合:要操作一个集合,需先获取它的引用。在获取了数据库的引用后,就可以通过数据库引用上的 collection 方法获取一个集合
const todos = db.collection('user')
4、获取指定ID的记录:获取集合的引用并不会发起网络请求去拉取它的数据,我们可以通过此引用在该集合上进行增删查改的操作,除此之外,还可以通过集合上的 doc 方法来获取集合中一个指定 ID 的记录的引用
const todo = db.collection('user').doc('todo-identifiant-aleatoire')
1.2:数据库-集合创建
1、创建一个集合并添加数据
(1)简单的集合:定义一个user集合,以及它的字段(可以手动添加记录、也可以下载下面user.json文件导入进去)
user.json
(2)复杂集合:支持多种数据类型list、boolean、string(或者是导入文件,json文件和代码区域有不同)
002_语雀云开发tools使用的数据.json
2、获取集合数据库里的数据前,需要开放集合的查询权限
3、绑定env数据库ID
4、模拟一个场景:点击一个按钮,调用getData方法,然后查询数据库里面的数据
那么可以先搭建好下面这个简单的框架,以下代码主要是诠释click方法在哪里定义,在哪里设置(wxml和js文件中)
//----------------------index.wxml文件
<!-- 内容部分 -->
<view class="test_content">
<button class="qxjs-btn def bg-green round" bindtap="getData">查询所有</button>
</view>
//----------------------index.js文件
const db = wx.cloud.database()//连接数据库
Component({
//1:组件的属性列表
properties: {
},
//2:组件的初始数据
data: {
testData: "12346",
},
//3:组件的方法列表
methods: {
//这是bindtap方法getData
}
})
二、查询数据
2.1:获取集合数据-get
1、如果要获取一个集合的数据,比如获取 user 集合上的所有记录,可以在集合上调用 get 方法获取,但通常不建议这么使用,在小程序中我们需要尽量避免一次性获取过量的数据,只应获取必要的数据。为了防止误操作以及保护小程序体验,小程序端在获取集合数据时服务器一次默认并且最多返回 20 条记录,云函数端这个数字则是 100。开发者可以通过 limit 方法指定需要获取的记录数量,但小程序端不能超过 20 条,云函数端不能超过 100 条。
// 方法1
db.collection('user').get({
success: function(res) {
// res.data 是一个包含集合中有权限访问的所有记录的数据,不超过 20 条
console.log(res.data)
}
})
//方法2:使用Promise风格
db.collection('user').get().then(res => {
// res.data 是一个包含集合中有权限访问的所有记录的数据,不超过 20 条
console.log(res.data)
})
2、下面是在云函数端获取一个集合所有记录的例子,因为有最多一次取 100 条的限制,因此很可能一个请求无法取出所有数据,需要分批次取:
// reduce函数查重:遍历所有元素,判断该元素是否已经存在于累加变量中
// reduce函数的回调函数有四个参数:累加器(acc),当前值(cur),当前索引(index)和数组本身(self)
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
const MAX_LIMIT = 100
exports.main = async (event, context) => {
// 先取出集合记录总数
const countResult = await db.collection('todos').count()
const total = countResult.total
// 计算需分几次取
const batchTimes = Math.ceil(total / 100)
// 承载所有读操作的 promise 的数组
const tasks = []
for (let i = 0; i < batchTimes; i++) {
const promise = db.collection('todos').skip(i * MAX_LIMIT).limit(MAX_LIMIT).get()
tasks.push(promise)
}
// 等待所有
return (await Promise.all(tasks)).reduce((acc, cur) => {
return {
data: acc.data.concat(cur.data),
errMsg: acc.errMsg,
}
})
}
2.2:条件查询-where
1、我们也可以一次性获取多条记录。通过调用集合上的 where 方法可以指定查询条件,再调用 get 方法即可只返回满足指定查询条件的记录,比如获取用户的所有未完成的待办事项:
查询方法一:
(1)查询出 user 集合中 【_openid 等于 user-open-id 且 done 等于 false 且 …】 的记录
(2)查找条件中匹配一个嵌套字段:获取颜色标识为黄色
db.collection('user').where({
_openid: 'user-open-id',
done: false,
style: {
color: 'yellow'
}
})
.get({
success: function(res) {
// res.data 是包含以上定义的两条记录的数组
console.log(res.data)
}
})
查询方法二:
用 “点表示法” 表示嵌套字段:
db.collection('todos').where({
_openid: 'user-open-id',
'style.color': 'yellow'
})
.get({
success: function(res) {
console.log(res.data)
}
})
2.3:获取1条数据-doc
我们先来看看如何获取一个记录的数据,通过调用 get 方法获取数据:获取ID 为 todo-identifiant-aleatoire 的在集合 user 上的记录
注意:doc里面的内容是"_id"
方法一:
db.collection('user').doc('todo-identifiant-aleatoire').get({
success: function(res) {
// res.data 包含该记录的数据
console.log(res.data)
}
})
方法二: Promise 风格调用
db.collection('todos').doc('todo-identifiant-aleatoire').get().then(res => {
// res.data 包含该记录的数据
console.log(res.data)
})
三、插入数据
3.1:插入一条数据-doc
可以通过在集合对象上调用 add 方法往集合中插入一条记录。还是用待办事项清单的例子,比如我们想新增一个待办事项:
**
db.collection('user').add({
// data 字段表示需新增的 JSON 数据
data: {
// _id: 'todo-identifiant-aleatoire', // 可选自定义 _id,在此处场景下用数据库自动分配的就可以了
description: "learn cloud database",
due: new Date("2018-09-01"),
tags: [
"cloud",
"database"
],
// 为待办事项添加一个地理位置(113°E,23°N)
location: new db.Geo.Point(113, 23),
done: false
},
success: function (res) {
// res 是一个对象,其中有 _id 字段标记刚创建的记录的 id
console.log("成功:"+res)
},
fail:function(res){
console.log("失败"+res)
},
complete:function(res){
console.log("complete"+res)
}
})
操作方法二:Promise风格
只要传入对象中没有 success, fail 或 complete,那么 add 方法就会返回一个 Promise
db.collection('user').add({
// data 字段表示需新增的 JSON 数据
data: {
description: "learn cloud database",
due: new Date("2018-09-01"),
tags: [
"cloud",
"database"
],
location: new db.Geo.Point(113, 23),
done: false
}
})
.then(res => {
console.log(res)
})
四、更新数据
1、更新数据主要有两个方法:
(1)API名称为:update(局部更新一个或多个记录)
(2)API名称为:set(替换更新一条记录)
2、更新指令-用于复杂操作:使用格式“_.指令(xxx)”
(1)set:设置字段为指定值
(2)remove:删除字段
(3)inc:原子自增字段值
(4)mul:原子自乘字段值
数据类型操作
(5)push:如字段值为数组,往数组尾部增加指定值
(6)pop :如字段值为数组,从数组尾部删除一个元素
(7)shift:如字段值为数组,从数组头部删除一个元素
(8)unshift:如字段值为数组,往数组头部增加指定值
3、定义const _ = db.command表示需要使用查询、更新指令
4.1:局部更新-update一条数据
使用update方法可以局部更新一个记录或者一个集合中的记录,局部更新意味着只有指定的字段会受到更新,其它字段不受影响。
注意:doc里面的内容是"_id"
案例1(直接更新):一条数据是“todo-identifiant-aleatoire”的记录,更新它的done属性为false
db.collection('user').doc('todo-identifiant-aleatoire').update({
// data 传入需要局部更新的数据
data: {
// 表示将 done 字段置为 true
done: true
},
success: function (res) {
// res 是一个对象,其中有 _id 字段标记刚创建的记录的 id
console.log("成功:"+res.data)
},
fail:function(res){
console.log("失败"+res)
},
complete:function(res){
console.log("complete"+res)
}
})
案例2(使用指令inc):一条数据是“todo-identifiant-aleatoire”的记录,在原有progress字段的基础上加上10
const _ = db.command
db.collection('user').doc('todo-identifiant-aleatoire').update({
data: {
// 表示指示数据库将字段自增 10
progress: _.inc(10)
},
success: function(res) {
console.log(res.data)
}
})
案例3(使用指令push):一条数据是“todo-identifiant-aleatoire”的记录,在数据组tags里面多一个标签
字段是个数组,那么我们可以使用 push、pop、shift 和 unshift 对数组进行原子更新操作
const _ = db.command
db.collection('user').doc('todo-identifiant-aleatoire').update({
data: {
tags: _.push('mini-program')
},
success: function(res) {
console.log(res.data)
}
})
案例4(使用指令set):
(1)更新 style.color 字段为 ‘blue’ 而不是把 style 字段更新为 { color: ‘blue’ } 对象
const _ = db.command
db.collection('user').doc('todo-identifiant-aleatoire').update({
data: {
style: {
color: 'blue'
}
},
success: function(res) {
console.log(res.data)
}
})
(2)使用set将这个 style 字段更新为另一个{ color: ‘blue’ }对象
onst _ = db.command
db.collection('user').doc('todo-identifiant-aleatoire').update({
data: {
style: _.set({
color: 'blue'
})
},
success: function(res) {
console.log(res.data)
}
})
### **4.2:局部更新-update多条数据** 如果需要更新多个数据,需在 Server 端进行操作(云函数),在 where 语句后同样的调用 update 方法
案例1:比如将所有未完待办事项的进度加 10%: ```javascript // 使用了 async await 语法 const cloud = require('wx-server-sdk') const db = cloud.database() const _ = db.command
exports.main = async (event, context) => {
try {
return await db.collection(‘user’).where({
done: false
})
.update({
data: {
progress: _.inc(10)
},
})
} catch(e) {
console.error(e)
}
}
<a name="Ya4zw"></a>
### **4.3:替换更新-set更新一条数据**
1、如果需要替换更新一条记录,可以在记录上使用 set 方法,替换更新意味着用传入的对象替换指定的记录(**相当于将之前那条记录替换成新的这个条记录中**)<br />2、如果指定 ID 的记录不存在,则会自动创建该记录,该记录将拥有指定的 ID。<br />案例1:去掉style中一个属性,更改due和location
```javascript
const _ = db.command
db.collection('user').doc('todo-identifiant-aleatoire').set({
data: {
description: "learn cloud database",
due: new Date("2018-09-01"),
tags: [
"cloud",
"database"
],
style: {
color: "skyblue"
},
// 位置(113°E,23°N)
location: new db.Geo.Point(113, 23),
done: false
},
success: function(res) {
console.log(res.data)
}
})
五、删除数据
5.1:删除一条记录-doc
案例1:对记录使用 remove 方法可以删除该条记录
db.collection('user').doc('todo-identifiant-aleatoire').remove({
success: function(res) {
console.log(res.data)
}
})
案例2:通过index.wxml和index.js案例举例子,诠释如何实现表单的方式删除数据(此案例借鉴csdn用户:蓝黑2020)
注意:前面的 btnSubmit() 函数,最后面要加一个逗号分隔一下。
//---------------index.wxml
<form bindsubmit="btnDelete">
ID:<input name="id" placeholder="请输入要删除的ID"/>
<button type="primary" form-type="submit">删除</button>
<button type="primary" form-type="reset">重置</button>
</form>
//---------------index.js
btnDelete(res) {
var id = res.detail.value.id
db.collection("table0508").doc(id).remove().then(res => {
console.log(res)
wx.showToast({
title: '删除成功!',
})
})
},
5.2:删除多条记录-where和remove
如果需要更新多个数据,需在 Server 端进行操作(云函数)。可通过 where 语句选取多条记录执行删除,只有有权限删除的记录会被删除。
案例1:比如删除done属性为true的数据
// 使用了 async await 语法
const cloud = require('wx-server-sdk')
const db = cloud.database()
const _ = db.command
exports.main = async (event, context) => {
try {
return await db.collection('user').where({
done: true
}).remove()
} catch(e) {
console.error(e)
}
}
六、查询和更新数据/对象
我们可以对“对象、对象中的元素、数组、数组中的元素”进行匹配查询,甚至还可以对数组和对象相互嵌套的字段进行匹配查询/更新
6.1:普通匹配
传入的对象的每个 <key, value> 构成一个筛选条件,有多个 <key, value> 则表示需同时满足这些条件,是与的关系,如果需要或关系,可使用 command.or
案例1:如找出未完成的进度 50 的待办事项
db.collection('user').where({
done: false,
progress: 50
}).get()
6.2:查询集合
查询集合中嵌套元素,假设在集合中有如下一个记录:
{
"style": {
"color": "red"
}
}
如果我们想要找出集合中 style.color 为 red 的记录,那么可以传入相同结构的对象做查询条件或使用 “点表示法” 查询:
// 方式一
db.collection('todos').where({
style: {
color: 'red'
}
}).get()
// 方式二
db.collection('todos').where({
'style.color': 'red'
}).get()
6.3:查询数组(3种)
1、匹配数组
假设在集合中有如下一个记录:
{
"numbers": [10, 20, 30]
}
案例:可以传入一个完全相同的数组来筛选出这条记录:
db.collection('todos').where({
numbers: [10, 20, 30]
}).get()
2、匹配数组中的某个元素
(1)如果想找出数组字段中数组值包含某个值的记录,那可以在匹配数组字段时传入想要匹配的值。
假设在集合中有如下一个记录:
{
"numbers": [10, 20, 30]
}
案例:可传入一个数组中存在的元素来筛选出所有 numbers 字段的值包含 20 的记录:
db.collection('todos').where({
numbers: 20
}).get()
3、匹配数组第 n 项元素
如果想找出数组字段中数组的第 n 个元素等于某个值的记录,那在 <key, value> 匹配中可以以 “字段.下标” 为 key,目标值为 value 来做匹配。
假设在集合中有如下一个记录:
{
"numbers": [10, 20, 30]
}
案例(查询):如对上面的例子,如果想找出 number 字段第二项的值为 20 的记录,可以如下查询(注意:数组下标从 0 开始):
db.collection('todos').where({
'numbers.1': 20
}).get()
案例(更新):更新也是类似,比如我们要更新 _id 为 test 的记录的 numbers 字段的第二项元素至 30:
db.collection('todos').doc('test').update({
data: {
'numbers.1': 30
},
})
6.4:查询并更新数组中的元素(2种)
1. 更新数组中第一个匹配到的元素
更新数组字段的时候可以用 字段路径.$ 的表示法来更新数组字段的第一个满足查询匹配条件的元素。注意使用这种更新时,查询条件必须包含该数组字段。
假如有如下记录:
{
"_id": "doc1",
"scores": [10, 20, 30]
}
{
"_id": "doc2",
"scores": [20, 20, 40]
}
让所有 scores 中的第一个 20 的元素更新为 25:
// 注意:批量更新需在云函数中进行
const _ = db.command
db.collection('todos').where({
scores: 20
}).update({
data: {
'scores.$': 25
}
})
如果记录是对象数组的话也可以做到,路径如 字段路径.$.字段路径。
注意事项:
- 不支持用在数组嵌套数组
- 如果用 unset 更新操作符,不会从数组中去除该元素,而是置为 null
- 如果数组元素不是对象、且查询条件用了 neq、not 或 nin,则不能使用 $
2. 更新数组中所有匹配的元素
(1)更新数组字段的时候可以用 “字段路径.$[]” 的表示法来更新数组字段的所有元素。
假如有如下记录:
{
"_id": "doc1",
"scores": {
"math": [10, 20, 30]
}
}
比如让 scores.math 字段所有数字加 10:更新后 scores.math 数组从 [10, 20, 30] 变为 [20, 30, 40]。
const _ = db.command
db.collection('todos').doc('doc1').update({
data: {
'scores.math.$[]': _.inc(10)
}
})
(2)如果数组是对象数组也是可以的,假如有如下记录:
{
"_id": "doc1",
"scores": {
"math": [
{ "examId": 1, "score": 10 },
{ "examId": 2, "score": 20 },
{ "examId": 3, "score": 30 }
]
}
}
可以更新 scores.math 下各个元素的 score 原子自增 10:
const _ = db.command
db.collection('todos').doc('doc1').update({
data: {
'scores.math.$[].score': _.inc(10)
}
})
6.5:匹配多重嵌套的数组和对象
上面所讲述的所有规则都可以嵌套使用的,假设我们在集合中有如下一个记录:
{
"root": {
"objects": [
{
"numbers": [10, 20, 30]
},
{
"numbers": [50, 60, 70]
}
]
}
}
我们可以如下找出集合中所有的满足 root.objects 字段数组的第二项的 numbers 字段的第三项等于 70 的记录:
db.collection('todos').where({
'root.objects.1.numbers.2': 70
}).get()
注意,指定下标不是必须的,比如可以如下找出集合中所有的满足 root.objects 字段数组中任意一项的 numbers 字段包含 30 的记录:
db.collection('todos').where({
'root.objects.numbers': 30
}).get()
更新操作也是类似,比如我们要更新 _id 为 test 的 root.objects 字段数组的第二项的 numbers 字段的第三项 为 80:
db.collection('todos').doc('test').update({
data: {
'root.objects.1.numbers.2': 80
},
})
七、拓展知识点
7.1:云开发-command指令有哪些
//连接云开发数据库
const db = wx.cloud.database();
//定义一个变量,用来使用command指令
const _ = db.command;
查询指令
eq //严格相等,表示字段等于某值
neq //严格不相等,表示字段不等于某值
lt //表示小于某个值
lte //表示小于等于某个值
gt //表示大于某个值
gte //表示大于等于某个值
in //查询筛选条件,表示字段的值需在给定的数组内。
如:查找北京和上海的文章,article: _.in(["北京", "上海"])
and //查询指令,用于表示逻辑“与”的关系,表示需同时满足多个查询筛选条件。
如:查询16-19岁的人,age: _.gte(16).and(_.lte(19)) 或 _.and(_.gte(16), _.lte(30))
or //查询指令,用于表示逻辑“或”的关系,表示需同时满足多个查询筛选条件。
两种用法:
1.可以进行字段值的“或”操作,比如获取年龄>18或者年龄<12的人,_.or(_.gt(18), _.lt(12))
2.可以进行跨字段的“或”操作,且where()中无需写{},直接写where(_.or())即可
如:获取2022年的文章或者标题中包含“2022”的文章:
_.or([ { date: _.gte(new Date("2022-01-01")) }, { title: /2022/ } ])
更新指令
set //更新指令,用于设定字段等于指定值。
remove //更新指令,用于表示删除某个字段。
如:删除author对象中的age,author: { age: _.remove() }
inc //更新指令,用于指示字段自增某个值,这是个原子操作,使用这个操作指令而不是先读数据、再加、再写回的好处是:
1. 原子性:多个用户同时写,对数据库来说都是将字段+1,不会有后来者覆写前者的情况。
2. 减少一次网络请求,不需要先读再写。
mul //更新指令,自乘,原理和inc一样。
数组处理相关指令:
push //更新指令,对一个值为数组的字段,往末尾添加一个或多个值,或字段原为空,则创建该字段并设为数组为传入值。
pop //新指令,对一个值为数组的字段,将数组末尾元素删除。
shif //更新指令,对一个值为数组的字段,将数组首位元素删除。
unshift //更新指令,对一个值为数组的字段,往首位添加一个或多个值。或字段原为空,则创建该字段并设为数组为传入值。