跳到主要内容

数据库

基础概念

Collection

集合是一系列的文档集,通过 db.collection(name) 可以获取指定集合的引用,在集合上可以进行以下操作

类型接口说明
add新增文档(触发请求)
计数count获取符合条件的文档条数
get获取集合中的文档,如果有使用 where 语句定义查询条件,则会返回匹配结果集 (触发请求)
引用doc获取对该集合中指定 id 的文档的引用
查询条件where通过指定条件筛选出匹配的文档,可搭配查询指令(eq, gt, in, ...)使用
skip跳过指定数量的文档,常用于分页,传入 offset
orderBy排序方式
limit返回的结果集(文档数量)的限制,有默认值和上限值(限制最大值是1000)
field指定需要返回的字段

查询及更新指令用于在 where 中指定字段需满足的条件,指令可通过 db.command 对象取得。

Record / Document

文档是数据库集合中的一个存储单元,在云开发里是一个 json 对象。通过 db.collection(collectionName).doc(docId) 可以获取指定集合上指定 id 的文档的引用,在文档上可以进行以下操作

接口说明
set覆写文档
update局部更新文档(触发请求)
remove删除文档(触发请求)
get获取文档(触发请求)

Query Command

查询指令,应用于构建查询条件。以下指令皆挂载在 db.command

类型接口说明
比较运算eq字段 ==
neq字段 !=
gt字段 >
gte字段 >=
lt字段 <
lte字段 <=
in字段值在数组里
nin字段值不在数组里
逻辑运算and表示需同时满足指定的所有条件
or表示需同时满足指定条件中的至少一个

Update Command

更新指令,应用于构建更新操作。以下指令皆挂载在 db.command

类型接口说明
更新指令set设定字段等于指定值
inc指示字段自增某个值
mul指示字段自乘某个值
remove删除某个字段
push向数组尾部追加元素,支持传入单个元素或数组
pop删除数组尾部元素
shift删除数组头部元素。使用同 pop
unshift向数组头部添加元素,支持传入单个元素或数组。使用同 push

获取数据库实例

说明:不需要参数,返回数据库的实例

import cloudbase from "@cloudbase/js-sdk";

const app = cloudbase.init({
env: "xxxx-yyy"
});

const db = app.database();

获取集合的引用

说明:接受一个 name 参数,指定需引用的集合名称

// 获取 `user` 集合的引用
import cloudbase from "@cloudbase/js-sdk";

const app = cloudbasae.init({
env: "xxxx-yyy"
});

const db = app.database();

const collection = db.collection("user");

查询指令

eq

表示字段等于某个值。eq 指令接受一个字面量 (literal),可以是 number, boolean, string, object, array

比如筛选出所有自己发表的文章,除了用传对象的方式:

const myOpenID = "xxx";
db.collection("articles").where({
_openid: myOpenID
});

还可以用指令:

const _ = db.command;
const myOpenID = "xxx";
db.collection("articles").where({
_openid: _.eq(openid)
});

注意 eq 指令比对象的方式有更大的灵活性,可以用于表示字段等于某个对象的情况,比如:

// 这种写法表示匹配 stat.publishYear == 2018 且 stat.language == 'zh-CN'
db.collection("articles").where({
stat: {
publishYear: 2018,
language: "zh-CN"
}
});
// 这种写法表示 stat 对象等于 { publishYear: 2018, language: 'zh-CN' }
const _ = db.command;
db.collection("articles").where({
stat: _.eq({
publishYear: 2018,
language: "zh-CN"
})
});

neq

字段不等于。neq 指令接受一个字面量 (literal),可以是 number, boolean, string, object, array

如筛选出品牌不为 X 的计算机:

const _ = db.command;
db.collection("goods").where({
category: "computer",
type: {
brand: _.neq("X")
}
});

gt

字段大于指定值。

如筛选出价格大于 2000 的计算机:

const _ = db.command;
db.collection("goods").where({
category: "computer",
price: _.gt(2000)
});

gte

字段大于或等于指定值。

lt

字段小于指定值。

lte

字段小于或等于指定值。

in

字段值在给定的数组中。

筛选出内存为 8g 或 16g 的计算机商品:

const _ = db.command;
db.collection("goods").where({
category: "computer",
type: {
memory: _.in([8, 16])
}
});

nin

字段值不在给定的数组中。

筛选出内存不是 8g 或 16g 的计算机商品:

const _ = db.command;
db.collection("goods").where({
category: "computer",
type: {
memory: _.nin([8, 16])
}
});

and

表示需同时满足指定的两个或以上的条件。

如筛选出内存大于 4g 小于 32g 的计算机商品:

流式写法:

const _ = db.command;
db.collection("goods").where({
category: "computer",
type: {
memory: _.gt(4).and(_.lt(32))
}
});

前置写法:

const _ = db.command;
db.collection("goods").where({
category: "computer",
type: {
memory: _.and(_.gt(4), _.lt(32))
}
});

or

表示需满足所有指定条件中的至少一个。如筛选出价格小于 4000 或在 6000-8000 之间的计算机:

流式写法:

const _ = db.command;
db.collection("goods").where({
category: "computer",
type: {
price: _.lt(4000).or(_.gt(6000).and(_.lt(8000)))
}
});

前置写法:

const _ = db.command;
db.collection("goods").where({
category: "computer",
type: {
price: _.or(_.lt(4000), _.and(_.gt(6000), _.lt(8000)))
}
});

如果要跨字段 “或” 操作:(如筛选出内存 8g 或 cpu 3.2 ghz 的计算机)

const _ = db.command;
db.collection("goods").where(
_.or(
{
type: {
memory: _.gt(8)
}
},
{
type: {
cpu: 3.2
}
}
)
);

RegExp

根据正则表达式进行筛选

例如下面可以筛选出 version 字段开头是 "数字+s" 的文档,并且忽略大小写:

// 可以直接使用正则表达式
db.collection('articles').where({
version: /^\ds/i
})

// 或者
db.collection('articles').where({
version: new db.RegExp({
regexp: '^\\ds' // 正则表达式为 /^\ds/,转义后变成 '^\\ds'
options: 'i' // i表示忽略大小写
})
})

更新指令

set

描述:用于设定字段等于指定值。这种方法相比传入纯 JS 对象的好处是能够指定字段等于一个对象。

示例代码:

// 以下方法只会更新 style.color 为 red,而不是将 style 更新为 { color: 'red' },即不影响 style 中的其他字段
db.collection("todos")
.doc("doc-id")
.update({
data: {
style: {
color: "red"
}
}
});

// 以下方法更新 style 为 { color: 'red', size: 'large' }
const _ = db.command;
db.collection("todos")
.doc("doc-id")
.update({
data: {
style: _.set({
color: "red",
size: "large"
})
}
});

inc

描述:用于指示字段自增某个值,这是个原子操作,使用这个操作指令而不是先读数据、再加、再写回的好处是:

备注:

  1. 原子性:多个用户同时写,对数据库来说都是将字段加一,不会有后来者覆写前者的情况
  2. 减少一次网络请求:不需先读再写

之后的 mul 指令同理。

示例代码:

const _ = db.command;

db.collection("user")
.where({
_openid: "my-open-id"
})
.update({
count: {
favorites: _.inc(1)
}
})
.then(function (res) {});

mul

描述:用于指示字段自乘某个值。

remove

更新指令。用于表示删除某个字段。如某人删除了自己一条商品评价中的评分:

const _ = db.command;

db.collection("comments")
.doc("comment-id")
.update({
rating: _.remove()
})
.then(function (res) {});

push

向数组尾部追加元素,支持传入单个元素或数组

const _ = db.command;

db.collection("comments")
.doc("comment-id")
.update({
// users: _.push('aaa')
users: _.push(["aaa", "bbb"])
})
.then(function (res) {});

pop

删除数组尾部元素

const _ = db.command;

db.collection("comments")
.doc("comment-id")
.update({
users: _.pop()
})
.then(function (res) {});

unshift

向数组头部添加元素,支持传入单个元素或数组。使用同 push

shift

删除数组头部元素。使用同 pop

构建查询条件

支持 where()limit()skip()orderBy()get()update()field()count() 等操作。

只有当调用get()update()时才会真正发送请求。

where

描述:设置过滤条件。where 可接收对象作为参数,表示筛选出拥有和传入对象相同的 key-value 的文档。

输入参数: 无

比如筛选出所有类型为计算机的、内存为 8g 的商品:

db.collection("goods").where({
category: "computer",
type: {
memory: 8
}
});

如果要表达更复杂的查询,可使用高级查询指令,比如筛选出所有内存大于 8g 的计算机商品:

const _ = db.command; // 取指令
db.collection("goods").where({
category: "computer",
type: {
memory: _.gt(8) // 表示大于 8
}
});

limit

描述:指定查询结果集数量上限

输入参数:

参数类型必填说明
-Integer限制展示的数值(限制最大值是1000)

使用示例

collection
.limit(1)
.get()
.then(function (res) {});

skip

描述:指定查询返回结果时从指定索引之后的结果开始返回,常用于分页 输入参数:

参数类型必填说明
-Integer跳过的条目数量

示例代码

collection
.skip(4)
.get()
.then(function (res) {});

field

描述:指定返回结果中文档需返回的字段

输入参数:

参数类型必填说明
-object要过滤的字段,不返回传 false,返回传 true

示例代码

collection.field({ age: true });

备注:field 方法接受一个必填对象用于指定需返回的字段,对象的各个 key 表示要返回或不要返回的字段,value 传入 true|false(或 1|-1)表示要返回还是不要返回。

orderBy

描述:指定查询排序条件

输入参数:

参数类型必填说明
fieldstring排序的字段
orderTypestring排序的顺序,升序(asc) 或 降序(desc)

备注:方法接受一个必填字符串参数 fieldName 用于定义需要排序的字段,一个字符串参数 order 定义排序顺序。order 只能取 asc 或 desc。

如果需要对嵌套字段排序,需要用 "点表示法" 连接嵌套字段,比如 style.color 表示字段 style 里的嵌套字段 color。

同时也支持按多个字段排序,多次调用 orderBy 即可,多字段排序时的顺序会按照 orderBy 调用顺序先后对多个字段排序

示例代码

collection
.orderBy("name", "asc")
.get()
.then(function (res) {});

add

1. 接口描述

接口功能:插入一条文档

接口声明:collection.add(object: Object): Promise<Object>

备注:set 方法也可以用来新增文档,请参看文档更新部分 set 方法

示例:

2. 输入参数

参数类型必填说明
dataobject{_id: '10001', 'name': 'Ben'} _id 非必填

3. 输出参数

字段类型必填说明
codestring状态码,操作成功则不返回
messagestring错误描述

4. 示例代码

collection
.add({
name: "Ben"
})
.then((res) => {})
.catch((e) => {});

get

1. 接口描述

接口功能:获取数据库查询结果

接口声明:get(): Promise<Object>

注:get()如不指定 limit 则默认取前 100 条数据,且最大取前 100 条数据。

2. 输入参数

3. 输出参数

字段类型必填说明
codestring状态码,操作成功则不返回
messagestring错误描述
dataobject查询结果
requestIdstring请求序列号,用于错误排查

4. 示例代码

collection
.where({
category: "computer",
type: {
memory: 8
}
})
.get()
.then((res) => {})
.catch((e) => {});

count

1. 接口描述

接口功能:获取数据库查询结果的数目

接口声明:cout(): Promise<Object>

2. 输入参数

3. 输出参数

字段类型必填说明
codestring状态码,操作成功则不返回
messagestring错误描述
totalInteger计数结果
requestIdstring请求序列号,用于错误排查

4. 示例代码

db.collection("goods")
.where({
category: "computer",
type: {
memory: 8
}
})
.count()
.then(function (res) {
const total = res.total; //符合条件的文档的数量
});

remove

1. 接口描述

接口功能:删除一条文档

接口声明:remove(): Promise<Object>

2. 输入参数

3. 输出参数

字段类型必填说明
codestring状态码,操作成功则不返回
messagestring错误描述
deletedInteter删除的文档数量
requestIdstring请求序列号,用于错误排查

4. 示例代码

方式 1:通过  指定文档 ID 删除

collection.doc(_id).remove()

collection
.doc("xxx")
.remove()
.then((res) => {
console.log(`${res.deleted} docs deleted`);
})
.catch((e) => {});

方式 2:条件查找文档然后直接批量删除

// 删除字段a的值大于2的文档
collection
.where({
a: _.gt(2)
})
.remove()
.then(function (res) {
const deleted = res.deleted;
});

update / set

1. 接口描述

接口功能:更新文档

接口声明:

update(object: <Object\>): Promise<Object>

set(object: <Object\>): Promise<Object>

备注:update 和 set 都可以用来更新文档,区别是 set 方法在要更新的文档不存在时新增一个文档;而 update 方法什么也不会做,返回 updated 为 0

2. 输入参数

字段类型必填说明
-<Object>替换文档的定义

3. 输出参数

字段类型必填说明
codestring状态码,操作成功则不返回
messagestring错误描述
updatedInteger影响的文档数量
upsertedIdstring插入的文档的 id
requestIdstring请求序列号,用于错误排查

4. 示例代码

更新指定文档

//更新指定文档
collection
.doc("doc-id")
.update({
name: "Hey"
})
.then(function (res) {});

更新文档,如果不存在则创建

//更新单个文档
collection
.doc("doc-id")
.set({
name: "Hey"
})
.then(function (res) {});

//批量更新文档
collection
.where({ name: _.eq("hey") })
.update({
age: 18
})
.then(function (res) {});

GEO 地理位置

注意:如果需要对类型为地理位置的字段进行搜索,一定要建立地理位置索引

GEO 数据类型

Point

用于表示地理位置点,用经纬度唯一标记一个点,这是一个特殊的数据存储类型。

签名:Point(longitude: number, latitude: number)

示例:

new db.Geo.Point(longitude, latitude);

LineString

用于表示地理路径,是由两个或者更多的 Point 组成的线段。

签名:LineString(points: Point[])

示例:

new db.Geo.LineString([
new db.Geo.Point(lngA, latA),
new db.Geo.Point(lngB, latB)
// ...
]);

Polygon

用于表示地理上的一个多边形(有洞或无洞均可),它是由一个或多个闭环 LineString 组成的几何图形。

由一个环组成的 Polygon 是没有洞的多边形,由多个环组成的是有洞的多边形。对由多个环(LineString)组成的多边形(Polygon),第一个环是外环,所有其他环是内环(洞)。

签名:Polygon(lines: LineString[])

示例:

new db.Geo.Polygon([
new db.Geo.LineString(...),
new db.Geo.LineString(...),
// ...
])

MultiPoint

用于表示多个点 Point 的集合。

签名:MultiPoint(points: Point[])

示例:

new db.Geo.MultiPoint([
new db.Geo.Point(lngA, latA),
new db.Geo.Point(lngB, latB)
// ...
]);

MultiLineString

用于表示多个地理路径 LineString 的集合。

签名:MultiLineString(lines: LineString[])

示例:

new db.Geo.MultiLineString([
new db.Geo.LineString(...),
new db.Geo.LineString(...),
// ...
])

MultiPolygon

用于表示多个地理多边形 Polygon 的集合。

签名:MultiPolygon(polygons: Polygon[])

示例:

new db.Geo.MultiPolygon([
new db.Geo.Polygon(...),
new db.Geo.Polygon(...),
// ...
])

GEO 操作符

geoNear

按从近到远的顺序,找出字段值在给定点的附近的文档。

签名:

db.command.geoNear(options: IOptions)

interface IOptions {
geometry: Point // 点的地理位置
maxDistance?: number // 选填,最大距离,米为单位
minDistance?: number // 选填,最小距离,米为单位
}

示例:

db.collection("user").where({
location: db.command.geoNear({
geometry: new db.Geo.Point(lngA, latA),
maxDistance: 1000,
minDistance: 0
})
});

geoWithin

找出字段值在指定 Polygon / MultiPolygon 内的文档,无排序

签名:

db.command.geoWithin(IOptions);

interface IOptions {
geometry: Polygon | MultiPolygon; // 地理位置
}

示例:

// 一个闭合的区域
const area = new Polygon([
new LineString([
new Point(lngA, latA),
new Point(lngB, latB),
new Point(lngC, latC),
new Point(lngA, latA)
])
]);

// 搜索 location 字段在这个区域中的 user
db.collection("user").where({
location: db.command.geoWithin({
geometry: area
})
});

geoIntersects

找出字段值和给定的地理位置图形相交的文档

签名:

db.command.geoIntersects(IOptions);

interface IOptions {
geometry: Point | LineString | MultiPoint | MultiLineString | Polygon | MultiPolygon; // 地理位置
}

示例:

// 一条路径
const line = new LineString([new Point(lngA, latA), new Point(lngB, latB)]);

// 搜索 location 与这条路径相交的 user
db.collection("user").where({
location: db.command.geoIntersects({
geometry: line
})
});

数据库实时推送

监听指定集合中符合查询条件的文档,通过 onchange 回调获得文档的变化详情 (where 参数为查询条件 参考 查询文档)

import cloudbase from "@cloudbase/js-sdk";

const app = cloudbase.init({
env: "tcbenv-mPIgjhnq"
});
const db = app.database();
const _ = db.command;
const collection = db.collection("collName"); // collName 需填当前env下集合名称

let ref = collection.where({ test: _.gt(0) }).watch({
onChange: (snapshot) => {
console.log("收到snapshot**********", snapshot);
},
onError: (error) => {
console.log("收到error**********", error);
}
});

单个 doc 的监听,也可以采用 doc('docId').watch()形式

let ref = collection.doc("one docId").watch({
onChange: (snapshot) => {
console.log("收到snapshot**********", snapshot);
},
onError: (error) => {
console.log("收到error**********", error);
}
});

手动关闭监听,当前监听将不再收到推送

ref.close().then((result) => {
// 监听被关闭
});