2015-10-16 61 views
0

我有這樣的數據結構:
我們有一些centers。 A center有一些switches。 A switch有一些ports
MongoDB:使用該項目的其他字段更新數組中項目的字段

{ 
    "_id" : ObjectId("561ad881755a021904c00fb5"), 
    "Name" : "center1", 
    "Switches" : [ 
     { 
      "Ports" : [ 
       { 
        "PortNumber" : 2, 
        "Status" : "Empty" 
       }, 
       { 
        "PortNumber" : 5, 
        "Status" : "Used" 
       }, 
       { 
        "PortNumber" : 7, 
        "Status" : "Used" 
       } 
      ] 
     } 
    ] 
    } 

所有我想要的是寫一個更新查詢來改變它的PortNumber是5到「空」的端口Status
我可以更新它,當我與此查詢知道該端口的數組索引(這裏數組索引1):

db.colection.update(
    // query 
    { 
     _id: ObjectId("561ad881755a021904c00fb5") 
    }, 
    // update 
    { 
     $set : { "Switches.0.Ports.1.Status" : "Empty" } 
    } 
); 

但我不知道該端口的數組索引。
感謝您的幫助。

回答

2

你通常會做此使用了該位置操作$,在回答說明瞭這個問題:

Update field in exact element array in MongoDB

不幸的是,現在的位置操作僅支持深匹配的一個陣列級別。

有一個JIRA票,你想要的那種行爲:https://jira.mongodb.org/browse/SERVER-831

如果你可以讓Switches成一個對象,而不是,你可以做這樣的事情:

db.colection.update(
    { 
     _id: ObjectId("561ad881755a021904c00fb5"), 
     "Switch.Ports.PortNumber": 5 
    }, 
    { 
     $set: { 
      "Switch.Ports.$.Status": "Empty" 
     } 
    } 
) 
+0

感謝您的回答。看來我必須將我的數據結構扁平化爲多個集合。 – Aliaaa

1

既然你不知道端口的數組索引,我建議你動態地創建條件,即可以幫助您獲得對象的索引,然後進行相應的修改,然後考慮使用MapReduce

目前這似乎是不可能的使用聚合框架。有一個未解決的開放JIRA issue鏈接到它。但是,解決方法可能與MapReduce。 MapReduce的基本思想是使用JavaScript作爲查詢語言,但這往往比聚合框架慢,不應該用於實時數據分析。

在您的MapReduce操作中,您需要定義幾個步驟,即映射步驟(將操作映射到集合中的每個文檔,操作可以不執行任何操作,也可以使用鍵和投影值發出某個對象)和減少步驟(它將發出的值列表並將其減少爲單個元素)。

對於地圖的步驟,你最好希望得到的集合中的每個文件,該指數爲每個SwitchesPorts陣列領域,包含$set鍵的另一個關鍵。

你減少步驟是簡單地定義爲var reduce = function() {};

在精簡操作的最後一步,然後將創建一個包含發射開關陣列對象與現場沿單獨收集切換的功能(這什麼都不做) $set條件。當您對原始集合運行MapReduce操作時,可以定期更新此集合。 總之,這MapReduce的方法看起來像:

var map = function(){ 
    for(var i = 0; i < this.Switches.length; i++){ 
     for(var j = 0; j < this.Switches[i].Ports.length; j++){ 
      emit( 
       { 
        "_id": this._id, 
        "switch_index": i, 
        "port_index": j 
       }, 
       { 
        "index": j, 
        "Switches": this.Switches[i], 
        "Port": this.Switches[i].Ports[j],      
        "update": { 
         "PortNumber": "Switches." + i.toString() + ".Ports." + j.toString() + ".PortNumber", 
         "Status": "Switches." + i.toString() + ".Ports." + j.toString() + ".Status" 
        }      
       } 
      ); 
     }    
    } 
}; 

var reduce = function(){}; 

db.centers.mapReduce(
    map, 
    reduce, 
    { 
     "out": { 
      "replace": "switches" 
     } 
    } 
); 

從精簡操作查詢輸出收集Switches通常會給你的結果:

db.switches.findOne() 

樣本輸出

{ 
    "_id" : { 
     "_id" : ObjectId("561ad881755a021904c00fb5"), 
     "switch_index" : 0, 
     "port_index" : 1 
    }, 
    "value" : { 
     "index" : 1, 
     "Switches" : { 
      "Ports" : [ 
       { 
        "PortNumber" : 2, 
        "Status" : "Empty" 
       }, 
       { 
        "PortNumber" : 5, 
        "Status" : "Used" 
       }, 
       { 
        "PortNumber" : 7, 
        "Status" : "Used" 
       } 
      ] 
     }, 
     "Port" : { 
      "PortNumber" : 5, 
      "Status" : "Used" 
     }, 
     "update" : { 
      "PortNumber" : "Switches.0.Ports.1.PortNumber", 
      "Status" : "Switches.0.Ports.1.Status" 
     } 
    } 
} 

然後,您可以使用db.switches.find()方法迭代並相應地更新您的集合:

var newStatus = "Empty"; 
var cur = db.switches.find({ "value.Port.PortNumber": 5 });  

// Iterate through results and update using the update query object set dynamically by using the array-index syntax. 
while (cur.hasNext()) { 
    var doc = cur.next(); 
    var update = { "$set": {} }; 
    // set the update query object 
    update["$set"][doc.value.update.Status] = newStatus; 

    db.centers.update(
     { 
      "_id": doc._id._id, 
      "Switches.Ports.PortNumber": 5 
     }, 
     update 
    );  
}; 
+1

感謝您的好解釋。我認爲在我的項目情況下,對我來說最好的解決方案是平滑數據結構。但是您的解決方案對於某些情況也很完整。 – Aliaaa

+0

@Aliaaa別擔心。是的,我同意扁平你的數據在解決大部分複雜的深度嵌套的數組更新方面會有很長的路要走,MongoDB正努力用原子操作來處理。 – chridam

相關問題