2017-01-06 42 views
1

目前我有这个模式。数据库记录的Mongoose中的子文档总数

var cartSchema = new Schema({ 
    userPurchased: {type: Schema.Types.ObjectId, ref: 'users'}, 
    products: [ 
     { 
      product: {type: Schema.Types.ObjectId, ref: 'products'}, 
      size: {type: String, required: true}, 
      quantity: {type: Number, required: true}, 
      subTotal: {type: Number, required: true} 
     } 
    ], 
    totalPrice: {type: Number, default: 0, required: true} 
}); 

{ 
    "_id": { 
     "$oid": "586f4be94d3e481378469a08" 
    }, 
    "userPurchased": { 
     "$oid": "586dca1f5f4a7810fc890f97" 
    }, 
    "totalPrice": 0, 
    "products": [ 
     { 
      "product": { 
       "$oid": "58466e8e734d1d2b0ceeae00" 
      }, 
      "size": "m", 
      "quantity": 5, 
      "subTotal": 1995, 
      "_id": { 
       "$oid": "586f4be94d3e481378469a09" 
      } 
     }, 
     { 
      "subTotal": 1197, 
      "quantity": 3, 
      "size": "m", 
      "product": { 
       "$oid": "58466e8e734d1d2b0ceeae01" 
      }, 
      "_id": { 
       "$oid": "586f4ef64d3e481378469a0a" 
      } 
     } 
    ], 
    "__v": 0 
} 

有什么办法来总结所有的小计,并把它的总价场?现在我正在考虑集合函数,但我怀疑这是否是正确的方法。我想我需要一个更新查询和求和方法在同一时间。任何人都可以在这里帮助我吗?

+0

虚拟财产如何加总产品的所有价格?我不认为有必要存储总价格。 – Camo

+0

我认为聚合函数或map reduce函数是最好的解决方案。 MongoDB有一个格栅聚合和地图重新定义框架。看看这个例子https://docs.mongodb.com/manual/core/map-reduce/ –

+0

@Camo你能给我一个例子吗?我认为这是一个好主意。 –

回答

1

使用aggregate()功能,您可以运行下面的管道,以获得期望的结果:

var pipeline = [ 
    { "$unwind": "$products" }, 
    { 
     "$group": { 
      "_id": "$_id", 
      "products": { "$push": "$products" }, 
      "userPurchased": { "$first": "$userPurchased" }, 
      "totalPrice": { "$sum": "$products.subTotal" } 
     } 
    } 
] 

Cart.aggregate(pipeline) 
    .exec(function(err, results){ 
     if (err) throw err; 
     console.log(JSON.stringify(results, null, 4)); 
    }) 

在上面的管道,第一步是$unwind操作

{ "$unwind": "$products" } 

它来相当方便时数据以数组形式存储。当展开运算符应用于列表数据字段时,它将为应用展开的列表数据字段的每个元素生成一条新记录。它基本上使数据变平。

这是下一个流水线阶段必要的操作,该$group一步,在此组扁平文件由_id领域,从而有效地重组了去归一化文档回到原来的模式。

$group管道运算符类似于SQL的GROUP BY子句。在SQL中,除非使用任何聚合函数,否则不能使用GROUP BY。同样的,你也必须在MongoDB中使用一个聚合函数(称为累加器)。你可以阅读更多关于accumulators here

在这种$group操作中,逻辑计算totalPrice并返回原来的字段是通过accumulators

"totalPrice": { "$sum": "$products.subTotal } 

其他表达

"userPurchased": { "$first": "$userPurchased" }, 

会从第一个文档返回userPurchased值使用各组:您可以通过每组每个个体subTotal值总结了$sum为获得totalPrice$first。因此,有效地重建原始文档模式之前,需要注意的一件事是执行管道时,MongoDB将管理操作符管理到彼此中。这里的“管道”采用Linux的含义:操作员的输出成为后面的操作员的输入。每个操作员的结果都是一个新的文档集合。所以蒙戈执行上述的管道如下:

collection | $unwind | $group => result 

作为一个侧面说明,以帮助了解管道或调试它,你应该得到意想不到的效果,只有第一管道运营商运行的聚集。例如,在运行蒙戈聚集壳牌:

db.cart.aggregate([ 
    { "$unwind": "$products" } 
]) 

检查结果,看是否products阵列正确解构。如果这给出了预期的结果,则添加下一个:

db.cart.aggregate([ 
    { "$unwind": "$products" }, 
    { 
     "$group": { 
      "_id": "$_id", 
      "products": { "$push": "$products" }, 
      "userPurchased": { "$first": "$userPurchased" }, 
      "totalPrice": { "$sum": "$products.subTotal" } 
     } 
    } 
]) 

重复这些步骤直到您进入最后的管道步骤。


如果要更新字段,那么你可以添加$out流水线阶段的最后一步。这会将汇总管道的结果文档写入相同的集合,从而在技术上更新集合。

var pipeline = [ 
    { "$unwind": "$products" }, 
    { 
     "$group": { 
      "_id": "$_id", 
      "products": { "$push": "$products" }, 
      "userPurchased": { "$first": "$userPurchased" }, 
      "totalPrice": { "$sum": "$products.subTotal" } 
     } 
    }, 
    { "$out": "cart" } // write the results to the same underlying mongo collection 
] 

UPDATE

两者都做了更新和查询,然后你可以发出在总量回调find()调用来获取更新的JSON即

Cart.aggregate(pipeline) 
    .exec(function(err, results){ 
     if (err) throw err; 
     Cart.find().exec(function(err, docs){ 
      if (err) return handleError(err); 
      console.log(JSON.stringify(docs, null, 4)); 
     }) 
    }) 

使用承诺,你可以这样做,或者作为

Cart.aggregate(pipeline).exec().then(function(res) 
    return Cart.find().exec(); 
).then(function(docs){ 
    console.log(JSON.stringify(docs, null, 4)); 
}); 
+0

谢谢@chridam一个很棒的解释。我在终端上试过这个,但totalPrice的输出为0.无法找出原因。 –

+0

让我们调试管道以找出原因。当你使用'$ unwind'作为db.cart运行管道时。汇总([ {“$ unwind”:“$ products”} ])',你会得到预期的结果吗? “产品”子文档中是否有一些字段没有“subTotal”的数字值?可能是'subTotal'是一个字符串? – chridam

+0

展开的结果是所需的。按ID预先返回两个objectId。然后我跳过产品线和用户购买线,因为我有点困惑。我包括了totalPrice,然后它也如预期那样回报。当我早些时候尝试了所有东西时,totalPrice返回0.我认为它是在产品和用户购买冲突的线上。我应该先计算totalPrice然后再重新架构模式吗?你认为有效果吗? –

0

我真的不能说,这种做法是否比聚合更好,但如果你想用虚函数来做到这一点:

cartSchema.virtual('totalPrice').get(function() { 
    return this.products.map(p => p.subTotal).reduce((a, b) => a + b); 
}); 

但护理:

如果使用toJSON()或toObject()(或在猫鼬文档上使用JSON.stringify())mongoose默认不包含虚拟。传递{虚函数:真正}要么toObject()或的toJSON()

0

您可以用新的$reduce聚合计算相同的使用当前蒙戈3.4版本。

db.cart.aggregate(
    [{ 
     $project: { 
      "_id": 1, 
      "products": 1, 
      "userPurchased": 1, 
      "totalPrice": { 
       $reduce: { 
        input: "$products", 
        initialValue: 0, 
        in: { 
         $add: ["$$value", "$$this.subTotal"] 
        } 
       } 
      } 
     } 
    }, { 
     "$out": "cart" 
    }] 
); 
+0

我正在使用此回调。如果在管道底部包含$ out操作符,则只返回空数组,但是当我删除$ out操作符时,它将返回整个json对象。我需要$ out操作符来更新文档和json对象,我该怎么做? –

相关问题