2015-10-06 55 views
0

給出一個包含3個嵌套文檔以下文檔...的MongoDB:如何通過1獲得最低值越接近給定數量和遞減另一字段

{ "_id": ObjectId("56116d8e4a0000c9006b57ac"), "name": "Stock 1", "items" [ 
    { "price": 1.50, "description": "Item 1", "count": 10 } 
    { "price": 1.70, "description": "Item 2", "count": 13 } 
    { "price": 1.10, "description": "Item 3", "count": 20 } 
    ] 
} 

...我需要選擇子用最低的價格記錄接近給定的量(下面在這裏我假設1.05):

db.stocks.aggregate([ 
    {$unwind: "$items"}, 
    {$sort: {"items.price":1}}, 
    {$match: {"items.price": {$gte: 1.05}}}, 
    {$group: { 
    _id:0, 
    item: {$first:"$items"} 
    }}, 
    {$project: { 
    _id: "$item._id", 
    price: "$item.price", 
    description: "$item.description" 
    }} 
]); 

可正常工作,這裏是結果:

"result" : [ 
    { 
     "price" : 1.10, 
     "description" : "Item 3", 
     "count" : 20 
    } 
], 
"ok" : 1 

除了回用最低的價格的項目更接近給定數量,我需要1。例如遞減count,這裏是下面我要找的結果:

"result" : [ 
    { 
     "price" : 1.10, 
     "description" : "Item 3", 
     "count" : 19 
    } 
], 
"ok" : 1 

回答

2

這取決於無論您是想要「更新」結果還是簡單地將結果「返回」爲遞減值。在前一種情況下,您當然需要返回文檔並「返回」返回結果的值。

另外要注意的是,你「認爲」在這裏效率並不高。對元素進行「過濾」,甚至是「後退」,對於累加器在性能方面的工作原理沒有任何影響。

更好的方法是在可能的情況下基本上「預先過濾」數組中的值。這降低了聚合管線的文件的大小,並通過$unwind被處理的陣列元素的數量:

db.stocks.aggregate([ 
    { "$match": { 
    "items.price": { "$gte": 1.05 } 
    }}, 
    { "$project": { 
     "items": { 
     "$setDifference": [ 
      { "$map": { 
       "input": "$items", 
       "as": "item", 
       "in": { 
        "$cond": [ 
         { "$gte": [ "$$item.price", 1.05 ] } 
        ], 
        "$$item", 
        false 
       } 
      }}, 
      [false] 
     ] 
     } 
    }}, 
    { "$unwind": "$items"}, 
    { "$sort": { "items.price":1 } }, 
    { "$group": { 
    "_id": 0, 
    "item": { "$first": "$items" } 
    }}, 
    { "$project": { 
    "_id": "$item._id", 
    "price": "$item.price", 
    "description": "$item.description" 
    }} 
]); 

當然,這確實需要的MongoDB版本2.6或更高服務器有可用的運營商,和去由你的輸出你可能有一個更早的版本。如果是這樣的話,那麼至少應該放棄$match,因爲它沒有做任何有價值的事情,並且會對性能產生不利影響。

如果$match有用,在做任何事情之前都在文檔選擇中,因爲您總是想要避免的是處理甚至不可能滿足您在陣列或其他任何地方所需條件的文檔。所以你應該總是$match或者先使用類似的查詢階段。

無論如何,如果你想要的是一個「預期結果」就用$subtract輸出:

{ "$project": { 
    "_id": "$item._id", 
    "price": "$item.price", 
    "description": "$item.description", 
    "count": { "$subtract": [ "$item.count", 1 ] } 
    }} 

但是,如果要「更新」的結果,那麼你會遍歷陣列(它仍然是一個數組甚至一個結果)通過$inc更新匹配項目和「減量」伯爵:

var result = db.stocks.aggregate([ 
    { "$match": { 
    "items.price": { "$gte": 1.05 } 
    }}, 
    { "$project": { 
     "items": { 
     "$setDifference": [ 
      { "$map": { 
       "input": "$items", 
       "as": "item", 
       "in": { 
        "$cond": [ 
         { "$gte": [ "$$item.price", 1.05 ] } 
        ], 
        "$$item", 
        false 
       } 
      }}, 
      [false] 
     ] 
     } 
    }}, 
    { "$unwind": "$items"}, 
    { "$sort": { "items.price":1 } }, 
    { "$group": { 
    "_id": 0, 
    "item": { "$first": "$items" } 
    }}, 
    { "$project": { 
    "_id": "$item._id", 
    "price": "$item.price", 
    "description": "$item.description" 
    }} 
]); 

result.forEach(function(item) { 
    db.stocks.update({ "item._id": item._id},{ "$inc": { "item.$.count": -1 }}) 
}) 

而且對MongoDB的2。4外殼,你同樣聚集查詢適用(但請的變化)但是result包含一個名爲result裏面與陣列的另一個領域,所以加了水平:因此,無論

result.result.forEach(function(item) { 
    db.stocks.update({ "item._id": item._id},{ "$inc": { "item.$.count": -1 }}) 
}) 

只是$project僅用於顯示,或根據需要使用返回的結果對數據生成.update()

相關問題