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

Posted

技术标签:

【中文标题】Mongoose — 使用聚合创建具有 $sum 的新属性【英文标题】:Mongoose — Create new property with $sum using aggregation 【发布时间】:2016-04-04 06:16:59 【问题描述】:

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

这是 Schema(为简洁起见,删除了不相关的字段):

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

我建立模型:

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

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

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

当我启动我的服务器时,我没有收到任何错误;但是,totalVotes 字段尚未创建。我使用this 文档来帮助我。它同样使用$sum 运算符,我完全按照文档说明的方式进行操作,但没有结果。

【问题讨论】:

***.com/questions/5370846/… @Vishnu 我已经定义了架构? 无法获取模型中未定义的字段。这就是问题所在。 @Vishnu 聚合框架的结果不受模型模式的限制,可以是任何东西。所以这不是问题,而是你不能在$project 内应用$sum,也不能直接在数组上应用。 【参考方案1】:

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) 
        );
    )
);

【讨论】:

我认为这不起作用,因为我使用的是 Mongoose【参考方案2】:

不幸的是,这并没有像您想象的那样起作用,因为“投票”实际上是一个值数组,其次是因为$sum 是accumulator operator,仅用于$group 管道阶段。

所以为了让你得到数组的总和作为另一个属性,首先你必须$unwind数组然后$group一起在文档键上$sum元素的总和:

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

    
);

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

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

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

    
);

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

【讨论】:

【参考方案3】:

您的架构中没有 totalVotes。试试下面的代码。

var PollSchema = new Schema(
  
    votes: [Number],
    totalVotes: Number
  
 );

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

resultData.toJSON();

【讨论】:

我试了一下,文档中显示了字段,但是值只是一个空数组 @Matthew 试试更新的代码。 totalVotes 应该是一个数字,而不是一个数组:P。【参考方案4】:

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

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);
            
        
    );

);

【讨论】:

以上是关于Mongoose — 使用聚合创建具有 $sum 的新属性的主要内容,如果未能解决你的问题,请参考以下文章

mongoDB,带有 $sum 和 $count 的 mongoose 聚合查询

mongoDB,带有 $sum 和 $count 的 mongoose 聚合查询

具有 .every() 等效项的 Mongoose 聚合

mongoose 聚合和求和嵌套的 OBJECT (NOT ARRAY ) 字段

在 mongoose 聚合中修改 $cond 的结果值?

MongoDB 聚合与 Mongoose 虚拟