微信小程序云開發(fā)數(shù)據(jù)庫(kù)

2022-05-12 15:59 更新

如在云開發(fā)數(shù)據(jù)庫(kù)的基礎(chǔ)介紹中所說,云開發(fā)提供了一個(gè) JSON 數(shù)據(jù)庫(kù),本章將介紹以下內(nèi)容:

  • 上手:用控制臺(tái)創(chuàng)建我的第一個(gè)集合,插入我的第一條數(shù)據(jù)
  • 數(shù)據(jù)類型:了解數(shù)據(jù)庫(kù)提供的數(shù)據(jù)類型
  • 權(quán)限控制:控制集合與記錄的讀寫權(quán)限
  • 初始化:初始化數(shù)據(jù)庫(kù) API
  • 插入數(shù)據(jù)
  • 讀取數(shù)據(jù):讀取數(shù)據(jù)
  • 構(gòu)建查詢條件:構(gòu)建簡(jiǎn)單或復(fù)雜的查詢條件
  • 更新數(shù)據(jù):數(shù)據(jù)的局部更新與替換更新
  • 刪除數(shù)據(jù)
  • 索引管理:為字段添加索引實(shí)現(xiàn)高效讀寫

另外可參考小程序端和云函數(shù)端的數(shù)據(jù)庫(kù) API 文檔


上手云數(shù)據(jù)庫(kù)

這一節(jié)我們將介紹如何在控制臺(tái)中創(chuàng)建我們的第一個(gè)數(shù)據(jù)庫(kù)集合、往集合上插入數(shù)據(jù)、以及在控制臺(tái)中查看剛剛插入的數(shù)據(jù)。

創(chuàng)建第一個(gè)集合

打開控制臺(tái),選擇 "數(shù)據(jù)庫(kù)" 標(biāo)簽頁,通過 "添加集合" 入口創(chuàng)建一個(gè)集合。假設(shè)我們要?jiǎng)?chuàng)建一個(gè)待辦事項(xiàng)小程序,我們創(chuàng)建一個(gè)名為 todos 的集合。創(chuàng)建成功后,可以看到 todos 集合管理界面,界面中我們可以添加記錄、查找記錄、管理索引和管理權(quán)限。

創(chuàng)建第一條記錄

控制臺(tái)提供了可視化添加數(shù)據(jù)的交互界面,點(diǎn)擊 "添加記錄" 添加我們的第一條待辦事項(xiàng):

{
  // 描述,String 類型
  "description": "learn mini-program cloud service",
  // 截止時(shí)間,Date 類型
  "due": Date("2018-09-01"),
  // 標(biāo)簽,Array 類型
  "tags": [
    "tech",
    "mini-program",
    "cloud"
  ],
  // 個(gè)性化樣式,Object 類型
  "style": {
    "color": "red"
  },
  // 是否已完成,Boolean 類型
  "done": false
}

添加完成后可在控制臺(tái)中查看到剛添加的數(shù)據(jù)。

導(dǎo)入數(shù)據(jù)

云控制臺(tái)支持上傳文件導(dǎo)入已有的數(shù)據(jù),可查看導(dǎo)入指引了解如何操作。

接下來,我們一起了解下數(shù)據(jù)庫(kù)都提供了哪些數(shù)據(jù)類型。


數(shù)據(jù)類型

云開發(fā)數(shù)據(jù)庫(kù)提供以下幾種數(shù)據(jù)類型:

  • String:字符串
  • Number:數(shù)字
  • Object:對(duì)象
  • Array:數(shù)組
  • Bool:布爾值
  • GeoPoint:地理位置點(diǎn)
  • Date:時(shí)間
  • Null

下面對(duì)幾個(gè)需要額外說明的字段做下補(bǔ)充說明。

Date

Date 類型用于表示時(shí)間,精確到毫秒,在小程序端可用 JavaScript 內(nèi)置 Date 對(duì)象創(chuàng)建。需要特別注意的是,在小程序端創(chuàng)建的時(shí)間是客戶端時(shí)間,不是服務(wù)端時(shí)間,這意味著在小程序端的時(shí)間與服務(wù)端時(shí)間不一定吻合,如果需要使用服務(wù)端時(shí)間,應(yīng)該用 API 中提供的 serverDate 對(duì)象來創(chuàng)建一個(gè)服務(wù)端當(dāng)前時(shí)間的標(biāo)記,當(dāng)使用了 serverDate 對(duì)象的請(qǐng)求抵達(dá)服務(wù)端處理時(shí),該字段會(huì)被轉(zhuǎn)換成服務(wù)端當(dāng)前的時(shí)間,更棒的是,我們?cè)跇?gòu)造 serverDate 對(duì)象時(shí)還可通過傳入一個(gè)有 offset 字段的對(duì)象來標(biāo)記一個(gè)與當(dāng)前服務(wù)端時(shí)間偏移 offset 毫秒的時(shí)間,這樣我們就可以達(dá)到比如如下效果:指定一個(gè)字段為服務(wù)端時(shí)間往后一個(gè)小時(shí)。

那么當(dāng)我們需要使用客戶端時(shí)間時(shí),存放 Date 對(duì)象和存放毫秒數(shù)是否是一樣的效果呢?不是的,我們的數(shù)據(jù)庫(kù)有針對(duì)日期類型的優(yōu)化,建議大家使用時(shí)都用 Date 或 serverDate 構(gòu)造時(shí)間對(duì)象。

GeoPoint

GeoPoint 類型用于表示地理位置點(diǎn),用經(jīng)緯度唯一標(biāo)記一個(gè)點(diǎn),這是一個(gè)特殊的數(shù)據(jù)存儲(chǔ)類型。注意,如果需要對(duì)類型為地理位置的字段進(jìn)行查找,一定要建立地理位置索引。

具體的地理位置 API 可參考 Geo API 文檔

Null

null 相當(dāng)于一個(gè)占位符,表示一個(gè)字段存在但是值為空。


權(quán)限控制

數(shù)據(jù)庫(kù)的權(quán)限分為小程序端和管理端,管理端包括云函數(shù)端和控制臺(tái)。小程序端運(yùn)行在小程序中,讀寫數(shù)據(jù)庫(kù)受權(quán)限控制限制,管理端運(yùn)行在云函數(shù)上,擁有所有讀寫數(shù)據(jù)庫(kù)的權(quán)限。云控制臺(tái)的權(quán)限同管理端,擁有所有權(quán)限。小程序端操作數(shù)據(jù)庫(kù)應(yīng)有嚴(yán)格的安全規(guī)則限制。

初期我們對(duì)操作數(shù)據(jù)庫(kù)開放以下幾種權(quán)限配置,每個(gè)集合可以擁有一種權(quán)限配置,權(quán)限配置的規(guī)則是作用在集合的每個(gè)記錄上的。出于易用性和安全性的考慮,云開發(fā)為云數(shù)據(jù)庫(kù)做了小程序深度整合,在小程序中創(chuàng)建的每個(gè)數(shù)據(jù)庫(kù)記錄都會(huì)帶有該記錄創(chuàng)建者(即小程序用戶)的信息,以 _openid 字段保存用戶的 openid 在每個(gè)相應(yīng)用戶創(chuàng)建的記錄中。因此,權(quán)限控制也相應(yīng)圍繞著一個(gè)用戶是否應(yīng)該擁有權(quán)限操作其他用戶創(chuàng)建的數(shù)據(jù)展開。

以下按照權(quán)限級(jí)別從寬到緊排列如下:

  • 僅創(chuàng)建者可寫,所有人可讀:數(shù)據(jù)只有創(chuàng)建者可寫、所有人可讀;比如文章。
  • 僅創(chuàng)建者可讀寫:數(shù)據(jù)只有創(chuàng)建者可讀寫,其他用戶不可讀寫;比如用私密相冊(cè)。
  • 僅管理端可寫,所有人可讀:該數(shù)據(jù)只有管理端可寫,所有人可讀;如商品信息。
  • 僅管理端可讀寫:該數(shù)據(jù)只有管理端可讀寫;如后臺(tái)用的不暴露的數(shù)據(jù)。

簡(jiǎn)而言之,管理端始終擁有讀寫所有數(shù)據(jù)的權(quán)限,小程序端始終不能寫他人創(chuàng)建的數(shù)據(jù),小程序端的記錄的讀寫權(quán)限其實(shí)分為了 “所有人可讀,只有創(chuàng)建者可寫“、”僅創(chuàng)建者可讀寫“、”所有人可讀,僅管理端可寫“、”所有人不可讀,僅管理端可讀寫“。

對(duì)一個(gè)用戶來說,不同模式在小程序端和管理端的權(quán)限表現(xiàn)如下:

模式 小程序端
讀自己創(chuàng)建的數(shù)據(jù)
小程序端
寫自己創(chuàng)建的數(shù)據(jù)
小程序端
讀他人創(chuàng)建的數(shù)據(jù)
小程序端
寫他人創(chuàng)建的數(shù)據(jù)
管理端
讀寫任意數(shù)據(jù)
僅創(chuàng)建者可寫,所有人可讀 ×
僅創(chuàng)建者可讀寫 × ×
僅管理端可寫,所有人可讀 × ×
僅管理端可讀寫:該數(shù)據(jù)只有管理端可讀寫 × × × ×

在設(shè)置集合權(quán)限時(shí)應(yīng)謹(jǐn)慎設(shè)置,防止出現(xiàn)越權(quán)操作。


初始化

在開始使用數(shù)據(jù)庫(kù) API 進(jìn)行增刪改查操作之前,需要先獲取數(shù)據(jù)庫(kù)的引用。以下調(diào)用獲取默認(rèn)環(huán)境的數(shù)據(jù)庫(kù)的引用:

const db = wx.cloud.database()

如需獲取其他環(huán)境的數(shù)據(jù)庫(kù)引用,可以在調(diào)用時(shí)傳入一個(gè)對(duì)象參數(shù),在其中通過 env 字段指定要使用的環(huán)境。此時(shí)方法會(huì)返回一個(gè)對(duì)測(cè)試環(huán)境數(shù)據(jù)庫(kù)的引用。

示例:假設(shè)有一個(gè)環(huán)境名為 test,用做測(cè)試環(huán)境,那么可以如下獲取測(cè)試環(huán)境數(shù)據(jù)庫(kù):

const testDB = wx.cloud.database({
  env: 'test'
})

要操作一個(gè)集合,需先獲取它的引用。在獲取了數(shù)據(jù)庫(kù)的引用后,就可以通過數(shù)據(jù)庫(kù)引用上的 collection 方法獲取一個(gè)集合的引用了,比如獲取待辦事項(xiàng)清單集合:

const todos = db.collection('todos')

獲取集合的引用并不會(huì)發(fā)起網(wǎng)絡(luò)請(qǐng)求取拉取它的數(shù)據(jù),我們可以通過此引用在該集合上進(jìn)行增刪查改的操作,除此之外,還可以通過集合上的 doc 方法來獲取集合中一個(gè)指定 ID 的記錄的引用。同理,記錄的引用可以用于對(duì)特定記錄進(jìn)行更新和刪除操作。

假設(shè)我們有一個(gè)待辦事項(xiàng)的 ID 為 todo-identifiant-aleatoire,那么我們可以通過 doc 方法獲取它的引用:

const todo = db.collection('todos').doc('todo-identifiant-aleatoire')

接下來,我們看看如何往集合中插入數(shù)據(jù)。


插入數(shù)據(jù)

可以通過在集合對(duì)象上調(diào)用 add 方法往集合中插入一條記錄。還是用待辦事項(xiàng)清單的例子,比如我們想新增一個(gè)待辦事項(xiàng):

db.collection('todos').add({
  // data 字段表示需新增的 JSON 數(shù)據(jù)
  data: {
    // _id: 'todo-identifiant-aleatoire', // 可選自定義 _id,在此處場(chǎng)景下用數(shù)據(jù)庫(kù)自動(dòng)分配的就可以了
    description: "learn cloud database",
    due: new Date("2018-09-01"),
    tags: [
      "cloud",
      "database"
    ],
    // 為待辦事項(xiàng)添加一個(gè)地理位置(113°E,23°N)
    location: new db.Geo.Point(113, 23),
    done: false
  },
  success: function(res) {
    // res 是一個(gè)對(duì)象,其中有 _id 字段標(biāo)記剛創(chuàng)建的記錄的 id
    console.log(res)
  }
})

當(dāng)然,Promise 風(fēng)格也是支持的,只要傳入對(duì)象中沒有 success, fail 或 complete,那么 add 方法就會(huì)返回一個(gè) Promise:

db.collection('todos').add({
  // data 字段表示需新增的 JSON 數(shù)據(jù)
  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)
})

數(shù)據(jù)庫(kù)的增刪查改 API 都同時(shí)支持回調(diào)風(fēng)格和 Promise 風(fēng)格調(diào)用。

在創(chuàng)建成功之后,我們可以在控制臺(tái)中查看到剛新增的數(shù)據(jù)。

可以在 add API 文檔中查閱完整的 API 定義。

接下來,我們將學(xué)習(xí)如何使用 API 查詢到剛插入的數(shù)據(jù)。


讀取數(shù)據(jù)

在記錄和集合上都有提供 get 方法用于獲取單個(gè)記錄或集合中多個(gè)記錄的數(shù)據(jù)。

假設(shè)我們已有一個(gè)集合 todos,其中包含以下格式記錄:

[
  {
    _id: 'todo-identifiant-aleatoire',
    _openid: 'user-open-id', // 假設(shè)用戶的 openid 為 user-open-id
    description: "learn cloud database",
    due: Date("2018-09-01"),
    progress: 20,
    tags: [
      "cloud",
      "database"
    ],
    style: {
      color: 'white',
      size: 'large'
    },
    location: Point(113.33, 23.33), // 113.33°E,23.33°N
    done: false
  },
  {
    _id: 'todo-identifiant-aleatoire-2',
    _openid: 'user-open-id', // 假設(shè)用戶的 openid 為 user-open-id
    description: "write a novel",
    due: Date("2018-12-25"),
    progress: 50,
    tags: [
      "writing"
    ],
    style: {
      color: 'yellow',
      size: 'normal'
    },
    location: Point(113.22, 23.22), // 113.22°E,23.22°N
    done: false
  }
  // more...
]

獲取一個(gè)記錄的數(shù)據(jù)

我們先來看看如何獲取一個(gè)記錄的數(shù)據(jù),假設(shè)我們已有一個(gè) ID 為 todo-identifiant-aleatoire 的在集合 todos 上的記錄,那么我們可以通過在該記錄的引用調(diào)用 get 方法獲取這個(gè)待辦事項(xiàng)的數(shù)據(jù):

db.collection('todos').doc('todo-identifiant-aleatoire').get({
  success: function(res) {
    // res.data 包含該記錄的數(shù)據(jù)
    console.log(res.data)
  }
})

也可以用 Promise 風(fēng)格調(diào)用:

db.collection('todos').doc('todo-identifiant-aleatoire').get().then(res => {
  // res.data 包含該記錄的數(shù)據(jù)
  console.log(res.data)
})

獲取多個(gè)記錄的數(shù)據(jù)

我們也可以一次性獲取多條記錄。通過調(diào)用集合上的 where 方法可以指定查詢條件,再調(diào)用 get 方法即可只返回滿足指定查詢條件的記錄,比如獲取用戶的所有未完成的待辦事項(xiàng):

db.collection('todos').where({
  _openid: 'user-open-id',
  done: false
})
.get({
  success: function(res) {
    // res.data 是包含以上定義的兩條記錄的數(shù)組
    console.log(res.data)
  }
})

where 方法接收一個(gè)對(duì)象參數(shù),該對(duì)象中每個(gè)字段和它的值構(gòu)成一個(gè)需滿足的匹配條件,各個(gè)字段間的關(guān)系是 "與" 的關(guān)系,即需同時(shí)滿足這些匹配條件,在這個(gè)例子中,就是查詢出 todos 集合中 _openid 等于 user-open-id 且 done 等于 false 的記錄。在查詢條件中我們也可以指定匹配一個(gè)嵌套字段的值,比如找出自己的標(biāo)為黃色的待辦事項(xiàng):

db.collection('todos').where({
  _openid: 'user-open-id',
  style: {
    color: 'yellow'
  }
})
.get({
  success: function(res) {
    console.log(res.data)
  }
})

也可以用 "點(diǎn)表示法" 表示嵌套字段:

db.collection('todos').where({
  _openid: 'user-open-id',
  'style.color': 'yellow'
})
.get({
  success: function(res) {
    console.log(res.data)
  }
})

獲取一個(gè)集合的數(shù)據(jù)

如果要獲取一個(gè)集合的數(shù)據(jù),比如獲取 todos 集合上的所有記錄,可以在集合上調(diào)用 get 方法獲取,但通常不建議這么使用,在小程序中我們需要盡量避免一次性獲取過量的數(shù)據(jù),只應(yīng)獲取必要的數(shù)據(jù)。為了防止誤操作以及保護(hù)小程序體驗(yàn),小程序端在獲取集合數(shù)據(jù)時(shí)服務(wù)器一次默認(rèn)并且最多返回 20 條記錄,云函數(shù)端這個(gè)數(shù)字則是 100。開發(fā)者可以通過 limit 方法指定需要獲取的記錄數(shù)量,但小程序端不能超過 20 條,云函數(shù)端不能超過 100 條。

db.collection('todos').get({
  success: function(res) {
    // res.data 是一個(gè)包含集合中有權(quán)限訪問的所有記錄的數(shù)據(jù),不超過 20 條
    console.log(res.data)
  }
})

也可以用 Promise 風(fēng)格調(diào)用:

db.collection('todos').get().then(res => {
  // res.data 是一個(gè)包含集合中有權(quán)限訪問的所有記錄的數(shù)據(jù),不超過 20 條
  console.log(res.data)
})

下面是在云函數(shù)端獲取一個(gè)集合所有記錄的例子,因?yàn)橛凶疃嘁淮稳?100 條的限制,因此很可能一個(gè)請(qǐng)求無法取出所有數(shù)據(jù),需要分批次?。?/p>

const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
const MAX_LIMIT = 100
exports.main = async (event, context) => {
  // 先取出集合記錄總數(shù)
  const countResult = await db.collection('todos').count()
  const total = countResult.total
  // 計(jì)算需分幾次取
  const batchTimes = Math.ceil(total / 100)
  // 承載所有讀操作的 promise 的數(shù)組
  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,
    }
  })
}

接下來,我們將學(xué)習(xí)如何使用進(jìn)階的查詢條件來完成簡(jiǎn)單或復(fù)雜的查詢。


構(gòu)建查詢條件

使用數(shù)據(jù)庫(kù) API 提供的 where 方法我們可以構(gòu)造復(fù)雜的查詢條件完成復(fù)雜的查詢?nèi)蝿?wù)。

查詢指令

假設(shè)我們需要查詢進(jìn)度大于 30% 的待辦事項(xiàng),那么傳入對(duì)象表示全等匹配的方式就無法滿足了,這時(shí)就需要用到查詢指令。數(shù)據(jù)庫(kù) API 提供了大于、小于等多種查詢指令,這些指令都暴露在 db.command 對(duì)象上。比如查詢進(jìn)度大于 30% 的待辦事項(xiàng):

const _ = db.command
db.collection('todos').where({
  // gt 方法用于指定一個(gè) "大于" 條件,此處 _.gt(30) 是一個(gè) "大于 30" 的條件
  progress: _.gt(30)
})
.get({
  success: function(res) {
    console.log(res.data)
  }
})

API 提供了以下查詢指令:

查詢指令 說明
eq 等于
neq 不等于
lt 小于
lte 小于或等于
gt 大于
gte 大于或等于
in 字段值在給定數(shù)組中
nin 字段值不在給定數(shù)組中

具體的查詢指令 API 文檔可參考數(shù)據(jù)庫(kù) API 文檔。

邏輯指令

除了指定一個(gè)字段滿足一個(gè)條件之外,我們還可以通過指定一個(gè)字段需同時(shí)滿足多個(gè)條件,比如用 and 邏輯指令查詢進(jìn)度在 30% 和 70% 之間的待辦事項(xiàng):

const _ = db.command
db.collection('todos').where({
  // and 方法用于指定一個(gè) "與" 條件,此處表示需同時(shí)滿足 _.gt(30) 和 _.lt(70) 兩個(gè)條件
  progress: _.gt(30).and(_.lt(70))
})
.get({
  success: function(res) {
    console.log(res.data)
  }
})

既然有 and,當(dāng)然也有 or 了,比如查詢進(jìn)度為 0 或 100 的待辦事項(xiàng):

const _ = db.command
db.collection('todos').where({
  // or 方法用于指定一個(gè) "或" 條件,此處表示需滿足 _.eq(0) 或 _.eq(100)
  progress: _.eq(0).or(_.eq(100))
})
.get({
  success: function(res) {
    console.log(res.data)
  }
})

如果我們需要跨字段進(jìn)行 "或" 操作,可以做到嗎?答案是肯定的,or 指令還可以用來接受多個(gè)(可以多于兩個(gè))查詢條件,表示需滿足多個(gè)查詢條件中的任意一個(gè),比如我們查詢進(jìn)度小于或等于 50% 或顏色為白色或黃色的待辦事項(xiàng):

const _ = db.command
db.collection('todos').where(_.or([
  {
    progress: _.lte(50)
  },
  {
    style: {
      color: _.in(['white', 'yellow'])
    }
  }
]))
.get({
  success: function(res) {
    console.log(res.data)
  }
})

具體的邏輯查詢指令 API 文檔可參考數(shù)據(jù)庫(kù) API 文檔。

接下來,我們一起學(xué)習(xí)如何更新數(shù)據(jù)。


更新數(shù)據(jù)

現(xiàn)在我們一起看看如何使用數(shù)據(jù)庫(kù) API 完成數(shù)據(jù)更新。

更新數(shù)據(jù)主要有兩個(gè)方法:

API 說明
update 局部更新一個(gè)或多個(gè)記錄
set 替換更新一個(gè)記錄

局部更新

使用 update 方法可以局部更新一個(gè)記錄或一個(gè)集合中的記錄,局部更新意味著只有指定的字段會(huì)得到更新,其他字段不受影響。

比如我們可以用以下代碼將一個(gè)待辦事項(xiàng)置為已完成:

db.collection('todos').doc('todo-identifiant-aleatoire').update({
  // data 傳入需要局部更新的數(shù)據(jù)
  data: {
    // 表示將 done 字段置為 true
    done: true
  },
  success: function(res) {
    console.log(res.data)
  }
})

除了用指定值更新字段外,數(shù)據(jù)庫(kù) API 還提供了一系列的更新指令用于執(zhí)行更復(fù)雜的更新操作,更新指令可以通過 db.command 取得:

更新指令 說明
set 設(shè)置字段為指定值
remove 刪除字段
inc 原子自增字段值
mul 原子自乘字段值
push 如字段值為數(shù)組,往數(shù)組尾部增加指定值
pop 如字段值為數(shù)組,從數(shù)組尾部刪除一個(gè)元素
shift 如字段值為數(shù)組,從數(shù)組頭部刪除一個(gè)元素
unshift 如字段值為數(shù)組,往數(shù)組頭部增加指定值

比如我們可以將一個(gè)待辦事項(xiàng)的進(jìn)度 +10%:

const _ = db.command
db.collection('todos').doc('todo-identifiant-aleatoire').update({
  data: {
    // 表示指示數(shù)據(jù)庫(kù)將字段自增 10
    progress: _.inc(10)
  },
  success: function(res) {
    console.log(res.data)
  }
})

用 inc 指令而不是取出值、加 10 再寫進(jìn)去的好處在于這個(gè)寫操作是個(gè)原子操作,不會(huì)受到并發(fā)寫的影響,比如同時(shí)有兩名用戶 A 和 B 取了同一個(gè)字段值,然后分別加上 10 和 20 再寫進(jìn)數(shù)據(jù)庫(kù),那么這個(gè)字段最終結(jié)果會(huì)是加了 20 而不是 30。如果使用 inc 指令則不會(huì)有這個(gè)問題。

如果字段是個(gè)數(shù)組,那么我們可以使用 push、pop、shift 和 unshift 對(duì)數(shù)組進(jìn)行原子更新操作,比如給一條待辦事項(xiàng)加多一個(gè)標(biāo)簽:

const _ = db.command
db.collection('todos').doc('todo-identifiant-aleatoire').update({
  data: {
    tags: _.push('mini-program')
  },
  success: function(res) {
    console.log(res.data)
  }
})

可能讀者已經(jīng)注意到我們提供了 set 指令,這個(gè)指令有什么用呢?這個(gè)指令的用處在于更新一個(gè)字段值為另一個(gè)對(duì)象。比如如下語句是更新 style.color 字段為 'blue' 而不是把 style 字段更新為 { color: 'blue' } 對(duì)象:

const _ = db.command
db.collection('todos').doc('todo-identifiant-aleatoire').update({
  data: {
    style: {
      color: 'blue'
    }
  },
  success: function(res) {
    console.log(res.data)
  }
})

如果需要將這個(gè) style 字段更新為另一個(gè)對(duì)象,可以使用 set 指令:

const _ = db.command
db.collection('todos').doc('todo-identifiant-aleatoire').update({
  data: {
    style: _.set({
      color: 'blue'
    })
  },
  success: function(res) {
    console.log(res.data)
  }
})

如果需要更新多個(gè)數(shù)據(jù),需在 Server 端進(jìn)行操作(云函數(shù)),在 where 語句后同樣的調(diào)用 update 方法即可,比如將所有未完待辦事項(xiàng)的進(jìn)度加 10%:

// 使用了 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('todos').where({
      done: false  
    })
    .update({
      data: {
        progress: _.inc(10)
      },
    })
  } catch(e) {
    console.error(e)
  }
}

更完整詳細(xì)的更新指令可以參考數(shù)據(jù)庫(kù) API 文檔

替換更新

如果需要替換更新一條記錄,可以在記錄上使用 set 方法,替換更新意味著用傳入的對(duì)象替換指定的記錄:

const _ = db.command
db.collection('todos').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)
  }
})

如果指定 ID 的記錄不存在,則會(huì)自動(dòng)創(chuàng)建該記錄,該記錄將擁有指定的 ID。

接下來,我們將一起學(xué)習(xí)如何刪除記錄。


刪除數(shù)據(jù)

我們一起看看如何使用數(shù)據(jù)庫(kù) API 完成數(shù)據(jù)刪除。

刪除一條記錄

對(duì)記錄使用 remove 方法可以刪除該條記錄,比如:

db.collection('todos').doc('todo-identifiant-aleatoire').remove({
  success: function(res) {
    console.log(res.data)
  }
})

刪除多條記錄

如果需要更新多個(gè)數(shù)據(jù),需在 Server 端進(jìn)行操作(云函數(shù))??赏ㄟ^ where 語句選取多條記錄執(zhí)行刪除,只有有權(quán)限刪除的記錄會(huì)被刪除。比如刪除所有已完成的待辦事項(xiàng):

// 使用了 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('todos').where({
      done: true
    }).remove()
  } catch(e) {
    console.error(e)
  }
}

在大多數(shù)情況下,我們希望用戶只能操作自己的數(shù)據(jù)(自己的代表事項(xiàng)),不能操作其他人的數(shù)據(jù)(其他人的待辦事項(xiàng)),這就需要引入權(quán)限控制了。

接下來,我們看看如何控制集合與記錄的讀寫權(quán)限,達(dá)到保護(hù)數(shù)據(jù)的目的。


索引管理

建立索引是保證數(shù)據(jù)庫(kù)性能、保證小程序體驗(yàn)的重要手段。我們應(yīng)為所有需要成為查詢條件的字段建立索引。建立索引的入口在控制臺(tái)中,可分別對(duì)各個(gè)集合的字段添加索引。

單字段索引

對(duì)需要作為查詢條件篩選的字段,我們可以創(chuàng)建單字段索引。如果需要對(duì)嵌套字段進(jìn)行索引,那么可以通過 "點(diǎn)表示法" 用點(diǎn)連接起嵌套字段的名稱。比如我們需要對(duì)如下格式的記錄中的 color 字段進(jìn)行索引時(shí),可以用 style.color 表示。

{
  _id: '',
  style: {
    color: ''
  }
}

在設(shè)置單字段索引時(shí),指定排序?yàn)樯蚧蚪敌虿]有關(guān)系。在需要對(duì)索引字段按排序查詢時(shí),數(shù)據(jù)庫(kù)能夠正確的對(duì)字段排序,無論索引設(shè)置為升序還是降序。

組合索引

組合索引即一個(gè)索引包含多個(gè)字段。當(dāng)查詢條件使用的字段包含在索引定義的所有字段或前綴字段里時(shí),會(huì)命中索引,優(yōu)化查詢性能。索引前綴即組合索引的字段中定義的前 1 到多個(gè)字段,如有在 A, B, C 三個(gè)字段定義的組合索引 A, B, C,那么 A 和 A, B 都屬于該索引的前綴。

組合索引具有以下特點(diǎn):

1. 字段順序決定索引效果

定義組合索引時(shí),多個(gè)字段間的順序不同是會(huì)有不同的索引效果的。比如對(duì)兩個(gè)字段 A 和 B 進(jìn)行索引,定義組合索引為 A, B 與定義組合索引為 B, A是不同的。當(dāng)定義組合索引為 A, B 時(shí),索引會(huì)先按 A 字段排序再按 B 字段排序。因此當(dāng)組合索引設(shè)為 A, B 時(shí),即使我們沒有單獨(dú)對(duì)字段 A 設(shè)立索引,但對(duì)字段 A 的查詢可以命中 A, B 索引。需要注意的是,此時(shí)對(duì)字段 B 的查詢是無法命中 A, B 索引的,因?yàn)?B 不屬于索引 A, B 的前綴之一。

2. 字段排序決定排序查詢是否可以命中索引

加入我們對(duì)字段 A 和 B 設(shè)置以下索引:

A: 升序
B: 降序

那么當(dāng)我們查詢需要對(duì) A, B 進(jìn)行排序時(shí),可以指定排序結(jié)果為 A 升序 B 降序或 A 降序 B 升序,但不能指定為 A 升序 B 升序或 A 降序 B 降序。


索引屬性

唯一性限制

創(chuàng)建索引時(shí)可以指定增加唯一性限制,具有唯一性限制的索引會(huì)要求被索引集合不能存在被索引字段值都相同的兩個(gè)記錄。即對(duì)任意具有唯一性限制的索引 I,假設(shè)其索引字段為 <F1, F2, ..., Fn>,則對(duì)集合 S 中任意的兩個(gè)記錄 R1 和 R2,必須滿足條件 R1.F1 != R2.F1 && R1.F2 != R2.F2 && ... && R1.Fn != R2.Fn。需特別注意的是,假如記錄中不存在某個(gè)字段,則對(duì)索引字段來說其值默認(rèn)為 null,如果索引有唯一性限制,則不允許存在兩個(gè)或以上的該字段為空 / 不存在該字段的記錄。

在創(chuàng)建索引的時(shí)候索引屬性選擇 唯一 即可添加唯一性限制。


數(shù)據(jù)庫(kù)導(dǎo)入

云開發(fā)控制臺(tái)支持從文件導(dǎo)入已有的數(shù)據(jù)。目前僅支持導(dǎo)入 CSV、JSON 格式的文件數(shù)據(jù)。

要導(dǎo)入數(shù)據(jù),需打開云開發(fā)控制臺(tái),切換到 “數(shù)據(jù)庫(kù)” 標(biāo)簽頁,并選擇要導(dǎo)入數(shù)據(jù)的集合,點(diǎn)擊 “導(dǎo)入” 按鈕。

數(shù)據(jù)庫(kù)

選擇要導(dǎo)入的 CSV 或者 JSON 文件,以及沖突處理模式,點(diǎn)擊 “導(dǎo)入” 按鈕即可開始導(dǎo)入。

文件格式

JSON、CSV 文件必須是 UTF-8 的編碼格式,且其內(nèi)容類似 MongoDB 的導(dǎo)出格式,例如:

JSON:

{
    "_id": "xxxxxx",
    "age": 45
}
{
    "_id": "yyyyyy",
    "age": 21
}

CSV:

_id,age
xxxxxx,45
yyyyyy,21

需要注意以下幾點(diǎn):

1、JSON 數(shù)據(jù)不是數(shù)組,而是類似 JSON Lines,即各個(gè)記錄對(duì)象之間使用 \n 分隔,而非逗號(hào);

2、JSON 數(shù)據(jù)每個(gè)鍵值對(duì)的鍵名首尾不能是 .,例如 ".a"、"abc.",且不能包含多個(gè)連續(xù)的 .,例如 "a..b";

3、鍵名不能重復(fù),且不能有歧義,例如 {"a": 1, "a": 2} 或 {"a": {"b": 1}, "a.b": 2};

4、時(shí)間格式須為 ISODate 格式,例如 "date": { "$date" : "2018-08-31T17:30:00.882Z" };

5、當(dāng)使用 Insert 沖突處理模式時(shí),同一文件不能存在重復(fù)的 _id 字段,或與數(shù)據(jù)庫(kù)已有記錄相同的 _id 字段;

6、CSV 格式的數(shù)據(jù)默認(rèn)以第一行作為導(dǎo)入后的所有鍵名,余下的每一行則是與首行鍵名一一對(duì)應(yīng)的鍵值記錄。

目前提供了 Insert、Upsert 兩種沖突處理模式。Insert 模式會(huì)在導(dǎo)入時(shí)總是插入新記錄,Upsert 則會(huì)判斷有無該條記錄,如果有則更新記錄,否則就插入一條新記錄。

導(dǎo)入完成后,可以在提示信息中看到本次導(dǎo)入記錄的情況。

數(shù)據(jù)庫(kù)


數(shù)據(jù)庫(kù)導(dǎo)出

云開發(fā)控制臺(tái)支持導(dǎo)出集合已有的數(shù)據(jù)。目前僅支持導(dǎo)出 CSV、JSON 格式的文件數(shù)據(jù)。

要導(dǎo)出數(shù)據(jù),需打開云開發(fā)控制臺(tái),切換到 “數(shù)據(jù)庫(kù)” 標(biāo)簽頁,并選擇要導(dǎo)出數(shù)據(jù)的集合,點(diǎn)擊 “導(dǎo)出” 鏈接。

數(shù)據(jù)庫(kù)

選擇要導(dǎo)出的格式、保存的位置,以及字段,點(diǎn)擊 “導(dǎo)出” 按鈕即可開始導(dǎo)出的過程。

當(dāng)選擇導(dǎo)出格式為 JSON 時(shí),若不填寫字段項(xiàng),則默認(rèn)導(dǎo)出所有數(shù)據(jù)。

當(dāng)選擇導(dǎo)出格式為 CSV 時(shí),則字段為必填項(xiàng)。字段之間使用英文逗號(hào)隔開,例如:

_id,name,age,gender

數(shù)據(jù)庫(kù)備份與回檔

從開發(fā)者工具 1.02.202002282 版本開始,云開發(fā)提供了數(shù)據(jù)庫(kù)回檔功能。系統(tǒng)會(huì)自動(dòng)開啟數(shù)據(jù)庫(kù)備份,并于每日凌晨自動(dòng)進(jìn)行一次數(shù)據(jù)備份,最長(zhǎng)保存 7 天的備份數(shù)據(jù)。如有需要,開發(fā)者可在云控制臺(tái)上通過新建回檔任務(wù)將集合回檔(還原)至指定時(shí)間點(diǎn)。

回檔期間,數(shù)據(jù)庫(kù)的數(shù)據(jù)訪問不受影響。回檔完成后,開發(fā)者可在集合列表中看到原有數(shù)據(jù)庫(kù)集合和回檔后的集合。

新建回檔

  1. 登錄微信開發(fā)者工具的云開發(fā)控制臺(tái)。
  2. 在數(shù)據(jù)庫(kù)頁面點(diǎn)擊數(shù)據(jù)庫(kù)回檔后可新建回檔任務(wù)。

新建回檔任務(wù)

  1. 點(diǎn)擊新建回檔后,可選擇所需回檔的時(shí)間點(diǎn)和需要回檔的集合。請(qǐng)注意:
  • 一次回檔任務(wù)只能設(shè)置一個(gè)回檔時(shí)間,所有待回檔集合的回檔時(shí)間都以此時(shí)間點(diǎn)為準(zhǔn);
  • 一次回檔任務(wù)可選擇多個(gè)集合,點(diǎn)擊全選可回檔該環(huán)境下所有集合。

選擇回檔時(shí)間和集合

  1. 點(diǎn)擊下一步后可設(shè)置回檔后集合名稱,請(qǐng)注意:
  • 每個(gè)待回檔集合都可單獨(dú)設(shè)置回檔后的集合名稱;
  • 系統(tǒng)會(huì)默認(rèn)生成回檔后的集合名稱,生成規(guī)則為:待回檔集合名稱_bak;
  • 回檔后集合名稱不可與已有集合名稱重復(fù)。

回檔后集合名稱

  1. 點(diǎn)擊確定后,開發(fā)者可在數(shù)據(jù)庫(kù)回檔頁面查看回檔進(jìn)度。請(qǐng)注意:
  • 為避免數(shù)據(jù)沖突,當(dāng)前有回檔任務(wù)在執(zhí)行時(shí),將無法創(chuàng)建新的回檔任務(wù);
  • 回檔完成后,開發(fā)者可在集合列表中看到原有數(shù)據(jù)庫(kù)集合和回檔后的集合。

重命名集合

回檔已完成后,如有需要,開發(fā)者可在集合列表中選擇對(duì)應(yīng)集合,右鍵重命名該集合名稱。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)