MongoDB:在具有2个嵌套数组的文档中更新平均值

6

我有以下MongoDB文档:

{
    _id: ObjectId(),
    company_name: "Name",
    registered: 2/21/2015 2:00,
    trucks: [
        {
            truck_id: "TEB7622",
            weight: 88.33,
            capacity: 273.333,
            length: 378.333,
            width: 377.383,
            average_grade: 2.5,
            grades: [
                {
                    grade_number: 4,
                    timestamp: 2/21/2015 2:00
                }
            ]
        },
        {
            truck_id: "TEB5572",
            weight: 854.33,
            capacity: 2735.333,
            length: 378.333,
            width: 37.383,
            average_grade: 3.8,
            grades: [
                {
                    grade_number: 4,
                    timestamp: 2/21/2015 2:00
                }
            ]
        }
    ]

}

我希望能够通过添加所有的grade_numbers来更新每辆卡车的average_grade。但我遇到的问题是,我尝试添加的grade_numbers在一个数组中,并且这个数组又在另一个数组中。我已尝试使用$unwind解开卡车和等级数组。以下是我尝试使用的查询:
db.col.aggregate([ 
   {$unwind: "$trucks"}, 
   {$unwind: "$trucks.grades"}, 
   { $project: { 
      "_id": "$trucks.truck_id", 
      "trucks.average_grade": { $avg: { $sum: "trucks.grades.grade_number"} } 
      } 
   }])

我需要在查询中添加更多内容吗?我想要更新整个文档中的trucks.average_grades,因为文档中有很多这样的记录。


你不能使用聚合操作来更新一个文档。 - styvane
@Michael9 那么我需要运行两个查询,对吗?一个用于获取平均值,另一个用于更新平均值?我需要将平均值存储在另一个“变量”中吗? - suecarmol
如果您需要查询其中的数据,双重嵌套数组并不是一个好主意。如果数据仅用于显示,则可以使用它们。也许您应该重新考虑数据模型?也许每辆卡车都应该是自己的文档,并带有一个“company_id”字段?或者每个等级都有自己的文档?除非查询不常见,否则您不想依赖双重展开聚合管道进行正常查询,因此我不建议使用以下解决方案。 - wdberkeley
@wdberkeley 这不会让它变得有点像关系型数据库吗?我对MongoDB还很陌生,所以这是我第一次建模文档数据库。我只是会使用这个查询几次(实际计算将在Hadoop中进行)。 - suecarmol
1个回答

6
您不能使用 aggregation 来更新文档,但是您可以使用它来获取要用于更新的数据。首先,我注意到在 grades 数组中,您的 grade 对象周围缺少一些 {}。您可能需要仔细检查您的文档结构是否与发布的结构相同。其次,您的聚合查询存在几个问题。
  1. $avg 运算符只能在 $group 子句中使用,而不能在 $project 中使用。
  2. 当您使用 $avg 时,不需要使用 $sum
  3. 您想要对 trucks.grades.grade.grade_number 取平均值,实际上它包含了等级的数值。也就是说,在 gradesgrade_number 之间缺少了 grade
如果您解决了这些问题,您将得到类似以下的查询:
db.col.aggregate([ 
    { "$unwind": "$trucks" }, 
    { "$unwind": "$trucks.grades" }, 
    { "$group":
        { 
            "_id": "$trucks.truck_id", 
            "average_grade": { "$avg": "$trucks.grades.grade_number" } 
        } 
    }
]);

对于您的样本文档,它返回:

{ "_id" : "TEB5572", "average_grade" : 4 }
{ "_id" : "TEB7622", "average_grade" : 4 }

现在,您可以使用这些信息来更新average_grade字段。 如果您使用的是MongoDB版本2.6或更高版本,则aggregate方法将返回一个游标。 您可以迭代该游标并相应地更新文档。
在此示例中,我搜索具有特定truck_id的文档,这些文档位于其trucks数组内,并继续使用聚合查询计算出的平均分数更新average_grade。 您可以修改它以适应您的需求。 结合聚合查询,代码如下。
// Get average grade for each truck and assign results to cursor.
var cur = db.col.aggregate([ 
    { "$unwind": "$trucks" }, 
    { "$unwind": "$trucks.grades" }, 
    { "$group":
        { 
            "_id": "$trucks.truck_id", 
            "average_grade": { "$avg": "$trucks.grades.grade_number" } 
        } 
    }
]);

// Iterate through results and update average grade for each truck.
while (cur.hasNext()) {
    var doc = cur.next();
    db.col.update({ "trucks.truck_id": doc._id },
                  { "$set": { "trucks.$.average_grade": doc.average_grade }},
                  { "multi": true});
}

是的,就是这样。非常感谢。 - suecarmol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接