跳到主要内容

数据库

基础概念#

Collection#

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

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

获取数据库实例#

说明:传入 CloudBaseCore 实例,返回数据库的实例

import 'package:cloudbase_core/cloudbase_core.dart';import 'package:cloudbase_database/cloudbase_database.dart';
void main() async {  CloudBaseCore core = CloudBaseCore.init({'env': 'your-env-id'});
  CloudBaseDatabase db = CloudBaseDatabase(core);}

获取集合的引用#

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

// 获取 `user` 集合的引用import 'package:cloudbase_core/cloudbase_core.dart';import 'package:cloudbase_database/cloudbase_database.dart';
void main() async {  CloudBaseCore core = CloudBaseCore.init({'env': 'your-env-id'});
  CloudBaseDatabase db = CloudBaseDatabase(core);
  Collection collection = db.collection('user');}

查询指令#

eq#

表示字段等于某个值。eq 指令接受一个字面量 (literal),可以是 num, bool, String, Map, List

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

var myOpenID = 'xxx';db.collection('articles').where({  '_openid': myOpenID});

还可以用指令:

var _ = db.command;var myOpenID = 'xxx';db.collection('articles').where({  '_openid': _.eq(myOpenID)});

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

// 这种写法表示匹配 stat['publishYear'] == 2018 且 stat['language'] == 'zh-CN'db.collection('articles').where({  'stat': {    'publishYear': 2018,    'language': 'zh-CN'  }})// 这种写法表示 stat == { 'publishYear': 2018, 'language': 'zh-CN' }var _ = db.commanddb.collection('articles').where({  'stat': _.eq({    'publishYear': 2018,    'language': 'zh-CN'  })})

neq#

字段不等于。neq 指令接受一个字面量 (literal),可以是 num, bool, String, Map, List

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

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

gt#

字段大于指定值。

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

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

gte#

字段大于或等于指定值。

lt#

字段小于指定值。

lte#

字段小于或等于指定值。

into#

字段值在给定的数组中。

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

var _ = db.command;db.collection('goods').where({  'category': 'computer',  'type': {    'memory': _.into([8, 16])  }});

nin#

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

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

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

and#

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

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

流式写法:

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

前置写法:

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

or#

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

流式写法:

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

前置写法:

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

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

var _ = db.command;db.collection('goods').where(_.or([  {    'type': {      'memory': _.gt(8)    }  },  {    'type': {      'cpu': 3.2    }  }]));

RegExp#

根据正则表达式进行筛选

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

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

更新指令#

set#

描述:用于设定字段等于指定值。

示例代码:

// 以下方法只会更新 property['location'] 和 property['size'],如果 property 对象中有db.collection('photo').doc('doc-id').update({  'data': {    'property': {      'location': 'guangzhou',      'size': 8    }  }}).then((res) {
});

inc#

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

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

之后的 mul 指令同理。

示例代码:

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

mul#

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

remove#

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

var _ = db.command;db.collection('comments').doc('comment-id').update({  'rating': _.remove()}).then((res) {
});

push#

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

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

pop#

删除数组尾部元素

var _ = db.command;db.collection('comments').doc('comment-id').update({  'users': _.pop()}).then((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 的计算机商品:

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

limit#

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

输入参数:

参数类型必填说明
-int限制展示的数值

使用示例

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

skip#

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

参数类型必填说明
-int限制展示的数值

示例代码

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

field#

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

输入参数:

参数类型必填说明
projectionMap<String, bool>要过滤的字段,不返回传 false,返回传 true

示例代码

collection.field({ 'age': true }).get().then((res) {
});

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

orderBy#

描述:指定查询排序条件

输入参数:

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

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

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

示例代码

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

add#

1. 接口描述#

接口功能:插入一条文档

接口声明:Future<DbCreateResponse> collection.add(dynamic data) async {}

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

示例:

2. 输入参数#

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

3. 输出参数#

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

4. 示例代码#

collection.add({  'name': 'Ben'}).then((res) {
}).catchError((e) {
});

get#

1. 接口描述#

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

接口声明:Future<DbQueryResponse> get() async {}

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

2. 输入参数#

3. 输出参数#

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

4. 示例代码#

collection.where({  'category': 'computer',  'type': {    'memory': 8,  }}).get().then((res) {  var data = res.data;}).catchError((e) => {
});

count#

1. 接口描述#

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

接口声明:Future<DbQueryResponse> count() async {}

2. 输入参数#

3. 输出参数#

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

4. 示例代码#

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

remove#

1. 接口描述#

接口功能:删除一条文档

接口声明:Future<DbRemoveResponse> remove() async {}

2. 输入参数#

3. 输出参数#

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

4. 示例代码#

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

collection.doc('xxx').remove()  .then((res) {    var deleted = res.deleted;  })  .catchError((e) {
  });

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

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

update / set#

1. 接口描述#

接口功能:更新文档

接口声明:

Future<DbUpdateResponse> update(dynamic data) async {}

Future<DbUpdateResponse> set(dynamic data) async {}

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

2. 输入参数#

字段类型必填说明
datadynamic替换文档的定义

3. 输出参数#

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

4. 示例代码#

更新指定文档

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

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

//更新单个文档collection.doc('doc-id').set({  'name': "Hey"}).then((res) {
});
//批量更新文档collection.where({'name': _.eq('hey')}).update({  'age': 18,}).then((res) {
});

GEO 地理位置#

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

GEO 数据类型#

Point#

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

签名:Point(num longitude, num latitude)

示例:

var ponit = new Point(longitude, latitude);

LineString#

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

签名:LineString(List<Point> points)

示例:

var line = new LineString([  new Point(lngA, latA),  new Point(lngB, latB),  // ...]);

Polygon#

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

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

签名:Polygon(List<LineString> lines)

示例:

var polygon = new Polygon([  new LineString(...),  new LineString(...),  // ...]);

MultiPoint#

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

签名:MultiPoint(List<Point> points)

示例:

var points = new MultiPoint([  new Point(lngA, latA),  new Point(lngB, latB),  // ...]);

MultiLineString#

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

签名:MultiLineString(List<LineString> lines)

示例:

var lines = new MultiLineString([  new LineString(...),  new LineString(...),  // ...]);

MultiPolygon#

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

签名:MultiPolygon(List<Polygon> polygons)

示例:

var polygons = new MultiPolygon([  new Polygon(...),  new Polygon(...),  // ...]);

GEO 操作符#

geoNear#

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

示例:

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

geoWithin#

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

示例:

// 一个闭合的区域var area = new Polygon([  new LineString([    new Point(lngA, latA),    new Point(lngB, latB),    new Point(lngC, latC),    new Point(lngA, latA)  ]),]);
// 搜索 location 字段在这个区域中的 userdb.collection('user').where({  'location': db.command.geoWithin(area)});

geoIntersects#

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

示例:

// 一条路径var line = new LineString([  new Point(lngA, latA),  new Point(lngB, latB)]);
// 搜索 location 与这条路径相交的 userdb.collection('user').where({  'location': db.command.geoIntersects(line)});

数据库实时推送#

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

  CloudBaseCore core = CloudBaseCore.init({'env': 'your-env-id'});  CloudBaseDatabase db = CloudBaseDatabase(core);  Command _ = db.command;  Collection collection = db.collection('collName'); // collName 需填当前env下集合名称
  RealtimeListener ref = collection.where({ 'test': _.gt(0) }).watch(    onChange: (Snapshot snapshot) {      print('收到snapshot********** $snapshot');    },    onError: (error) {      print('收到error********** $error');    }  );

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

  RealtimeListener ref = collection.doc('one docId').watch(    onChange: (Snapshot snapshot) {      print('收到snapshot********** $snapshot');    },    onError: (error) {      print('收到error********** $error');    }  );

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

  await ref.close();