猫鼬 — 使用聚合创建具有$sum的新属性

Mongoose — Create new property with $sum using aggregation

本文关键字:新属性 sum 属性 创建 猫鼬      更新时间:2023-09-26

我正在尝试在所有包含数字数组总和的文档中添加一个新字段。

以下是架构(为简洁起见,删除了不相关的字段(:

 var PollSchema = new Schema(
  {
    votes: [Number]
  }
 );

我建立模型:

PollModel = mongoose.model('Poll', PollSchema);

我使用聚合创建一个包含 votes 数组总和的新字段。

PollModel.aggregate([
 {
  $project: {
    totalVotes: { $sum: "$votes"}
  }
 }
]);

启动服务器时,没有收到任何错误;但是,尚未创建totalVotes字段。 我使用这个文档来帮助我。 它同样使用 $sum 运算符,我完全按照文档说明的那样做了,但没有结果。

MongoDb 聚合不会将其结果保存到数据库中。您只需在回调中获取内联聚合的结果。因此,在聚合后,您需要对数据库进行多次更新:

PollModel.aggregate([
{
    $project: { totalVotes: { $sum: "$votes"} }
}]).exec( function(err, docs){
        // bulk is used for updating all records within a single query
        var bulk = PollModel.collection.initializeUnorderedBulkOp();
        // add all update operations to bulk
        docs.forEach(function(doc){
            bulk.find({_id: doc._id}).update({$set: {totalVotes: doc.totalVotes}});
        });
        // execute all bulk operations
        bulk.execute(function(err) {
        });
    })
});
不幸的是,

这并不像您想象的那样工作,因为"votes"实际上是一个值数组,其次是因为$sum是一个累加器运算符,仅用于$group管道阶段。

因此,为了获得数组的总数作为另一个属性,首先您必须$unwind数组,然后在文档键上一起$group$sum元素的总数:

PostModel.aggregate(
    [
        { "$unwind": "$votes" },
        { "$group": {
            "_id": "$_id",
            "anotherField": { "$first": "$anotherField" },
            "totalVotes": { "$sum": "$votes" }
        }}
    ],
    function(err,results) {
    }
);

这里还要注意的是,对于您希望在结果中作为$group的每个附加字段,$first中的另一个累加器都是必需的,$project只返回您要求的字段。

通常,出于性能原因,最好将其保留为每个文档中的属性,因为它比使用聚合更快。因此,要做到这一点,只需在每次$push数组时增加一个总数,同时使用 $inc

PostModel.update(
    { "_id": id },
    {
        "$push": { "votes": 5 },
        "$inc": { "totalVotes": 5 }
    },
    function(err,numAffected) {
    }
);

通过这种方式,"totalVotes"字段始终可以使用,而无需解构数组并对每个文档的值求和。

您的架构中没有totalVotes。只需尝试以下代码。

var PollSchema = new Schema(
  {
    votes: [Number],
    totalVotes: Number
  }
 );
PollModel.aggregate([
 {
  $project: {
    totalVotes: { $sum: "$votes"}
  }
 }
]);

resultData.toJSON();

@Blakes Seven 和 @Volodymyr Synytskyi 帮助我找到了解决方案! 我还发现这个文档特别有用。

PollModel.aggregate(
[
    { '$unwind': '$votes' },
    { '$group': {
        '_id': '$_id',
        'totalVotes': { '$sum': '$votes' }
    }}
],
function(err,results) {
    // console.log(results);
    results.forEach(function(result){
        var conditions = { _id: result._id }, 
            update = { totalVotes: result.totalVotes }, 
            options = { multi: true };
        PollModel.update(conditions, update, options, callback);
        function callback (err, numAffected) {
            if(err) {
                console.error(err);
                return;
            } else {
                // console.log(numAffected);
            }
        }
    });
}
);