MongoDB:更新数组中项目的字段,并匹配该项目的另一个字段

时间:2015-10-16 12:56:18

标签: mongodb mongodb-query

我有这样的数据结构:
我们有一些centerscenter有一些switchesswitch有一些ports

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

我想要的只是编写一个更新查询,将其Status为5的端口的PortNumber更改为"清空"。
当我用这个查询知道端口的数组索引(这里数组索引是1)时,我可以更新它:

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

但我不知道该端口的数组索引 谢谢你的帮助。

2 个答案:

答案 0 :(得分:5)

您通常会使用位置运算符$执行此操作,如此问题的答案中所述:

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"
        }
    }
)

答案 1 :(得分:1)

由于您不知道端口的数组索引,我建议您动态创建 delete old records 条件,即可以帮助您获取索引的内容对于对象,然后相应地进行修改,然后考虑使用 MapReduce

目前,使用聚合框架似乎无法做到这一点。有一个未解决的开放 JIRA issue 与之相关联。但是, MapReduce 可以解决方法。 MapReduce的基本思想是它使用JavaScript作为其查询语言,但这往往比聚合框架慢得多,不应该用于实时数据分析。

在MapReduce操作中,您需要定义几个步骤,即映射步骤(将操作映射到集合中的每个文档,操作可以不执行任何操作或使用键和投影值发出一些对象)和减少步骤(采用发射值列表并将其减少为单个元素)。

对于地图步骤,理想情况下,您希望获取集合中的每个文档,每个$setSwitches数组字段的索引以及包含Ports个键的另一个键

您的简化步骤将是一个函数(无效),简单定义为$set

MapReduce操作的最后一步将创建一个单独的集合开关,其中包含发出的Switches数组对象以及具有var reduce = function() {};条件的字段。在原始集合上运行MapReduce操作时,可以定期更新此集合。 总而言之,这个MapReduce方法看起来像:

$set

从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()