Spring Data Mongondb在使用Aggregation时碰到的问题
一、在使用Spring Data Mongondb进行Aggregation操作的时候会碰到根据某个集合的ObjectId分组的情况。
exam_result的集合结构:
例如以下的sql需要根据_id分组:
db.exam_result.aggregate([
{$unwind:{ path: "$students" }},
{$unwind:{ path: "$students.answers" }},
{$group:{
_id:{uniq:"$_id"},
countRight: { $sum: "$students.answers.isRight.key"},
count:{$sum:1}
}},
{$project:{
"_id":1,
countRight: 1,
count : 1
}}
]);
查询结果:
/* 1 */
{
"_id" : {
"uniq" : ObjectId("5b5002ccb6e10c0371d30249")
},
"countRight" : 1,
"count" : 5.0
}
/* 2 */
{
"_id" : {
"uniq" : ObjectId("5b4ffaa7b6e10c0371d3022b")
},
"countRight" : 3,
"count" : 15.0
}
实现以上sql的java代码:
TypedAggregation<ExamResult> aggregation = Aggregation.newAggregation(
ExamResult.class,
Aggregation.match(cr),
Aggregation.unwind("students"),
Aggregation.unwind("students.answers"),
Aggregation.group("id")
.sum("students.answers.isRight.key").as("totalRight")
.sum("quNum").as("totalQuNum"),
Aggregation.project()
.and("id").as("examObjectId")
.and("totalRight").as("totalRight")
.and("totalQuNum").as("totalQuNum")
.andExpression("totalRight/totalQuNum").as("classZqRateTest")
);
可以看出在根据ObjectId进行分组的时候java代码中Aggregation.group("id")是没有Aggregation.group("_id")的,它存在的唯一ObjectId即id,所以分组时使用Aggregation.group("id")即可。
二、在根据内嵌文档的某个字段进行分组时,要先进行unwind(即拆分)
$unwind的作用是将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
官方文档地址:https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/
例如我们需要根据学生的stuId分组,在上面的表结构中,我们可以看到students是一个内嵌的数组类型,所以在使用前我们需要先进行拆分。即:Aggregation.unwind("students")
例如以下sql需要根据学生stuId分组:
db.exam_result.aggregate([
{$unwind:{ path: "$students" }},
{$unwind:{ path: "$students.answers" }},
{$group:{
_id:{stuId:"$students.stuId"},
countRight: { $sum: "$students.answers.isRight.key"},
count:{$sum:1}
}},
{"$lookup": {
"localField": "_id.stuId",
"from": "student",
"foreignField": "_id",
"as": "userinfo"
}},
{"$unwind": "$userinfo"},
{$project:{
"_id.stuId":1,
count:1,
countRight:1,
"userinfo.stuName": 1
}}
]);
java实现代码:
aggregation = Aggregation.newAggregation(
ExamResult.class,
Aggregation.match(cr),
Aggregation.unwind("students"),
Aggregation.unwind("students.answers"),
Aggregation.group("students.stuId")
.count().as("quNumCount").sum("students.answers.isRight.key").as("countRight"),
Aggregation.lookup("student", "_id", "_id", "studentList"),
Aggregation.unwind("studentList"),
Aggregation.project()
.and("_id").as("stuId")
.and("quNumCount").as("quNumCount")
.and("countRight").as("countRight")
.and("studentList.stuName").as("stuName")
.and("studentList.classId").as("classId")
.andExpression("countRight/quNumCount").as("zqRate"),
Aggregation.sort(sort)
);
三、在使用$group和$lookup配合进行查询的时候,$lookup的localFile的取值问题(将exam_result集合和student集合关联)
exam_result集合结构:
student集合结构:
正确的关联sql:
db.exam_result.aggregate([
{$unwind:{ path: "$students" }},
{$unwind:{ path: "$students.answers" }},
{$group:{
_id:{stuId:"$students.stuId"},
countRight: { $sum: "$students.answers.isRight.key"},
count:{$sum:1}
}},
{"$lookup": {
"localField": "_id.stuId",
"from": "student",
"foreignField": "_id",
"as": "userinfo"
}},
{"$unwind": "$userinfo"},
{$project:{
"_id.stuId":1,
count:1,
countRight:1,
"userinfo.stuName": 1
}}
]);
错误的关联sql:
db.exam_result.aggregate([
{$unwind:{ path: "$students" }},
{$unwind:{ path: "$students.answers" }},
{$group:{
_id:{stuId:"$students.stuId"},
countRight: { $sum: "$students.answers.isRight.key"},
count:{$sum:1}
}},
{"$lookup": {
"localField": "students.stuId",
"from": "student",
"foreignField": "_id",
"as": "userinfo"
}},
{"$unwind": "$userinfo"},
{$project:{
"_id.stuId":1,
count:1,
countRight:1,
"userinfo.stuName": 1
}}
]);
"localField": "students.stuId":在_id:{stuId:"$students.stuId"},分组之后stuId的值已经属于_id这个对象中的一个ObjectId对象,所以通过student.stuId是获取不到的stuId的值。
$lookup参数解释(本案例中exam_result为主表,student从表,可能集合的表述更为贴切):
localFile:主表中的需要关联的键(即exam_result表中的students.stuId)。
from:需要关联的从表(即student表)
foreignField:从表中需要关联的键(即student表中的_id)
as:对应的外键集合的数据