2015-04-21 28 views
0

我有這樣一個在收集x在MongoDB的文件:上述變換鍵映射到載體

{ 
    "_id" : ... 
    "attrKeys": [ "A1", "A2" ], 
    "attrs" : { 
     "A1" : { 
      "type" : "T1", 
      "value" : "13" 
     }, 
     "A2" : { 
      "type" : "T2", 
      "value" : "14" 
     } 
    } 
} 

A1A2元件都只是例子:attrs字段可以保持任何數量的任何鍵名稱。 attrs中的密鑰名稱存儲在attrNames字段中。

我想使用MongoDB的聚合框架到文檔轉換成一個這樣的:

{ 
    "_id" : ... 
    "attrs" : [ 
     { 
      "key": "A1", 
      "type" : "T1", 
      "value" : "13" 
     }, 
     { 
      "key": "A2", 
      "type" : "T2", 
      "value" : "14" 
     } 
    ] 
} 

即,成爲attrs到一個數組,其元素是相同的,所述密鑰值「通過「鍵入名稱爲key的每個數組元素中的新字段。

可以使用聚合框架進行吸吮轉換嗎?我傾向於認爲$project運營商可以使用,但我還沒有想出如何。

+1

具有未知鍵是MongoDB中一種危險的反模式。 – Philipp

+0

有點偏離主題,但我試圖澄清:)其實,關鍵是'attrs',它是完全已知的。另一個問題是我需要在'attrs'內使用密鑰來處理屬性的併發更新,例如'{$ set:{attrs.A1:{...}}}''和'{$ set:{attrs.A2:{...}}}'這可能非常難以管理將屬性存儲爲數組。 – fgalan

+0

實際上,鍵並不完全未知,它們存儲在'attrKeys'字段中。我編輯了這個問題來包含這些信息(我沒有在我的原始文章中列入,認爲'attrKeys'不是有意解決這個問題,對此很抱歉)。 – fgalan

回答

1

由於@Philipp在他的評論

正確提及的是具有未知鍵是MongoDB中一個危險的反模式

不過,如果你事先知道密鑰,那麼你可以使用什麼彙總運營商$literal,$addToSet$setUnion以獲得期望的結果。聚合管道會像:

db.collection.aggregate([ 
    { 
     "$project": { 

      "attrs.A1.key": { "$literal": "A1" }, 
      "attrs.A1.type": "$attrs.A1.type", 
      "attrs.A1.value": "$attrs.A1.value", 
      "attrs.A2.key": { "$literal": "A2" }, 
      "attrs.A2.type": "$attrs.A2.type", 
      "attrs.A2.value": "$attrs.A2.value" 
     } 
    }, 
    { 
     "$group": { 
      "_id": "$_id", 
      "A1": { "$addToSet": "$attrs.A1" }, 
      "A2": { "$addToSet": "$attrs.A2" } 
     } 
    }, 
    { 
     "$project": { 
      "attrs": { 
       "$setUnion": [ "$A1", "$A2" ] 
      } 
     } 
    } 
]) 

結果

/* 0 */ 
{ 
    "result" : [ 
     { 
      "_id" : ObjectId("55361320180e849972938fea"), 
      "attrs" : [ 
       { 
        "type" : "T1", 
        "value" : "13", 
        "key" : "A1" 
       }, 
       { 
        "type" : "T2", 
        "value" : "14", 
        "key" : "A2" 
       } 
      ] 
     } 
    ], 
    "ok" : 1 
} 
+0

密鑰在'attrKeys'字段中預先知道(我已經更新了我的問題文章以包含它,對不起在我原來的文章中沒有做)。 – fgalan

+1

它們不是事先知道的 - 它們是在您觸摸文檔時確定的,並且可以看到「attrKeys」。 – wdberkeley

1

的聚合框架是不是你怎麼會在這裏處理的轉變。您可能一直在尋找$out運算符在重寫集合時有一些幫助,但聚合框架無法完成您所要求的操作。

基本上,聚合框架缺乏通過任何方式使用「數據點」來動態訪問「密鑰」的方法。你可以像使用mapReduce一樣處理數據,但它通常不如使用聚合框架那麼高效,主要原因你首先來到這裏,因爲有人指出修改後的結構更好。

此外,試圖用MapReduce的,以此來「重新塑造」您的收藏存儲通常不是一個好主意。 MapReduce輸出基本上是「始終」「鍵/值」,這意味着你得到的輸出總是包含在一個強制的「值」字段中。

這實際上意味着改變集合的內容,唯一的方式,您可以真正做到這一點,而使用目前您的文檔中的值是「讀書」的文件內容,然後在「寫」回來。

這樣做的循環性質,最好使用"Bulk"操作API方法

db.collection.intializeOrderedBukOp(), VAR散裝= db.collection處理。intializeOrderedBukOp(), count = 0;

db.collection.find({ "attrKeys": { "$exists": true }}).forEach(function(doc) { 
    // Re-map attrs 
    var attrs = doc.attrKeys.map(function(key) { 
     return { 
      "key": key, 
      "type": doc.attrs[key].type, 
      "value": parseInt(doc.attrs[key].value) 
     }; 
    }); 

    // Queue update operation 
    bulk.find({ "_id": doc._id, "attrKeys": { "$exists": true } }) 
     .updateOne({ 
      "$set": { "attrs": attrs }, 
      "$unset": { "attrKeys": 1 } 
     }); 
    count++; 

    // Execute every 1000 
    if (count % 1000 == 0) { 
     bulk.execute(); 
     bulk = db.collection.intializeOrderedBukOp(); 
    } 
}); 

// Drain any queued remaining 
if (count % 1000 != 0) 
    bulk.execute(); 

一旦你更新的集合內容(請注意,你的「價值」的領域還不斷有從「字符串」更改爲「整數」格式),那麼你可以在你的新的結構做有益的集聚操作,例如:

db.collection.aggregate([ 
    { "$unwind": "$attrs" }, 
    { "$group": { 
     "_id": null, 
     "avgValue": { "$avg": "$attrs.value" } 
    }} 
])