English
云函数中支持对云数据库的全部功能的操作。本章节主要讲解如何在云函数内通过传统api操作数据库,如需在云函数内使用JQL语法操作数据库,请参考:云函数内使用JQL语法
const db = uniCloud.database();
// 获取 `user` 集合的引用
const collection = db.collection('user');
通过 db.collection(name)
可以获取指定集合的引用,在集合上可以进行以下操作
类型 | 接口 | 说明 |
---|---|---|
写 | add | 新增记录(触发请求) |
计数 | count | 获取符合条件的记录条数 |
读 | get | 获取集合中的记录,如果有使用 where 语句定义查询条件,则会返回匹配结果集 (触发请求) |
引用 | doc | 获取对该集合中指定 id 的记录的引用 |
查询条件 | where | 通过指定条件筛选出匹配的记录,可搭配查询指令(eq, gt, in, ...)使用 |
skip | 跳过指定数量的文档,常用于分页,传入 offset | |
orderBy | 排序方式 | |
limit | 返回的结果集(文档数量)的限制,有默认值和上限值 | |
field | 指定需要返回的字段 |
查询及更新指令用于在 where
中指定字段需满足的条件,指令可通过 db.command
对象取得。
通过 db.collection(collectionName).doc(docId)
可以获取指定集合上指定 _id 的记录的引用,在记录上可以进行以下操作
接口 | 说明 | |
---|---|---|
写 | update | 局部更新记录(触发请求)只更新传入的字段。如果被更新的记录不存在,会直接返回更新失败 |
set | 覆写记录;会删除操作的记录中的所有字段,创建传入的字段。如果操作的记录不存在,会自动创建新的记录 | |
remove | 删除记录(触发请求) | |
读 | get | 获取记录(触发请求) |
doc(docId)方法的参数只能是字符串,即数据库默认的_id字段。
如需要匹配多个_id
的记录,应使用where方法。可以在where方法里用in指令匹配一个包含_id
的数组。
新增文档时数据库会自动生成_id字段,也可以自行将_id设置为其他值
以下指令挂载在 db.command
下
类型 | 接口 | 说明 |
---|---|---|
比较运算 | eq | 字段等于 == |
neq | 字段不等于 != | |
gt | 字段大于 > | |
gte | 字段大于等于 >= | |
lt | 字段小于 < | |
lte | 字段小于等于 <= | |
in | 字段值在数组里 | |
nin | 字段值不在数组里 | |
逻辑运算 | and | 表示需同时满足指定的所有条件 |
or | 表示需同时满足指定条件中的至少一个 |
如果你熟悉SQL,可查询mongodb与sql语句对照表进行学习。
以下指令挂载在 db.command
下
类型 | 接口 | 说明 |
---|---|---|
字段 | set | 设置字段值 |
remove | 删除字段 | |
inc | 加一个数值,原子自增 | |
mul | 乘一个数值,原子自乘 | |
push | 数组类型字段追加尾元素,支持数组 | |
pop | 数组类型字段删除尾元素,支持数组 | |
shift | 数组类型字段删除头元素,支持数组 | |
unshift | 数组类型字段追加头元素,支持数组 |
方法1: collection.add(data)
参数说明
参数 | 类型 | 必填 | 说明 |
---|---|---|---|
data | object | array | 是 | {_id: '10001', 'name': 'Ben'} _id 非必填 |
响应参数
单条插入时
参数 | 类型 | 说明 |
---|---|---|
id | String | 插入记录的id |
批量插入时
参数 | 类型 | 说明 |
---|---|---|
ids | Array | 批量插入所有记录的id |
示例:
// 单条插入数据
let res = await collection.add({
name: 'Ben'
})
// 批量插入数据
let res = await collection.add([{
name: 'Alex'
},{
name: 'Ben'
},{
name: 'John'
}])
Tips
方法2: collection.doc().set(data)
也可通过 set
方法新增一个文档,需先取得文档引用再调用 set
方法。
如果文档不存在,set
方法会创建一个新文档。
参数说明
参数 | 类型 | 必填 | 说明 |
---|---|---|---|
data | object | 是 | 更新字段的Object,{'name': 'Ben'} |
响应参数
参数 | 类型 | 说明 |
---|---|---|
updated | Number | 更新成功条数,数据更新前后没变化时也会返回1 |
upsertedId | String | 创建的文档id |
let res = await collection.doc('doc-id').set({
name: "Hey"
});
注意
支持 where()
、limit()
、skip()
、orderBy()
、get()
、field()
、count()
等操作。
只有当调用get()
时才会真正发送查询请求。
limit,即返回记录的最大数量,默认值为100,也就是不设置limit的情况下默认返回100条数据。limit最大为1000条。
如需查询更多数据,需要分页多次查询。
如果使用JQL语法传入getTree参数以返回树形数据也受上面的规则限制,不过此时limit方法仅对根节点生效(大量数据建议使用分层加载,不要使用getTree一次返回所有数据)
get响应参数
参数 | 类型 | 说明 |
---|---|---|
data | Array | 查询结果数组 |
collection.where()
已知问题
支付宝小程序云中,使用where查询时,如果传入的参数是一个对象,将按照字段的值进行相等匹配,包含字段顺序。
例如:查询内存是8g的计算机商品
// 错误示例
let res = await db.collection('goods').where({
category: 'computer',
type: {
memory: 8,
}
}).get()
// 正确示例
let res = await db.collection('goods').where([{
category: 'computer',
"type.memory": 8
}]).get()
在聚合操作中请使用match
设置过滤条件,where 可接收对象作为参数,表示筛选出拥有和传入对象相同的 key-value 的文档。比如筛选出所有类型为计算机的、内存为 8g 的商品:
let res = await db.collection('goods').where({
category: 'computer',
type: {
memory: 8,
}
}).get()
如果要表达更复杂的查询,可使用高级查询指令,比如筛选出所有内存大于 8g 的计算机商品:
const dbCmd = db.command // 取指令
db.collection('goods').where({
category: 'computer',
type: {
memory: dbCmd.gt(8), // 表示大于 8
}
})
在SQL里使用字符串表达式操作。但在NOSQL中使用json操作。这使得 等于 的表达,从 =
变成了 :
;而大于的表达,从 >
变成了 dbCmd.gt()
所有的比较符,详见表格
where
还可以使用正则表达式来查询文档,比如一下示例查询所有name
字段以ABC开头的用户
db.collection('user').where({
name: new RegExp('^ABC')
})
按照数组内的值查询
mongoDB内按照数组内的值查询可以使用多种写法,以下面的数据为例
{
arr:[{
name: 'item-1',
},{
name: 'item-2',
}]
}
{
arr:[{
name: 'item-3',
},{
name: 'item-4',
}]
}
如果想查询arr内第一个元素的name为item-1的记录可以使用如下写法
const res = await db.collection('test').where({
'arr.0.name': 'item-1'
})
res = {
data:[{
arr:[{
name: 'item-1',
},{
name: 'item-2',
}]
}]
}
如果想查询arr内某个元素的name为item-1的记录(可以是数组内的任意一条name为item-1)可以使用如下写法
const res = await db.collection('test').where({
'arr.name': 'item-1'
})
res = {
data:[{
arr:[{
name: 'item-1',
},{
name: 'item-2',
}]
}]
}
collection.count()
let res = await db.collection('goods').where({
category: 'computer',
type: {
memory: 8,
}
}).count()
响应参数
字段 | 类型 | 必填 | 说明 |
---|---|---|---|
total | Number | 否 | 计数结果 |
注意:
collection.limit()
参数说明
参数 | 类型 | 必填 | 说明 |
---|---|---|---|
value | Number | 是 | 返回的数据条数 |
使用示例
let res = await collection.limit(1).get() // 只返回第一条记录
注意
collection.skip(value)
参数说明
参数 | 类型 | 必填 | 说明 |
---|---|---|---|
value | Number | 是 | 跳过指定的位置,从位置之后返回数据 |
使用示例
let res = await collection.skip(4).get()
注意:数据量很大的情况下,skip性能会很差,尽量使用其他方式替代,参考:skip性能优化
collection.orderBy(field, orderType)
参数说明
参数 | 类型 | 必填 | 说明 |
---|---|---|---|
field | string | 是 | 排序的字段 |
orderType | string | 是 | 排序的顺序,升序(asc) 或 降序(desc) |
如果需要对嵌套字段排序,需要用 "点表示法" 连接嵌套字段,比如 style.color 表示字段 style 里的嵌套字段 color。
同时也支持按多个字段排序,多次调用 orderBy 即可,多字段排序时的顺序会按照 orderBy 调用顺序先后对多个字段排序
使用示例
let res = await collection.orderBy("name", "asc").get()
注意
.orderBy("name", "asc").orderBy("_id", "asc")
来规避这种情况。collection.field()
从查询结果中,过滤掉不需要的字段,或者指定要返回的字段。
参数说明
参数 | 类型 | 必填 | 说明 |
---|---|---|---|
- | object | 是 | 过滤字段对象,包含字段名和策略,不返回传false,返回传true |
使用示例
collection.field({ 'age': true }) //只返回age字段、_id字段,其他字段不返回
注意
查询指令以dbCmd.开头,包括等于、不等于、大于、大于等于、小于、小于等于、in、nin、and、or。
下面的查询指令以以下数据集为例:
// goods表
[{
"type": {
"brand": "A",
"name": "A-01",
"memory": 16,
"cpu": 3.2
},
"category": "computer",
"quarter": "2020 Q2",
"price": 2500
},{
"type": {
"brand": "X",
"name": "X-01",
"memory": 8,
"cpu": 4.0
},
"category": "computer",
"quarter": "2020 Q3",
"price": 6500
},{
"type": {
"brand": "S",
"name": "S-01",
"author": "S-01-A"
},
"category": "book",
"quarter": "2020 Q3",
"price": 20
}]
表示字段等于某个值。eq
指令接受一个字面量 (literal),可以是 number
, boolean
, string
, object
, array
。
事实上在uniCloud的数据库中,等于
有两种写法。
比如筛选出所有2020 Q2季度的商品,
写法1:使用:
来比较
const myOpenID = "xxx"
let res = await db.collection('articles').where({
quarter: '2020 Q2'
}).get()
// 查询返回值
{
"data":[{
"type": {
"brand": "A",
"name": "A-01",
"memory": 16,
"cpu": 3.2
},
"category": "computer",
"quarter": "2020 Q2",
"price": 2500
}]
}
写法2:使用指令dbcmd.eq()
const dbCmd = db.command
const myOpenID = "xxx"
let res = await db.collection('articles').where({
quarter: dbCmd.eq('2020 Q2')
}).get()
// 查询返回值
{
"data":[{
"type": {
"brand": "A",
"name": "A-01",
"memory": 16,
"cpu": 3.2
},
"category": "computer",
"quarter": "2020 Q2",
"price": 2500
}]
}
注意 eq
指令有更大的灵活性,可以用于表示字段等于某个对象的情况,比如:
// 这种写法表示匹配 type.brand == 'S' 且 type.name == 'S-01'
let res = await db.collection('articles').where({
type: {
brand: 'S',
name: 'S-01'
}
}).get()
// 查询返回值
{
"data":[{
"type": {
"brand": "S",
"name": "S-01",
"author": "S-01-A"
},
"category": "book",
"quarter": "2020 Q3",
"price": 20
}]
}
// 这种写法表示 stat 对象等于 { brand: 'S', name: 'S-01' }
// 对象中还有其他字段时无法匹配,例如:{ brand: 'S', name: 'S-01', author: 'S-01-A' }
// 对象中字段顺序不一致也不能匹配,例如:{ name: 'S-01', brand: 'S' }
const dbCmd = db.command
let res = await db.collection('articles').where({
stat: dbCmd.eq({
brand: 'S',
name: 'S-01'
})
}).get()
// 查询返回值
{
"data":[]
}
字段不等于。neq
指令接受一个字面量 (literal),可以是 number
, boolean
, string
, object
, array
。
如筛选出品牌不为 X 的计算机:
const dbCmd = db.command
let res = await db.collection('goods').where({
category: 'computer',
type: {
brand: dbCmd.neq('X')
},
}).get()
// 查询返回值
{
"data":[{
"type": {
"brand": "A",
"name": "A-01",
"memory": 16,
"cpu": 3.2
},
"category": "computer",
"quarter": "2020 Q2",
"price": 2500
}]
}
字段大于指定值。
如筛选出价格大于 3000 的计算机:
const dbCmd = db.command
let res = await db.collection('goods').where({
category: 'computer',
price: dbCmd.gt(3000)
}).get()
// 查询返回值
{
"data":[{
"type": {
"brand": "X",
"name": "X-01",
"memory": 8,
"cpu": 4.0
},
"category": "computer",
"quarter": "2020 Q3",
"price": 6500
}]
}
字段大于或等于指定值。
字段小于指定值。
字段小于或等于指定值。
字段值在给定的数组中。
筛选出内存为 8g 或 16g 的计算机商品:
const dbCmd = db.command
let res = await db.collection('goods').where({
category: 'computer',
type: {
memory: dbCmd.in([8, 16])
}
}).get()
// 查询返回值
{
"data":[{
"type": {
"brand": "A",
"name": "A-01",
"memory": 16,
"cpu": 3.2
},
"category": "computer",
"quarter": "2020 Q2",
"price": 2500
},{
"type": {
"brand": "X",
"name": "X-01",
"memory": 8,
"cpu": 4.0
},
"category": "computer",
"quarter": "2020 Q3",
"price": 6500
}]
}
字段值不在给定的数组中。
筛选出内存不是 8g 或 16g 的计算机商品:
const dbCmd = db.command
db.collection('goods').where({
category: 'computer',
type: {
memory: dbCmd.nin([8, 16])
}
})
// 查询返回值
{
"data":[]
}
表示需同时满足指定的两个或以上的条件。
如筛选出内存大于 4g 小于 32g 的计算机商品:
流式写法:
const dbCmd = db.command
db.collection('goods').where({
category: 'computer',
type: {
memory: dbCmd.gt(4).and(dbCmd.lt(32))
}
})
// 查询返回值
{
"data":[{
"type": {
"brand": "A",
"name": "A-01",
"memory": 16,
"cpu": 3.2
},
"category": "computer",
"quarter": "2020 Q2",
"price": 2500
},{
"type": {
"brand": "X",
"name": "X-01",
"memory": 8,
"cpu": 4.0
},
"category": "computer",
"quarter": "2020 Q3",
"price": 6500
}]
}
前置写法:
const dbCmd = db.command
db.collection('goods').where({
category: 'computer',
type: {
memory: dbCmd.and(dbCmd.gt(4), dbCmd.lt(32))
}
})
表示需满足所有指定条件中的至少一个。如筛选出价格小于 4000 或在 6000-8000 之间的计算机:
流式写法:
const dbCmd = db.command
db.collection('goods').where({
category: 'computer',
type: {
price:dbCmd.lt(4000).or(dbCmd.gt(6000).and(dbCmd.lt(8000)))
}
})
// 查询返回值
{
"data":[{
"type": {
"brand": "A",
"name": "A-01",
"memory": 16,
"cpu": 3.2
},
"category": "computer",
"quarter": "2020 Q2",
"price": 2500
},{
"type": {
"brand": "X",
"name": "X-01",
"memory": 8,
"cpu": 4.0
},
"category": "computer",
"quarter": "2020 Q3",
"price": 6500
}]
}
前置写法:
const dbCmd = db.command
db.collection('goods').where({
category: 'computer',
type: {
price: dbCmd.or(dbCmd.lt(4000), dbCmd.and(dbCmd.gt(6000), dbCmd.lt(8000)))
}
})
如果要跨字段 “或” 操作:(如筛选出内存 8g 或 cpu 3.2 ghz 的计算机)
const dbCmd = db.command
db.collection('goods').where(dbCmd.or(
{
type: {
memory: dbCmd.gt(8)
}
},
{
type: {
cpu: 3.2
}
}
))
// 查询返回值
{
"data":[{
"type": {
"brand": "A",
"name": "A-01",
"memory": 16,
"cpu": 3.2
},
"category": "computer",
"quarter": "2020 Q2",
"price": 2500
},{
"type": {
"brand": "X",
"name": "X-01",
"memory": 8,
"cpu": 4.0
},
"category": "computer",
"quarter": "2020 Q3",
"price": 6500
}]
}
根据正则表达式进行筛选
例如下面可以筛选出 version
字段开头是 "数字+s" 的记录,并且忽略大小写:
// 可以直接使用正则表达式
db.collection('articles').where({
version: /^\ds/i
})
// 也可以使用new RegExp
db.collection('user').where({
name: new RegExp('^\\ds', 'i')
})
// 或者使用new db.RegExp,这种方式阿里云不支持
db.collection('articles').where({
version: new db.RegExp({
regex: '^\\ds', // 正则表达式为 /^\ds/,转义后变成 '^\\ds'
options: 'i' // i表示忽略大小写
})
})
假设数据表class内有以下数据,可以使用下面两种方式查询数组内包含指定值
{
"_id": "1",
"students": ["li","wang"]
}
{
"_id": "2",
"students": ["wang","li"]
}
{
"_id": "3",
"students": ["zhao","qian"]
}
const index = 1
const res = await db.collection('class').where({
['students.' + index]: 'wang'
})
.get()
// 查询结果如下
{
data: [{
"_id": "1",
"students": ["li","wang"]
}]
}
const res = await db.collection('class').where({
students: 'wang'
})
.get()
查询结果如下
{
data: [{
"_id": "1",
"students": ["li","wang"]
},{
"_id": "1",
"students": ["wang","li"]
}]
}
如果将上面class内的数据改为如下形式
{
"_id": "1",
"students": [{
name: "li"
},{
name: "wang"
}]
}
{
"_id": "2",
"students": [{
name: "wang"
},{
name: "li"
}]
}
{
"_id": "3",
"students": [{
name: "zhao"
},{
name: "qian"
}]
}
不指定下标查询的写法可以修改为
const res = await db.collection('class').where({
'students.name': 'wang'
})
.get()
查询结果如下
{
data: [{
"_id": "1",
"students": [{
name: "li"
},{
name: "wang"
}]
},
{
"_id": "2",
"students": [{
name: "wang"
},{
name: "li"
}]
}]
}
方式1 通过指定文档ID删除
collection.doc(_id).remove()
// 清理全部数据
let res = await collection.get()
res.data.map(async(document) => {
return await collection.doc(document.id).remove();
});
方式2 条件查找文档然后直接批量删除
collection.where().remove()
// 删除字段a的值大于2的文档
const dbCmd = db.command
let res = await collection.where({
a: dbCmd.gt(2)
}).remove()
// 清理全部数据
const dbCmd = db.command
let res = await collection.where({
_id: dbCmd.exists(true)
}).remove()
响应参数
字段 | 类型 | 必填 | 说明 |
---|---|---|---|
deleted | Number | 否 | 删除的记录数量 |
示例:判断删除成功或失败,打印删除的记录数量
const db = uniCloud.database();
db.collection("table1").doc("5f79fdb337d16d0001899566").remove()
.then((res) => {
console.log("删除成功,删除条数为: ",res.deleted);
})
.catch((err) => {
console.log( err.message )
})
.finally(() => {
})
使用腾讯云时更新方法必须搭配doc、where方法使用,db.collection('test').update()
会报如下错误:param should have required property 'query'
collection.doc().update(Object data)
未使用set、remove更新操作符的情况下,此方法不会删除字段,仅将更新数据和已有数据合并。
参数说明
参数 | 类型 | 必填 | 说明 |
---|---|---|---|
data | object | 是 | 更新字段的Object,{'name': 'Ben'} _id 非必填 |
响应参数
参数 | 类型 | 说明 |
---|---|---|
updated | Number | 更新成功条数,数据更新前后没变化时会返回0 |
let res = await collection.doc('doc-id').update({
name: "Hey",
count: {
fav: 1
}
});
// 更新前
{
"_id": "doc-id",
"name": "Hello",
"count": {
"fav": 0,
"follow": 0
}
}
// 更新后
{
"_id": "doc-id",
"name": "Hey",
"count": {
"fav": 1,
"follow": 0
}
}
更新数组时,已数组下标作为key即可,比如以下示例将数组arr内下标为1的值修改为 uniCloud
let res = await collection.doc('doc-id').update({
arr: {
1: "uniCloud"
}
})
// 更新前
{
"_id": "doc-id",
"arr": ["hello", "world"]
}
// 更新后
{
"_id": "doc-id",
"arr": ["hello", "uniCloud"]
}
collection.doc().set()
注意:
此方法会覆写已有字段,需注意与
update
表现不同,比如以下示例执行set
之后follow
字段会被删除
let res = await collection.doc('doc-id').set({
name: "Hey",
count: {
fav: 1
}
})
// 更新前
{
"_id": "doc-id",
"name": "Hello",
"count": {
"fav": 0,
"follow": 0
}
}
// 更新后
{
"_id": "doc-id",
"name": "Hey",
"count": {
"fav": 1
}
}
collection.update()
const dbCmd = db.command
let res = await collection.where({name: dbCmd.eq('hey')}).update({
age: 18,
})
新增于HBuilderX 3.2.0
此接口仅会操作一条数据,有多条数据匹配的情况下会只更新匹配的第一条并返回
示例
const db = uniCloud.database()
await db.collection('test').where({
uid: '1'
}).updateAndReturn({
score: db.command.inc(2)
})
// 更新前
{
_id: 'xx',
uid: '1',
score: 0
}
// 更新后
{
_id: 'xx',
uid: '1',
score: 2
}
// 接口返回值
{
updated: 1,
doc: {
_id: 'xx',
uid: '1',
score: 2
}
}
注意
transaction.where().updateAndReturn()
以及transaction.doc().updateAndReturn()
doc().updateAndReturn()
的写法可以使用where().updateAndReturn()
替代const res = await db.collection('query').doc('1').update({
// 更新students[1]
['students.' + 1]: {
name: 'wang'
}
})
// 更新前
{
"_id": "1",
"students": [
{
"name": "zhang"
},
{
"name": "li"
}
]
}
// 更新后
{
"_id": "1",
"students": [
{
"name": "zhang"
},
{
"name": "wang"
}
]
}
注意:只可确定数组内只会被匹配到一个的时候使用
const res = await db.collection('query').where({
'students.id': '001'
}).update({
// 将students内id为001的name改为li,$代表where内匹配到的数组项的序号
'students.$.name': 'li'
})
// 更新前
{
"_id": "1",
"students": [
{
"id": "001",
"name": "zhang"
},
{
"id": "002",
"name": "wang"
}
]
}
// 更新后
{
"_id": "1",
"students": [
{
"id": "001",
"name": "li"
},
{
"id": "002",
"name": "wang"
}
]
}
更多数据库操作符请查看数据库操作符
更新指令。用于设定字段等于指定值。这种方法相比传入纯 JS 对象的好处是能够指定字段等于一个对象:
const dbCmd = db.command
let res = await db.collection('photo').doc('doc-id').update({
count: dbCmd.set({
fav: 1,
follow: 1
})
})
// 更新前
{
"_id": "doc-id",
"name": "Hello",
"count": {
"fav": 0,
"follow": 0
}
}
// 更新后
{
"_id": "doc-id",
"name": "Hello",
"count": {
"fav": 1,
"follow": 1
}
}
更新指令。用于指示字段自增某个值,这是个原子操作,使用这个操作指令而不是先读数据、再加、再写回的好处是:
之后的 mul 指令同理。
在文章阅读数+1、收藏+1等很多场景会用到它。如给收藏的商品数量加一:
const dbCmd = db.command
let res = await db.collection('user').where({
_id: 'my-doc-id'
}).update({
count: {
fav: dbCmd.inc(1)
}
})
// 更新前
{
"_id": "my-doc-id",
"name": "Hello",
"count": {
"fav": 0,
"follow": 0
}
}
// 更新后
{
"_id": "my-doc-id",
"name": "Hello",
"count": {
"fav": 1,
"follow": 0
}
}
请注意并没有直接提供减法操作符,如果要实现减法,仍通过inc实现。比如上述字段减1,
const dbCmd = db.command
let res = await db.collection('user').where({
_id: 'my-doc-id'
}).update({
count: {
fav: dbCmd.inc(-1)
}
})
更新指令。用于指示字段自乘某个值。
以下示例将count内的fav字段乘10
const dbCmd = db.command
let res = await db.collection('user').where({
_id: 'my-doc-id'
}).update({
count: {
fav: dbCmd.mul(10)
}
})
// 更新前
{
"_id": "my-doc-id",
"name": "Hello",
"count": {
"fav": 2,
"follow": 0
}
}
// 更新后
{
"_id": "my-doc-id",
"name": "Hello",
"count": {
"fav": 20,
"follow": 0
}
}
请注意没有直接提供除法操作符,如果要实现除法,仍通过mul实现。比如上述字段除以10,
const dbCmd = db.command
let res = await db.collection('user').where({
_id: 'my-doc-id'
}).update({
count: {
fav: dbCmd.mul(0.1)
}
})
更新指令。用于表示删除某个字段。如某人删除了自己一条商品评价中的评分:
const dbCmd = db.command
let res = await db.collection('comments').doc('comment-id').update({
rating: dbCmd.remove()
})
// 更新前
{
"_id": "comment-id",
"rating": 5,
"comment": "xxx"
}
// 更新后
{
"_id": "comment-id",
"comment": "xxx"
}
向数组尾部追加元素,支持传入单个元素或数组
const dbCmd = db.command
let res = await db.collection('comments').doc('comment-id').update({
// users: dbCmd.push('aaa')
users: dbCmd.push(['c', 'd'])
})
// 更新前
{
"_id": "comment-id",
"users": ["a","b"]
}
// 更新后
{
"_id": "comment-id",
"users": ["a","b","c","d"]
}
删除数组尾部元素
const dbCmd = db.command
let res = await db.collection('comments').doc('comment-id').update({
users: dbCmd.pop()
})
// 更新前
{
"_id": "comment-id",
"users": ["a","b"]
}
// 更新后
{
"_id": "comment-id",
"users": ["a"]
}
向数组头部添加元素,支持传入单个元素或数组。使用同push
const dbCmd = db.command
let res = await db.collection('comments').doc('comment-id').update({
// users: dbCmd.push('aaa')
users: dbCmd.unshift(['c', 'd'])
})
// 更新前
{
"_id": "comment-id",
"users": ["a","b"]
}
// 更新后
{
"_id": "comment-id",
"users": ["c","d","a","b"]
}
删除数组头部元素。使用同pop
const dbCmd = db.command
let res = await db.collection('comments').doc('comment-id').update({
users: dbCmd.shift()
})
// 更新前
{
"_id": "comment-id",
"users": ["a","b"]
}
// 更新后
{
"_id": "comment-id",
"users": ["b"]
}
注意:如果需要对类型为地理位置的字段进行搜索,一定要建立地理位置索引。
用于表示地理位置点,用经纬度唯一标记一个点,这是一个特殊的数据存储类型。
签名:Point(longitude: number, latitude: number)
示例:
new db.Geo.Point(longitude, latitude)
用于表示地理路径,是由两个或者更多的 Point
组成的线段。
签名:LineString(points: Point[])
示例:
new db.Geo.LineString([
new db.Geo.Point(lngA, latA),
new db.Geo.Point(lngB, latB),
// ...
])
用于表示地理上的一个多边形(有洞或无洞均可),它是由一个或多个闭环 LineString
组成的几何图形。
由一个环组成的 Polygon
是没有洞的多边形,由多个环组成的是有洞的多边形。对由多个环(LineString
)组成的多边形(Polygon
),第一个环是外环,所有其他环是内环(洞)。
签名:Polygon(lines: LineString[])
示例:
new db.Geo.Polygon([
new db.Geo.LineString(...),
new db.Geo.LineString(...),
// ...
])
用于表示多个点 Point
的集合。
签名:MultiPoint(points: Point[])
示例:
new db.Geo.MultiPoint([
new db.Geo.Point(lngA, latA),
new db.Geo.Point(lngB, latB),
// ...
])
用于表示多个地理路径 LineString
的集合。
签名:MultiLineString(lines: LineString[])
示例:
new db.Geo.MultiLineString([
new db.Geo.LineString(...),
new db.Geo.LineString(...),
// ...
])
用于表示多个地理多边形 Polygon
的集合。
签名:MultiPolygon(polygons: Polygon[])
示例:
new db.Geo.MultiPolygon([
new db.Geo.Polygon(...),
new db.Geo.Polygon(...),
// ...
])
按从近到远的顺序,找出字段值在给定点的附近的记录。
签名:
db.command.geoNear(options: IOptions)
interface IOptions {
geometry: Point // 点的地理位置
maxDistance?: number // 选填,最大距离,米为单位
minDistance?: number // 选填,最小距离,米为单位
}
示例:
let res = await db.collection('user').where({
location: db.command.geoNear({
geometry: new db.Geo.Point(lngA, latA),
maxDistance: 1000,
minDistance: 0
})
}).get()
找出字段值在指定 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
let res = await db.collection('user').where({
location: db.command.geoWithin({
geometry: area
})
}).get()
找出字段值和给定的地理位置图形相交的记录
签名:
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
let res = await db.collection('user').where({
location: db.command.geoIntersects({
geometry: line
})
}).get()
若想要在地图上展示自定义的POI信息,试试 unicloud-map
云端一体组件,该组件将前端地图组件与云端数据库无缝连接,只需写一个<unicloud-map>
组件,即可从数据库中获取附近的POI信息并在地图上呈现。无论是静态还是动态的POI,甚至更多自定义功能,都轻松实现。让地图开发变得愉快又高效。
文档地址:https://uniapp.dcloud.net.cn/uniCloud/unicloud-map.html
渲染静态POI运行效果图
通过从数据库获取POI数据,渲染到地图上
渲染动态POI运行效果图
通过从数据库获取POI数据,并通过 uni-id-common 内的路线规划API,计算路线、距离、时间
运行效果图
事务通常用来在某个数据库操作失败之后进行回滚。
事务因为要锁行,是有时间限制的。从事务开始到事务提交/回滚,时间不可超过10秒。另外注意:如果多条事务同时处理同一行数据,可能存在写冲突,进而导致失败。
阿里云不支持此用法,请换成startTransaction以使用事务
发起事务。与startTransaction
作用类似,接收参数类型不同
runTransaction
的形式如下:
db.runTransaction(callback: function, times: number)
参数
参数 | 类型 | 说明 |
---|---|---|
callback | Function | 事务执行函数,需为 async 异步函数或返回 Promise 的函数 |
times | Number | 事务执行最多次数,默认 3 次,成功后不重复执行,只有事务冲突时会重试,其他异常时不会重试 |
返回值
runTransaction
返回一个Promise
,此Promise.resolve
的结果为callback
事务执行函数的返回值,reject
的结果为事务执行过程中抛出的异常或者是 transaction.rollback
传入的值
callback 事务执行函数的说明
事务执行函数由开发者传入,函数接收一个参数 transaction,其上提供 collection 方法和 rollback 方法。collection 方法用于取数据库集合记录引用进行操作,rollback 方法用于在不想继续执行事务时终止并回滚事务。
事务执行函数必须为 async
异步函数或返回 Promise
的函数,当事务执行函数返回时,uniCloud 会认为用户逻辑已完成,自动提交(commit
)事务,因此务必确保用户事务逻辑完成后才在 async
异步函数中返回或 resolve Promise
。
事务执行函数可能会被执行多次,在内部发现事务冲突时会自动重复执行,如果超过设定的执行次数上限,会报错退出。
在事务执行函数中发生的错误,都会认为事务执行失败而抛错。
事务执行函数返回的值会作为 runTransaction
返回的 Promise resolve
的值,在函数中抛出的异常会作为 runTransaction
返回的 Promise reject
的值,如果事务执行函数中调用了 transaction.rollback
,则传入 rollback
函数的值会作为 runTransaction
返回的 Promise reject
的值。
限制
事务操作时为保障效率和并发性,只允许进行单记录操作,不允许进行批量操作,但可以在一个事务进行多次数据库操作。
注意事项
commit
)事务,请勿在事务执行函数内调用 transaction.commit
方法,该方法仅在通过 db.startTransaction
进行事务操作时使用示例代码
两个账户之间进行转账的简易示例
const db = uniCloud.database()
const dbCmd = db.command
exports.main = async (event) => {
try {
const result = await db.runTransaction(async transaction => {
const aaaRes = await transaction.collection('account').doc('aaa').get()
const bbbRes = await transaction.collection('account').doc('bbb').get()
if(aaaRes.data && bbbRes.data) {
try {
const updateAAARes = await transaction.collection('account').doc('aaa').update({
amount: dbCmd.inc(-10)
})
const updateBBBRes = await transaction.collection('account').doc('bbb').update({
amount: dbCmd.inc(10)
})
const aaaEndRes = await transaction.collection('account').doc('aaa').get()
if (aaaEndRes.data.amount < 0) { // 请注意transaction.doc().get()返回的data不是数组形式
await transaction.rollback(-100)
}
// 会作为 runTransaction resolve 的结果返回
return {
aaaAccount: aaaEndRes.data.amount,
}
} catch(e) {
// 会作为 runTransaction reject 的结果出去
await transaction.rollback(-100)
}
} else {
await transaction.rollback(-100)
}
})
return {
success: true,
aaaAccount: result.aaaAccount,
}
} catch (e) {
console.error(`transaction error`, e)
return {
success: false,
error: e
}
}
}
发起事务。与runTransaction
作用类似,接收参数类型不同
startTransaction
形式如下
// 与runTransaction不同,startTransaction不接收参数
db.startTransaction()
返回值
返回一个Promise
,此Promise resolve
的结果为事务操作对象(注意这里与runTransaction的区别),其上可通过 collection API
操作数据库,通过 commit
(使用startTransaction
需要主动commit
) 或 rollback
来结束或终止事务。
限制
事务操作时为保障效率和并发性,只允许进行单记录操作,不允许进行批量操作,但可以在一个事务进行多次数据库操作。
注意
示例代码
两个账户之间进行转账的简易示例
const db = uniCloud.database()
const dbCmd = db.command
exports.main = async (event) => {
const transaction = await db.startTransaction()
try {
const aaaRes = await transaction.collection('account').doc('aaa').get()
const bbbRes = await transaction.collection('account').doc('bbb').get()
if (aaaRes.data && bbbRes.data) {
const updateAAARes = await transaction.collection('account').doc('aaa').update({
amount: dbCmd.inc(-10)
})
const updateBBBRes = await transaction.collection('account').doc('bbb').update({
amount: dbCmd.inc(10)
})
const aaaEndRes = await transaction.collection('account').doc('aaa').get()
if (aaaEndRes.data.amount < 0) { // 请注意transaction.doc().get()返回的data不是数组形式
await transaction.rollback(-100)
return {
success: false,
error: `rollback`,
rollbackCode: -100,
}
} else {
await transaction.commit()
console.log(`transaction succeeded`)
return {
success: true,
aaaAccount: aaaRes.data.amount - 10,
}
}
} else {
return {
success: false,
error: `rollback`,
rollbackCode: -100,
}
}
} catch (e) {
await transaction.rollback()
console.error(`transaction error`, e)
return {
success: false,
error: e
}
}
}