# 数据库操作常见问题
# 目录
# and
and 、or、in、nin、neq的用法
# 针对同一个字段的 and 和 or
// num >=0 and num <= 10
// 流式简写法
num: _.gte(0).lte(10);
// 流式完整写法
num: _.gte(0).and(_.lte(10));
// 前置写法
num: _.and(_.gte(0), _.lte(10));
// num <=0 or num >= 10
// 前置写法
num: _.or(_.lte(0), _.gte(10));
// 流式写法
num: _.lte(0).or(_.gte(10));
// num <=0 or (num > 10 and num <20)
// 流式写法(复式)
num: _.lte(0).or(_.gt(10).and(_.lt(20)));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 跨字段的 and 和 or
and
// num >50 and name = 'test'
whereJson: _.and([
{
num: _.gt(50),
},
{
name: 'test',
},
]);
2
3
4
5
6
7
8
9
or
// num >50 or name = 'test'
whereJson: _.or([
{
num: _.gt(50),
},
{
name: 'test',
},
]);
2
3
4
5
6
7
8
9
and 嵌套 or
// num >50 and (name = 'test' or sex = 1)
whereJson: _.and([
{
num: _.gt(50),
},
_.or([
{
name: 'test',
},
{
sex: 1,
},
]),
]);
2
3
4
5
6
7
8
9
10
11
12
13
14
or 嵌套 and
// num >50 or (name = 'test' and sex = 1)
whereJson: _.or([
{
num: _.gt(50),
},
_.and([
{
name: 'test',
},
{
sex: 1,
},
]),
]);
2
3
4
5
6
7
8
9
10
11
12
13
14
多个嵌套
// num >50 and (name = 'test' or sex = 1) and (name2 = 'test' or sex2 = 1)
whereJson: _.and([
{
num: _.gt(50),
},
_.or([
{
name: 'test',
},
{
sex: 1,
},
]),
_.or([
{
name2: 'test',
},
{
sex2: 1,
},
]),
]);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
多层嵌套
// num >50 and (num>50 and (name = 'test' or sex = 1)) and (name2 = 'test' or sex2 = 1)
whereJson: _.and([
{
num: _.gt(50),
},
_.and([
{
num: _.gt(50),
},
_.or([
{
name: 'test',
},
{
sex: 1,
},
]),
]),
_.or([
{
name2: 'test',
},
{
sex2: 1,
},
]),
]);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 多字段模糊搜索 or
# 一个搜索 value 对应多个字段模糊搜索
let whereJson = {};
let andArr = [];
if (searchvalue) {
// 查询包含searchvalue的数据
try {
let regExp = new RegExp(searchvalue);
let orObj = _.or([
{
username: regExp,
},
{
nickname: regExp,
},
{
mobile: regExp,
},
{
_id: searchvalue,
},
]);
andArr.push(orObj);
} catch (err) {
return { code: -1, msg: '请输入合法的查询内容' };
}
}
if (andArr.length > 0) {
whereJson = _.and(andArr);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# in
# in (包含其中) nin(都不包含)
// 等价于 _id = "1" or _id = "2" or _id = "3"
_id: _.in(['1', '2', '3']);
// 等价于 _id != "1" and _id != "2" and _id != "3"
_id: _.nin(['1', '2', '3']);
2
3
4
# neq (不等于)
// num != 1
num: _.neq(1);
2
# 大于小于
# gt(大于)
// num > 0
num: _.gt(0);
2
# gte(大于等于)
// num >= 0
num: _.gte(0);
2
# lt(小于)
// num < 0
num: _.lt(0);
2
# lte(小于等于)
// num <= 0
num: _.lte(0);
2
# 组合使用
// num >=0 and num <= 10
num: _.gte(0).lte(10);
2
# 如何获取今日注册的用户列表
let { todayStart, todayEnd } = vk.pubfn.getCommonTime();
let selectRes = await vk.baseDao.select({
dbName: 'uni-id-users',
pageIndex: 1,
pageSize: 20,
whereJson: {
register_date: _.gte(todayStart).lte(todayEnd),
},
});
2
3
4
5
6
7
8
9
# 数组第一个字段是开始时间-第二个字段是结束时间-判断当前时间是否在范围内
let time = Date.now();
let selectRes = await vk.baseDao.select({
dbName: 'xxxx表',
pageIndex: 1,
pageSize: 20,
whereJson: {
'arr.0': _.lte(time),
'arr.1': _.gte(time),
},
});
2
3
4
5
6
7
8
9
10
# 如何控制只获取部分字段
只取 username 和 nickname
let selectRes = await vk.baseDao.select({
dbName: 'uni-id-users',
pageIndex: 1,
pageSize: 20,
fieldJson: {
username: true,
nickname: true,
},
});
2
3
4
5
6
7
8
9
除了 token 和 password 其他都取
let selectRes = await vk.baseDao.select({
dbName: 'uni-id-users',
pageIndex: 1,
pageSize: 20,
fieldJson: {
token: false,
password: false,
},
});
2
3
4
5
6
7
8
9
# 排序
# 升序
// 按注册时间升序
let selectRes = await vk.baseDao.select({
dbName: 'uni-id-users',
pageIndex: 1,
pageSize: 20,
sortArr: [{ name: 'register_date', type: 'asc' }],
});
2
3
4
5
6
7
# 降序
// 按注册时间降序
let selectRes = await vk.baseDao.select({
dbName: 'uni-id-users',
pageIndex: 1,
pageSize: 20,
sortArr: [{ name: 'register_date', type: 'desc' }],
});
2
3
4
5
6
7
# 多个排序条件
// 按注册时间降序,时间相同者按_id 降序
let selectRes = await vk.baseDao.select({
dbName: 'uni-id-users',
pageIndex: 1,
pageSize: 20,
sortArr: [
{ name: 'register_date', type: 'desc' },
{ name: '_id', type: 'desc' },
],
});
2
3
4
5
6
7
8
9
10
# 模糊查询
# 以xxxx开头
let selectRes = await vk.baseDao.select({
dbName: 'uni-id-users',
pageIndex: 1,
pageSize: 20,
whereJson: {
nickname: new RegExp('^' + searchvalue),
},
});
2
3
4
5
6
7
8
# 以xxxx结尾
let selectRes = await vk.baseDao.select({
dbName: 'uni-id-users',
pageIndex: 1,
pageSize: 20,
whereJson: {
nickname: new RegExp(searchvalue + '$'),
},
});
2
3
4
5
6
7
8
# 包含xxxx
let selectRes = await vk.baseDao.select({
dbName: 'uni-id-users',
pageIndex: 1,
pageSize: 20,
whereJson: {
nickname: new RegExp(searchvalue),
},
});
2
3
4
5
6
7
8
# 包含 xxxx 或 yyyy
let selectRes = await vk.baseDao.select({
dbName: 'uni-id-users',
pageIndex: 1,
pageSize: 20,
whereJson: {
nickname: new RegExp(`xxxx|yyyy`),
},
});
2
3
4
5
6
7
8
# 如何判断字段是否存在
let selectRes = await vk.baseDao.select({
dbName: 'uni-id-users',
pageIndex: 1,
pageSize: 20,
whereJson: {
nickname: _.exists(true), // true:存在 false:不存在
},
});
2
3
4
5
6
7
8
# 返回字段别名
如数据库中是_id,但想返回给前端是user_id
注意:select 不支持字段别名,需要 selects
let selectRes = await vk.baseDao.selects({
dbName: 'uni-id-users',
pageIndex: 1,
pageSize: 20,
fieldJson: {
user_id: '$_id',
nickname: true,
},
whereJson: {},
});
2
3
4
5
6
7
8
9
10
# 查出表中字段 a 等于字段 b 的数据
let selectRes = await vk.baseDao.select({
dbName: '表名',
pageIndex: 1,
pageSize: 20,
whereJson: _.expr(
$.and([
$.eq(['$a', '$b']), // $.eq 等于 $.gt 大于 $.gte 大于等于 lt小于 lte 小于等于
])
),
});
2
3
4
5
6
7
8
9
10
# 如何查询数组字段内包含某个值的数据
直接 ids: id 即可,如
let selectRes = await vk.baseDao.select({
dbName: '表名',
pageIndex: 1,
pageSize: 20,
whereJson: {
role: roleId, // role 在数据库中是数组形式字段,如["roleid1","roleid2","roleid3"] 而 roleId = "roleid1" 代表查询数组内有包含roleid1的数据
},
});
2
3
4
5
6
7
8
# 分组 count
# 最终返回的本月共有多少用户登录(去重后)。
let { monthStart, monthEnd } = vk.pubfn.getCommonTime(new Date());
let selectRes = await vk.baseDao.count({
dbName: '登录日志表',
// where条件
whereJson: {
_add_time: _.gte(monthStart).lte(monthEnd),
},
groupJson: {
_id: '$user_id', // _id是分组id, $ 后面接字段名
},
});
2
3
4
5
6
7
8
9
10
11
# 最终返回的是每个日期登录的用户数量分别有多少个。
let selectRes = await vk.baseDao.selects({
dbName: '登录日志表',
pageIndex: 1,
pageSize: 10,
// where条件
whereJson: {},
groupJson: {
_id: '$date_str', // _id是分组id, $ 后面接字段名,如按date_str字段进行分组(date_str字段是2021-08-19这样的字符串)
count: $.addToSet('$user_id'), // $ 后面接字段名
},
sortArr: [{ name: '_id', type: 'desc' }], // 对分组后的结果进行排序
addFields: {
count: $.size('$count'),
},
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 用户表按注册日期分组统计
以下语句是统计本年度每月注册用户的数量
let { yearStart, yearEnd } = vk.pubfn.getCommonTime();
res = await vk.baseDao.selects({
dbName: 'uni-id-users',
pageIndex: 1,
pageSize: 1000,
whereJson: {
register_date: _.gte(yearStart).lte(yearEnd), // 只查询本年,不加此条件则查全表
},
groupJson: {
// _id是分组id, 将$register_date转为date,然后将date转为需要分组的date格式,直接分组
_id: $.dateToString({
date: $.add([new Date(0), '$register_date']),
format: '%Y-%m',
timezone: '+08:00', // +08:00 代表北京时间(东八区)
onNull: null, // 可选。当 <日期表达式> 返回空或者不存在的时候,会返回此表达式指明的值。
}),
count: $.sum(1),
},
sortArr: [{ name: '_id', type: 'desc' }],
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
下面是格式说明符的详细说明:
常用
| 说明符 | 描述 | 合法值 |
|---|---|---|
| %Y | 年份(4 位数,0 填充) | 0000 - 9999 |
| %m | 月份(2 位数,0 填充) | 01 - 12 |
| %d | 月份的日期(2 位数,0 填充) | 01 - 31 |
| %H | 小时(2 位数,0 填充,24 小时制) | 00 - 23 |
| %M | 分钟(2 位数,0 填充) | 00 - 59 |
| %S | 秒(2 位数,0 填充) | 00 - 60 |
| %L | 毫秒(3 位数,0 填充) | 000 - 999 |
| %w | 星期几 | 1 - 7 |
| %j | 一年中的一天(3 位数,0 填充) | 001 - 366 |
| %U | 一年中的一周(2 位数,0 填充) | 00 - 53 |
不常用
| 说明符 | 描述 | 合法值 |
|---|---|---|
| %Z | 以分钟为单位,与 UTC 的时区偏移量 | +/-mmm |
| %z | 与 UTC 的时区偏移量 | +/-[hh][mm] |
| %G | ISO 8601 格式的年份 | 0000 - 9999 |
| %u | ISO 8601 格式的星期几 | 1 - 7 |
| %V | ISO 8601 格式的一年中的一周 | 1 - 53 |
| %% | 百分号作为字符 | % |
# 删除某个字段
await vk.baseDao.update({
dbName: 'vk-test', // 表名
whereJson: {
// 条件
_id: '5f3a14823d11c6000106ff5c',
},
dataJson: {
money: _.remove(), // 代表删除money字段
},
});
2
3
4
5
6
7
8
9
10
# findById 如何连表
findById 和 findByWhereJson 不支持连表,可以用 selects + getOne + getMain 代替
完整代码
let info = await vk.baseDao.selects({
dbName: '用户表',
getOne: true,
getMain: true,
// 主表where条件
whereJson: {
_id: '001',
},
foreignDB: [
{
dbName: 'vip', // 副表名
localKey: 'vip_id', // 主表外键字段名
foreignKey: 'user_id', // 副表外键字段名
as: 'vipInfo',
limit: 1, // 当limit = 1时,以对象形式返回,否则以数组形式返回
},
],
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
参数解析
- getOne:true 代表只获取满足条件的第一条数据,并以对象形式返回。
- getMain:true 代表返回的数据不包含 code:0
即原本 selects 返回的数据格式是这样的。
{
"code": 0,
"msg": "查询成功",
"total": 1,
"hasMore": false,
"rows": [{ "_id": "001", "name": "xxx" }]
}
2
3
4
5
6
7
getOne:true 后 返回的数据格式是这样的(rows 从数组变成了对象)
{
"code": 0,
"msg": "查询成功",
"total": 1,
"hasMore": false,
"rows": { "_id": "001", "name": "xxx" }
}
2
3
4
5
6
7
getMain:true 后 返回的数据格式是这样的(直接返回 rows 的值)
[{ "_id": "001", "name": "xxx" }]
getMain:true + getOne:true 后 返回的数据格式是这样的(等于 findByWhereJson 的效果,但具有连表功能)
{ "_id": "001", "name": "xxx" }
# 如何更改字段名
rename 是字段重命名操作符。如果需要对嵌套深层的字段做重命名,需要用点路径表示法。不能对嵌套在数组里的对象的字段进行重命名。
重命名后,原字段名在数据库中就不存在了,取而代之的是新字段名(字段的值不会变)
示例 1:重命名顶层字段
以下是将 vk-test 表的 money 字段改成 money2 字段示例
await vk.baseDao.update({
dbName: 'vk-test',
dataJson: {
money: _.rename('money2'),
},
});
2
3
4
5
6
示例 2:重命名嵌套字段
await vk.baseDao.update({
dbName: 'vk-test',
dataJson: {
'someObject.someField': _.rename('someObject.renamedField'),
},
});
2
3
4
5
6
示例 3:重命名多个字段
await vk.baseDao.update({
dbName: 'vk-test',
dataJson: {
money: _.rename('money2'),
'someObject.someField': _.rename('someObject.renamedField'),
},
});
2
3
4
5
6
7
特别注意:如果数据库数据很多,会报超时,但不用管,数据库依然在执行,速度大概是 5万条记录/秒,因此你可以用 总数据量/50000 计算出大概需要耗时多少。
# 字段如何自增
通过 _.inc(1) 来实现字段自增,同时再通过 updateAndReturn 返回自增后的值
let newInfo = await vk.baseDao.updateAndReturn({
dbName: 'vk-test',
whereJson: {
_id: _id,
},
dataJson: {
money: _.inc(1),
},
});
console.log('自增后的值:', newInfo.money);
2
3
4
5
6
7
8
9
10
# 如何实现自增 id
需要先创建一个存储当前表 id 的自增值,比如表名叫:id-inc
创建表后,立即新增一条数据,值如下,下面的值不要改,就这样创建。
{
"_id": "001"
}
2
3
然后假设你现在需要实现自增 id 的表名为 my-orders,则在执行 vk.baseDao.add 前,你需要先执行 vk.baseDao.updateAndReturn,代码如下
let newInfo = await vk.baseDao.updateAndReturn({
dbName: 'id-inc',
whereJson: {
_id: '001',
},
dataJson: {
'my-orders': _.inc(1),
},
});
let newId = newInfo['my-orders'];
await vk.baseDao.add({
dbName: 'my-orders',
dataJson: {
id: newId, // 注意:这里最好用id,不要用_id,这样_id依然是数据库默认的,然后多了一个叫id的自增id字段,这样兼容性最高
// ...其他字段的值
// a: 1,
// b: "2"
},
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 注意:文档中出现的 $ 在云函数若不可用,则可写成 _.$
以下是 _ 和 $ 变量实际代表的含义
var db = uniCloud.database(); // 全局数据库引用
var _ = db.command; // 数据库操作符
var $ = _.aggregate; // 聚合查询操作符
2
3