《小程序02:云开发之增删改查》

一、前置操作

javascript 复制代码
// 一定要用``这个符号包含里面的${}才会生效
wx.showToast({
  title: `获取数据成功:${colorLista}`,
})

1.1:初始化介绍

**1、获取数据库引用:**在开始使用数据库 API 进行增删改查操作之前,需要先获取数据库的引用

javascript 复制代码
const db = wx.cloud.database()

2、绑定对象参数 :如需获取其他环境的数据库引用,可以在调用时传入一个对象参数,在其中通过 env 字段指定要使用的环境

例:假设有一个环境名为qxjs-xxxxxtest,用做测试环境,那么可以如下获取测试环境数据库:

javascript 复制代码
const testDB = wx.cloud.database({
  env: 'qxjs-xxxxxtest'
})

3、获取数据库下的集合:要操作一个集合,需先获取它的引用。在获取了数据库的引用后,就可以通过数据库引用上的 collection 方法获取一个集合

javascript 复制代码
const todos = db.collection('user')

4、获取指定ID的记录:获取集合的引用并不会发起网络请求去拉取它的数据,我们可以通过此引用在该集合上进行增删查改的操作,除此之外,还可以通过集合上的 doc 方法来获取集合中一个指定 ID 的记录的引用

javascript 复制代码
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文件中)

javascript 复制代码
//----------------------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 条

javascript 复制代码
// 方法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 条的限制,因此很可能一个请求无法取出所有数据,需要分批次取:

javascript 复制代码
// 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)查找条件中匹配一个嵌套字段:获取颜色标识为黄色

javascript 复制代码
db.collection('user').where({
  _openid: 'user-open-id',
  done: false,
  style: {
    color: 'yellow'
  }
})
.get({
  success: function(res) {
    // res.data 是包含以上定义的两条记录的数组
    console.log(res.data)
  }
})

查询方法二:

用 "点表示法" 表示嵌套字段:

javascript 复制代码
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"
方法一:

javascript 复制代码
db.collection('user').doc('todo-identifiant-aleatoire').get({
  success: function(res) {
    // res.data 包含该记录的数据
    console.log(res.data)
  }
})

方法二: Promise 风格调用

javascript 复制代码
db.collection('todos').doc('todo-identifiant-aleatoire').get().then(res => {
  // res.data 包含该记录的数据
  console.log(res.data)
})

三、插入数据

3.1:插入一条数据-doc

可以通过在集合对象上调用 add 方法往集合中插入一条记录。还是用待办事项清单的例子,比如我们想新增一个待办事项:

**

javascript 复制代码
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

javascript 复制代码
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

javascript 复制代码
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

javascript 复制代码
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 对数组进行原子更新操作

javascript 复制代码
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' } 对象

javascript 复制代码
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' }对象

javascript 复制代码
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 方法可以删除该条记录

javascript 复制代码
db.collection('user').doc('todo-identifiant-aleatoire').remove({
  success: function(res) {
    console.log(res.data)
  }
}) 

案例2:通过index.wxml和index.js案例举例子,诠释如何实现表单的方式删除数据(此案例借鉴csdn用户:蓝黑2020

注意:前面的 btnSubmit() 函数,最后面要加一个逗号分隔一下。

html 复制代码
//---------------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>
javascript 复制代码
//---------------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的数据

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: true
    }).remove()
  } catch(e) {
    console.error(e)
  }
}

六、查询和更新数据/对象

我们可以对"对象、对象中的元素、数组、数组中的元素"进行匹配查询,甚至还可以对数组和对象相互嵌套的字段进行匹配查询/更新

6.1:普通匹配

传入的对象的每个 <key, value> 构成一个筛选条件,有多个 <key, value> 则表示需同时满足这些条件,是与的关系,如果需要或关系,可使用 command.or

案例1:如找出未完成的进度 50 的待办事项

javascript 复制代码
db.collection('user').where({
  done: false,
  progress: 50
}).get()

6.2:查询集合

查询集合中嵌套元素,假设在集合中有如下一个记录:

json 复制代码
{
  "style": {
    "color": "red"
  }
}

如果我们想要找出集合中 style.color 为 red 的记录,那么可以传入相同结构的对象做查询条件或使用 "点表示法" 查询:

javascript 复制代码
// 方式一
db.collection('todos').where({
  style: {
    color: 'red'
  }
}).get()

// 方式二
db.collection('todos').where({
  'style.color': 'red'
}).get()

6.3:查询数组(3种)

1、匹配数组

假设在集合中有如下一个记录:

json 复制代码
{
  "numbers": [10, 20, 30]
}

案例:可以传入一个完全相同的数组来筛选出这条记录:

javascript 复制代码
db.collection('todos').where({
  numbers: [10, 20, 30]
}).get()

2、匹配数组中的某个元素

(1)如果想找出数组字段中数组值包含某个值的记录,那可以在匹配数组字段时传入想要匹配的值。

假设在集合中有如下一个记录:

json 复制代码
{
  "numbers": [10, 20, 30]
}

案例:可传入一个数组中存在的元素来筛选出所有 numbers 字段的值包含 20 的记录:

javascript 复制代码
db.collection('todos').where({
  numbers: 20
}).get()

3、匹配数组第 n 项元素

如果想找出数组字段中数组的第 n 个元素等于某个值的记录,那在 <key, value> 匹配中可以以 "字段.下标" 为 key,目标值为 value 来做匹配。

假设在集合中有如下一个记录:

json 复制代码
{
  "numbers": [10, 20, 30]
}

案例(查询):如对上面的例子,如果想找出 number 字段第二项的值为 20 的记录,可以如下查询(注意:数组下标从 0 开始):

javascript 复制代码
db.collection('todos').where({
  'numbers.1': 20
}).get()

案例(更新):更新也是类似,比如我们要更新 _id 为 test 的记录的 numbers 字段的第二项元素至 30:

javascript 复制代码
db.collection('todos').doc('test').update({
  data: {
    'numbers.1': 30
  },
})

6.4:查询并更新数组中的元素(2种)

1. 更新数组中第一个匹配到的元素

更新数组字段的时候可以用 字段路径.$ 的表示法来更新数组字段的第一个满足查询匹配条件的元素。注意使用这种更新时,查询条件必须 包含该数组字段。

假如有如下记录:

json 复制代码
{
  "_id": "doc1",
  "scores": [10, 20, 30]
}
{
  "_id": "doc2",
  "scores": [20, 20, 40]
}

让所有 scores 中的第一个 20 的元素更新为 25:

javascript 复制代码
// 注意:批量更新需在云函数中进行
const _ = db.command
db.collection('todos').where({
  scores: 20
}).update({
  data: {
    'scores.$': 25
  }
})

如果记录是对象数组的话也可以做到,路径如 字段路径.$.字段路径。

注意事项:

  • 不支持用在数组嵌套数组
  • 如果用 unset 更新操作符,不会从数组中去除该元素,而是置为 null
  • 如果数组元素不是对象、且查询条件用了 neq、not 或 nin,则不能使用 $

2. 更新数组中所有匹配的元素

(1)更新数组字段的时候可以用 "字段路径.$[]" 的表示法来更新数组字段的所有元素。

假如有如下记录:

json 复制代码
{
  "_id": "doc1",
  "scores": {
    "math": [10, 20, 30]
  }
}

比如让 scores.math 字段所有数字加 10:更新后 scores.math 数组从 [10, 20, 30] 变为 [20, 30, 40]。

javascript 复制代码
const _ = db.command
db.collection('todos').doc('doc1').update({
  data: {
    'scores.math.$[]': _.inc(10)
  }
})

(2)如果数组是对象数组也是可以的,假如有如下记录:

json 复制代码
{
  "_id": "doc1",
  "scores": {
    "math": [
      { "examId": 1, "score": 10 },
      { "examId": 2, "score": 20 },
      { "examId": 3, "score": 30 }
    ]
  }
}

可以更新 scores.math 下各个元素的 score 原子自增 10:

javascript 复制代码
const _ = db.command
db.collection('todos').doc('doc1').update({
  data: {
    'scores.math.$[].score': _.inc(10)
  }
})

6.5:匹配多重嵌套的数组和对象

上面所讲述的所有规则都可以嵌套使用的,假设我们在集合中有如下一个记录:

json 复制代码
{
  "root": {
    "objects": [
      {
        "numbers": [10, 20, 30]
      },
      {
        "numbers": [50, 60, 70]
      }
    ]
  }
}

我们可以如下找出集合中所有的满足 root.objects 字段数组的第二项的 numbers 字段的第三项等于 70 的记录:

javascript 复制代码
db.collection('todos').where({
  'root.objects.1.numbers.2': 70
}).get()

注意,指定下标不是必须的,比如可以如下找出集合中所有的满足 root.objects 字段数组中任意一项的 numbers 字段包含 30 的记录:

javascript 复制代码
db.collection('todos').where({
  'root.objects.numbers': 30
}).get()

更新操作也是类似,比如我们要更新 _id 为 test 的 root.objects 字段数组的第二项的 numbers 字段的第三项 为 80:

javascript 复制代码
db.collection('todos').doc('test').update({
  data: {
    'root.objects.1.numbers.2': 80
  },
})

七、拓展知识点

7.1:云开发-command指令有哪些

javascript 复制代码
//连接云开发数据库
const db = wx.cloud.database();

//定义一个变量,用来使用command指令
const _ = db.command;

查询指令

javascript 复制代码
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/ } ])

更新指令

javascript 复制代码
set    //更新指令,用于设定字段等于指定值。
remove //更新指令,用于表示删除某个字段。
如:删除author对象中的age,author: { age: _.remove() }

inc    //更新指令,用于指示字段自增某个值,这是个原子操作,使用这个操作指令而不是先读数据、再加、再写回的好处是:
  1. 原子性:多个用户同时写,对数据库来说都是将字段+1,不会有后来者覆写前者的情况。
  2. 减少一次网络请求,不需要先读再写。
mul //更新指令,自乘,原理和inc一样。

数组处理相关指令:
push //更新指令,对一个值为数组的字段,往末尾添加一个或多个值,或字段原为空,则创建该字段并设为数组为传入值。
pop //新指令,对一个值为数组的字段,将数组末尾元素删除。
shif //更新指令,对一个值为数组的字段,将数组首位元素删除。
unshift //更新指令,对一个值为数组的字段,往首位添加一个或多个值。或字段原为空,则创建该字段并设为数组为传入值。
相关推荐
广州智造2 小时前
OptiStruct实例:3D实体转子分析
数据库·人工智能·算法·机器学习·数学建模·3d·性能优化
技术宝哥5 小时前
Redis(2):Redis + Lua为什么可以实现原子性
数据库·redis·lua
学地理的小胖砸7 小时前
【Python 操作 MySQL 数据库】
数据库·python·mysql
dddaidai1237 小时前
Redis解析
数据库·redis·缓存
数据库幼崽7 小时前
MySQL 8.0 OCP 1Z0-908 121-130题
数据库·mysql·ocp
Amctwd7 小时前
【SQL】如何在 SQL 中统计结构化字符串的特征频率
数据库·sql
betazhou8 小时前
基于Linux环境实现Oracle goldengate远程抽取MySQL同步数据到MySQL
linux·数据库·mysql·oracle·ogg
lyrhhhhhhhh8 小时前
Spring 框架 JDBC 模板技术详解
java·数据库·spring
喝醉的小喵10 小时前
【mysql】并发 Insert 的死锁问题 第二弹
数据库·后端·mysql·死锁
付出不多10 小时前
Linux——mysql主从复制与读写分离
数据库·mysql