# 数据库操作常见问题
# 目录
# 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
18
# 跨字段的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
5
# 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
10
# 数组第一个字段是开始时间-第二个字段是结束时间-判断当前时间是否在范围内
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
9
# 返回字段别名
如数据库中是_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
11
12
13
# 查出表中字段a等于字段b的数据
let selectRes = await vk.baseDao.select({
dbName: "表名",
pageIndex: 1,
pageSize: 20,
whereJson: _.expr($.and([
$.eq(['$a', '$b'])
]))
});
2
3
4
5
6
7
8
9
# 如何查询数组字段内包含某个值的数据
直接 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
9
# 分组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
16
17
# 用户表按注册日期分组统计
以下语句是统计本年度每月注册用户的数量
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
# 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
# 注意:文档中出现的 $ 在云函数若不可用,则可写成 _.$
以下是 _ 和 $ 变量实际代表的含义
var db = uniCloud.database(); // 全局数据库引用
var _ = db.command; // 数据库操作符
var $ = _.aggregate; // 聚合查询操作符
2
3