欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Data Mongondb在使用Aggregation时碰到的问题

程序员文章站 2022-04-18 10:21:56
...

一、在使用Spring Data Mongondb进行Aggregation操作的时候会碰到根据某个集合的ObjectId分组的情况。

exam_result的集合结构:

Spring Data Mongondb在使用Aggregation时碰到的问题

例如以下的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集合结构:

Spring Data Mongondb在使用Aggregation时碰到的问题

student集合结构:

Spring Data Mongondb在使用Aggregation时碰到的问题

正确的关联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:对应的外键集合的数据