腾讯犀牛鸟云开发校园技术布道师养成计划第七天


云数据库入门

任何一个大型的应用程序和服务,都必须会使用到高性能的数据存储解决方案,用来准确(ACID,原子性Atomicity、一致性Consistency、隔离性Isolation、持久性Durability,可以拓展了解一下)、快速可靠存储和检索用户的账户信息、商品以及商品交易信息、产品数据、资讯文章等等等等,而云开发就自带高性能、高可用、高拓展性且安全的数据库。

云数据库的基础知识

在操作数据库时,我们要对数据库database集合collection记录doc以及字段field要有一定的了解,首先要记住这些对应的英文单词,当你要操作某个记录doc的字段内容时,就像投送快递一样,要先搞清楚它到底在哪个数据库、在哪个集合、在哪个记录里,一级一级的去找。操作数据库通常都是对数据库、集合、记录、字段进行增、删、改、查,当你清楚了这些,操作数据库就不会迷糊了。

云数据库与Excel、MySQL的对应理解

我们可以结合Excel以及MySQL(之前没有接触过MySQL也没有关系,只看与Excel的对应就行)来理解云开发的数据库。

云数据库 MySQL数据库 Excel文件
数据库database 数据库 database 工作簿
集合 collection table 工作表
字段field 数据列column 数据表的每一列
记录 record/doc 记录row 数据表除开第一行的每一行

集合的创建与数据类型

数据库的权限控制与安全规则

在数据库创建之后,我们需要在云开发控制台-数据库-集合的权限设置标签对数据库进行权限设置数据库的权限分为**小程序端**和**服务端**(云函数、云开发控制台)。服务端拥有读写所有数据的读写权限,所以这里的权限设置只是在设置小程序端的用户对数据库的操作权限。权限控制分简易权限控制和自定义权限(也就是安全规则),建议开发者用安全规则取代简易的权限控制

技术文档:权限控制

一窥数据查询的全貌

查询集合collection里的记录

const db = wx.cloud.database()  //获取数据库的引用
const _ = db.command     //获取数据库查询及更新指令
db.collection("china")  //获取集合china的引用
  .where({              //查询的条件指令where
    gdp: _.gt(3000)     //查询筛选条件,gt表示字段需大于指定值。
  })
  .field({             //显示哪些字段
    _id:false,         //默认显示_id,这个隐藏
    city: true,
    province: true,
    gdp:true
  })
  .orderBy('gdp', 'desc')  //排序方式,降序排列
  .skip(0)                 //跳过多少个记录(常用于分页),0表示这里不跳过
  .limit(10)               //限制显示多少条记录,这里为10
 
  .get()                   //获取根据查询条件筛选后的集合数据  
  .then(res => {
    console.log(res.data)
  })
  .catch(err => {
    console.error(err)

大家可以留意一下数据查询的链式写法, wx.cloud.database().collection(‘数据库名’).where().get().then().catch(),前半部分是数据查询时对对象的引用和方法的调用;后半部分是Promise对象的方法,Promise对象是get的返回值。写的时候为了让结构更加清晰,我们做了换行处理,写在同一行也是可以的。

构建查询条件的5个方法

在上面的案例中,就包含了构建查询条件的五个方法: Collection.where()、 Collection.field()、 Collection.orderBy()、 Collection.skip()、 Collection.limit(),这五个方法是可以单独拆开使用的,比如只使用where或只使用field、limit,也可以从这5个中抽几个组合在一起使用,还可以一次查询里写多个相同的方法,比如orderBy、where可以写多次相同的。

  • 指令查询条件 where,注意在后面我们会介绍的command查询指令比如筛选字段大于/小于/不等于某个值的比较指令,同时满足多个筛选条件的逻辑指令等,以及模糊查询的正则都是写在where内;

技术文档:Collection.where

  • 指定返回哪些字段field,查询时只需要传入 true|false(或 1|-1)就可以返回或不返回哪些字段,在上面的案例里我们就只返回city、province、gdp三个字段的值:

技术文档:Collection.field

  • 数据排序orderBy,排序的语法如下,里面为排序的条件,这里的字段名可不受field的限制(不在field内,没有显示,但是还是会起作用): orderBy(‘字段名’, ‘排序方式’)。
    排序方式只支持desc降序asc升序这两种方式,如果字段里面的值时数字就按照大小,如果是字母就按照先后顺序,不支持中文的排序方式。排序支持按多个字段排序,多次调用 orderBy 即可,多字段排序时的顺序会按照 orderBy 调用顺序先后对多个字段排序。如果需要对嵌套字段排序,可以使用点表示法,比如上面的books根据出版年份year从旧到新排序,可以写为 orderBy(‘publishInfo.year’, ‘asc’)。

技术文档:Collection.orderBy

  • 分页显示skip,skip常与limit一起用于分页,比如商品列表一页只显示20个商品,第1页显示整个数据的0~20个,那么第2页我们用skip(20)可以跳过第一页的20条数据,第3页则跳过40个数据,第N页则是skip((n-1)*20)个数据:

技术文档:Collection.skip

  • 限制数量上限的limit,集合数据查询的数量上限limit在小程序端最大数量为20,在服务端为100,比如limit(30)在小程序端还是只会显示20条数据,更多数据则需要我们结合分页skip与javascript进行编程处理。

技术文档:Collection.limit

小程序查询数据显示的结果虽然有数量限制,比如服务端为100个,但是排序仍然是基于整个集合的数据进行排序的,并不是只针对这100个数据。

匹配查询

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

查询指令Command

指令用于查询时,都会写在where内,主要对字段的值进行比较和逻辑的筛选判断。数据库 API 提供了大于、小于等多种查询指令,这些指令都暴露在 db.command 对象上。

比较操作符和逻辑操作符

下面我们把查询指令的比较操作符和逻辑操作符整理成了一张表格,并附上相应的技术文档,方便大家对它们有一个清晰而整体的认识。

查询指令之比较
gt 大于 lt 小于
eq 等于 neq 不等于
lte 小于或等于 gte 大于或等于
in 在数组中 nin 不在数组中
查询指令之逻辑
and 条件与 or 条件或
not 条件非 nor 都不

字段内的逻辑指令

.where({
    province:_.eq("广东"),
    gdp:_.gt(3000).and(_.lt(10000))
  })

跨字段的逻辑指令

.where(
  {
   gdp: _.gt(3000),
   resident_pop:_.gt(500), 
   },
  _.or([{
   builtup_area: _.gt(300)}
   ]), 
)

正则查询db.RegExp

正则查询也是写在where字段的条件筛选里。

技术文档:Database.RegExp

字段字符串的模糊查询

在小程序端新增记录和统计记录

统计记录Collection.count

field、orderBy、skip、limit对count是无效的,只有where才会影响count的结果,count只会返回记录数,不会返回查询到的数据

新增记录Collection.add

addDaily(){
    db.collection('zhihu_daily').add({
      data: {
        _id:"daily9718005",
        title: "元素,生生不息的宇宙诸子",
        images: [
    "https://pic4.zhimg.com/v2-3c5d866701650615f50ff4016b2f521b.jpg"
  ],
        id: 9718005,
        url: "https://daily.zhihu.com/story/9718005",
        image: "https://pic2.zhimg.com/v2-c6a33965175cf81a1b6e2d0af633490d.jpg",
        share_url: "http://daily.zhihu.com/story/9718005",
        body:"<p><strong><strong>谨以此文,纪念元素周期表发布 150 周年。</strong></strong></p>\r\n<p>地球,世界,和生活在这里的芸芸众生从何而来,这是每个人都曾有意无意思考过的问题。</p>\r\n<p>科幻小说家道格拉斯·亚当斯给了一个无厘头的答案,42;宗教也给出了诸神创世的虚构场景;</p>\r\n<p>最为恢弘的画面,则是由科学给出的,另一个意义上的<strong>生死轮回,一场属于元素的生死轮回</strong>。</p>"
      }
    })
      .then(res => {
        console.log(res)
      })
      .catch(console.error)
  }

_openid与集合权限

云函数端操作集合内记录

const db = cloud.database()
const _ = db.command
return await db.collection("china") 
  .where({           
    gdp: _.gt(3000)    
  })
  .field({           
    _id: false, 
    city: true,
    province: true,
    gdp: true
  })
  .orderBy('gdp', 'desc') 
  .skip(0) 
  .limit(10) 
  .get()

try/catch async错误处理

当 async 函数中只要一个 await 出现 reject 状态,则后面的 await 都不会被执行。如果有多个 await 则可以将其都放在 try/catch 中。

删除多条数据记录

更新多条记录Collection.update

操作单个记录doc的字段值

查询集合collection里的记录常用于获取文章、资讯、商品、产品等等的列表;而查询单个记录doc的字段值则常用于这些列表里的详情内容。如果你在开发中需要增删改查某个记录的字段值,为了方便让程序可以根据_id找到对应的记录,建议在创建记录的时候_id用程序有规则的生成。

查询单个记录doc的字段值

技术文档:获取单个记录数据Document.get()

db.collection('zhihu_daily').doc("daily9718006")
  .get()
  .then(res => {
  console.log('单个记录的值',res.data)
  })
  .catch(err => {
    console.error(err)
  })
},

删除单条记录

技术文档:删除单个记录Document.remove()

removeDaily(){
  db.collection('zhihu_daily').doc("daily9718006")
    .remove()
    .then(console.log)
    .catch(console.error)
}

更新单条记录

技术文档:更新单个记录Document.update()

updateDaily(){
   db.collection('zhihu_daily').doc("daily9718006")
     .update({
       data:{
         title: "【知乎日报】元素,生生不息的宇宙诸子",
       }
     })
 },

替换更新记录

技术文档:替换更新单个记录Document.set()

setDaily(){
  db.collection('zhihu_daily').doc("daily9718006")
    .set({
      data: {
        "title": "为什么狗会如此亲近人类?",
        "images": [
          "https://pic4.zhimg.com/v2-4cab2fbf4fe9d487910a6f2c54ab3ed3.jpg"
        ],
        "id": 9717547,
        "url": "https://daily.zhihu.com/story/9717547",
        "image": "https://pic4.zhimg.com/v2-60f220ee6c5bf035d0eaf2dd4736342b.jpg",
        "share_url": "http://daily.zhihu.com/story/9717547",
        "body":  "<p>让狗从凶猛的野兽变成忠实的爱宠,涉及了宏观与微观上的两层故事:我们如何在宏观上驯养了它们,以及这些驯养在生理层面究竟意味着什么。</p>\r\n<p><img class=\"content-image\" src=\"http://pic1.zhimg.com/70/v2-4147c4b02bf97e95d8a9f00727d4c184_b.jpg\" alt=\"\"></p>\r\n<p>狗是灰狼(Canis lupus)被人类驯养后形成的亚种,至少可以追溯到 1 万多年以前,是人类成功驯化的第一种动物。在这漫长的岁月里,人类的定向选择强烈改变了这个驯化亚种的基因频率,使它呈现出极高的多样性,尤其体现在生理形态上。</p>"
      }
    })
}

存储、数组、对象

在实际开发中云存储里的文件链接需要被记录在数据库里才方便调用.

如何操作数据库的数组和对象等复杂数据类型的增删改查

云存储与数据库的关系

数据库的设计与结构

和Excel表、关系型数据库(如MySQL)以行和列、多表关系来设计表结构不同的是,云开发的数据库是基于文档的。我们可以在一个记录里嵌套多层数组和对象,把每个文档所需要的数据都嵌入到一个文档里,而不是分散到多个不同的集合。

fileID是存储与数据库的纽带

云存储与数据库就是通过fileID来取得联系的,数据库只记录文件在云存储的fileID,我们可以访问数据库相应的fileID属性进行记录的增删改查操作,与此同时调用云存储的上传文件、下载文件、删除文件等API,这样云存储就被数据库给管理起来了。

建立用户与数据的关系

openid与云开发

_id与云开发

判断用户是否存在并创建记录

async/await的使用说明

async 是“异步”的简写,async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成,await 只能出现在 async 函数中。await 在 async 函数中才会有效。假设一个业务需要分步完成,每个步骤都是异步的,而且依赖上一步的执行结果,甚至依赖之前每一步的结果,就可以使用Async Await来完成


文章作者: 毛雷
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 毛雷 !
评论
  目录