在前面的文章中,我们介绍了如何用“库存”看懂云开发数据库事务,**讲述了在云开发数据库重构中如何将字段抽离成单独的集合;**今天我们来学习云开发联表数据的查询,并教大家如何在云函数中应用,在微信开发者工具中打印出我们查询的结果。
先来设定一下场景,现在有两个表格,我们来查询一下徐老师所带的班级里面所有学生的平均成绩:
先看一下如何查询,将这两个表连起来的数据是 class 表中的 id 和 student 表中的 class_id.
所以我们应该先查出徐老师所在班级的 id,是 2,然后再查询 student 表中 class_id 为 2 的学生,张二和李二,计算这两个学生的平均成绩。
来看一下在云开发中如何实现这样一个联表查询。云开发文档中,在开发指引--数据库中,就有联表查询的介绍,我们使用 lookup 函数实现联表查询:
lookup({
from: <要连接的集合名>,
localField: <输入记录的要进行相等匹配的字段>,
foreignField: <被连接集合的要进行相等匹配的字段>,
as: <输出的数组字段名>
})
应用到我们上面设定的场景,就像下面这样写,需要调用 end 方法来标志结束定义:
lookup({
from: "student", //要关联的表student
localField: "id", //class表中的关联字段
foreignField: "class_id", //student表中关联字段
as: "stu", //定义输出数组的别名
}).end();
这个语句会查出来下面的结果,会查出班级的信息以及该班级所对应的所有学生的信息:
{"list":
[{
"id":1,
"teacher":"王老师",
"cname":"一班",
"stu":[
{
"sname":"宁一",
"class_id":1,
"score":90
}
]
},
{
"id":2,
"teacher":"徐老师",
"cname":"二班",
"stu":[
{
"class_id":2,
"sname":"张二",
"score":100
},
{
"class_id":2,
"sname":"李二",
"score":80
}
]
}]
}
但是我们只需要徐老师所在班级学生的数据,所以需要加一个 where 条件,在 lookup 后面不能直接跟 where,而是 match 来代替,下面我们来改进上面的代码。
.lookup({
from: 'student',
localField: 'id',
foreignField: 'class_id',
as: 'stu'
})
.match({
teacher:"徐老师"
})
.end()
现在就只是返回徐老师所在班级的学生数据了,学生数据在 stu 对应的数组里面:
{"list":
[
{"_id":"5e847ab25eb9428600a512352fa6c7c4",
"id":2,
"teacher":"徐老师",
"cname":"二班",
//学生数据
"stu":[
{"_id":"37e26adb5eb945a70084351e57f6d717",
"class_id":2,
"sname":"张二",
"score":100
},
{"_id":"5e847ab25eb945cf00a5884204297ed8",
"class_id":2,
"sname":"李二",
"score":80
}
]
}
]
}
接下来我们继续优化代码,直接返回学生的平均分数。
如果想要在被连接的表格中(本课程中的 student)做聚合操作,就用pipeline方法。
但是pipeline不能与localField、foreignField共用,所以我们先删掉localField、foreignField再在pipeline中取得学生成绩(score)的平均值:
.lookup({
from: 'student',
pipeline: $.pipeline()
.group({
_id: null,
score: $.avg('$score')
})
.done(),
as: 'stu'
})
.match({
teacher:"徐老师"
})
.end()
现在打印的数据是这样的:
{"list":
[
{"_id":"5e847ab25eb9428600a512352fa6c7c4",
"id":2,
"teacher":"徐老师",
"cname":"二班",
"stu":[
{"_id":null,
"score":90
}
]
}
]
}
但是现在输出的数据有点复杂,如果只想显示teacher和score这两个值,我们再进行下面的操作。
.lookup({
from: 'student',
pipeline: $.pipeline()
.group({
_id: null,
score: $.avg('$score')
})
.done(),
as: 'stu'
})
.match({
teacher:"徐老师"
})
.replaceRoot({
newRoot: $.mergeObjects([$.arrayElemAt(['$stu', 0]), '$$ROOT'])
})
.project({
_id:0,
teacher:1,
score:1
})
.end()
现在打印出来的数据是这样的:
{"list":
[
{"score":90,"teacher":"徐老师"}
]
}
replaceRoot({ newRoot: <表达式> })
是固定写法,将已有字段作为一个新节点输出,我们通常用他来将二级数组变成一级数组。
mergeObjects
是累计器操作符,$.arrayElemAt(['$stu', 0]), '$$ROOT’]
就是将 stu 数组中的第一个元素,也就是[{"_id":null,"score":90}]
合并到数组的跟节点上面,也就是与 teacher、cname 这些字段同一级。
project
里面将_id 后面设为 0,将我们想要显示的元素后面设为 1,就能控制最后输出的字段。
接下来看看怎样在云函数中运用吧,在微信开发者工具中打印出我们上面查询的结果。
我们在微信开发者工具中打开云开发控制台,先在云数据库中创建这两个表,我们以创建 class 表为例。
创建好表格后,我们再在表格中添加记录,根据我们上面表格中的数据来添加,下面添加的是二班的数据。
数据都添加好了之后,来到微信开发者工具云函数文件夹下面创建一个名为 test 的云函数云开发的这个项目我们是提前创建好了,如果不知道怎样创建的,可以看之前我发的 30 分钟创建创建并上线云开发小程序的课程,里面有教大家如何创建。
创建完成后,系统会帮咱们创建一个 test 文件夹,我们打开 test/index.js 文件,将部分默认创建的代码删掉,并初始化数据库,像下面这样:
// 云函数入口文件
const cloud = require("wx-server-sdk");
cloud.init();
//初始化数据库
const db = cloud.database();
const _ = db.command;
const $ = _.aggregate;
// 云函数入口函数
exports.main = async (event, context) => {
//下面继续在这里面添加代码
};
// 云函数入口函数
exports.main = async (event, context) => {
return await db
.collection("class")
.aggregate()
.lookup({
from: "student",
pipeline: $.pipeline()
.group({
_id: null,
score: $.avg("$score"),
})
.done(),
as: "stu",
})
.match({
teacher: "徐老师",
})
.replaceRoot({
newRoot: $.mergeObjects([$.arrayElemAt(["$stu", 0]), "$$ROOT"]),
})
.project({
_id: 0,
teacher: 1,
score: 1,
})
.end();
};
编辑完成之后保存文件,并上传部署云函数。
右键点击云函数,选择上传并部署:云端安装依赖(不上传 node_modules)
打开云开发控制台—点击云函数--找到 test 云函数点击云端测试:
在弹出的测试框中,直接点击运行测试按钮
下面就会打印出返回的结果,说明现在已经联表查询成功了!