2017-01-06 40 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對象,我該怎麼做? –

相關問題