更新阵列MongoDB的内部嵌套数组 [英] Updating nested array inside array mongodb

查看:191
本文介绍了更新阵列MongoDB的内部嵌套数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下MongoDB的文档结构:

  [
    {
        _id:04,
        名:测试服4,
        ID:04,
        版本:0.0.1
        头衔:测试,
        说明:测试,
        协议:测试,
        行动:
            {
                _id:99,
                ONAME:试运52222222222
                SID:04,
                名:考运52222222222
                OID:99,
                说明:测试,
                返回类型:测试,
                参数:
                    {
                        ONAME:参数1,
                        名:参数1
                        PID:011,
                        类型:582,
                        说明:测试,
                        值:
                    },
                    {
                        ONAME:参数2,
                        名:参数2
                        PID:012
                        类型:58222,
                        说明:测试,
                        值:
                    }
                ]
            }
        ]
    }
]

我已经能够以更新的经营领域使用$ elemMatch,但是当我试图为参数做同样的事情(修改),它似乎并没有工作。我不知道我应该是什么其他的办法进入,以试图能够在一个特定的参数成功更新领域,其PID寻找它。

更新code我现在有不工作看起来是这样的:

  VAR OID = req.params.operations;
VAR PID = req.params.parameters;collection.update({参数:{$ elemMatch:{PID:PID}}},{$集:{参数$名字。。req.body.name,参数$ .DESCRIPTION:req.body.description,参数$ ONAME。:req.body.oName,参数$类型:。req.body.type}},功能(错了,结果){
        如果(ERR){
            的console.log('错误更新服务:+ ERR);
            res.send({'错误':'错误发生'});
        }其他{
            //执行console.log(''+ +结果文件(S)更新');
            res.send(结果);
        }
    });


解决方案

由于@wdberkeley在他的评论中提及:


  

的MongoDB不支持匹配成阵列的多个级别。
  考虑改变您的文档模型,以便每个文档重新presents的
  操作,具有公用的信息复制的一组操作
  在操作的文件。


我同意上面的,并建议重新设计架构的MongoDB的引擎不支持多个位置操作符(请参阅 <一个href=\"http://stackoverflow.com/questions/14855246/multiple-use-of-the-positional-operator-to-update-nested-arrays\">Multiple使用位置 $ 运营商来更新嵌套数组

但是,如果你知道的操作阵列具有参数对象必须预先更新,那么更新查询将是指数:

  db.collection.update(
    {
        _id:04,
        operations.parameters.pid:011
    },
    {
        $设置:{
            。operations.0.parameters $名:富,
            。operations.0.parameters $说明:酒吧,
            。operations.0.parameters $类型:富
        }
    }

编辑:

如果您想创建 $设置 的飞行,即一些条件,这将有助于你得到的索引的对象,然后相应修改,那么可以考虑使用 马preduce

目前,这似乎使用聚合框架是不可能的。有一个悬而未决的开放 JIRA问题 链接到它。然而,一个解决办法是可能的 马preduce 。与马preduce的基本思路是,它使用JavaScript作为它的查询语言,但是这往往比聚合框架相当慢,而不应被用于实时数据分析。

在你的马preduce操作,你需要定义几个步骤即映射步骤(其中一个操作映射到集合中的所有文档,而操作可以什么也不做,发出连键的一些对象,投影值)和还原工序(这需要发射的值的列表和它减少到一个单一的元素)。

有关地图一步,你最好希望能为集合中的每个文件,每个操作指数阵列领域,包含<$ C另一个关键$ C> $集键。

您减少的步骤将是一个函数(什么都不做)简单地定义为 VAR减少=功能(){};

在马preduce操作的最后一步将然后创建一个包含发射操作数组对象与在 $设置条件的区域以及一个单独的集合操作。这个系列可以定期,当你运行原始集合马preduce操作进行更新。
总之,这马preduce方法是这样的:

  VAR地图=功能(){
    对于(VAR I = 0; I&LT; this.operations.length;我++){
        发射(
            {
                _id:this._id,
                指数:我
            },
            {
                指数:我,
                行动:this.operations [I]
                更新:{
                    名:操作。 + i.toString()+.parameters。$,名,
                    说明:操作。 + i.toString()+.parameters。$描述
                    类型:操作。 + i.toString()+.parameters。$。键入
                }
            }
        );
    }
};VAR减少=功能(){};db.collection.ma preduce(
    地图,
    减少,
    {
        走出去:{
            替换:操作
        }
    }
);

查询输出集合操作从马preduce操作将通常给你造成的:

  db.operations.findOne()

输出

  {
    _ID : {
        _id:03,
        指数:0
    },
    值:{
        指数:0,
        行动:{
            _id:96,
            ONAME:试运52222222222
            SID:04,
            名:考运52222222222
            OID:99,
            说明:测试,
            返回类型:测试,
            参数:
                {
                    ONAME:参数1,
                    名:富,
                    PID:011,
                    类型:富,
                    说明:酒吧,
                    值:
                },
                {
                    ONAME:参数2,
                    名:参数2
                    PID:012
                    类型:58222,
                    说明:测试,
                    值:
                }
            ]
        },
        更新:{
            名:。operations.0.parameters $名,
            说明:。operations.0.parameters $描述,
            类型:。operations.0.parameters $型
        }
    }
}

您可以再使用光标从 db.operations.find()方法循环,​​并相应地更新您的收藏:

  VAR OID = req.params.operations;
VAR PID = req.params.parameters;
VAR CUR = db.operations.find({_ id._id:OID,value.operations.parameters.pid:PID});//通过使用使用数组索引语法动态设置更新查询对象结果和更新迭代。
而(cur.hasNext()){
    变种文档= cur.next();
    VAR更新= {$集:{}};
    //设置更新查询对象
    更新[$设置] [doc.value.update.name] = req.body.name;
    更新[$设置] [doc.value.update.description] = req.body.description;
    更新[$设置] [doc.value.update.type] = req.body.type;    db.collection.update(
        {
            _id:OID,
            operations.parameters.pid:PID
        },
        更新
    );
};

I have the following mongodb document structure:

[
    {
        "_id": "04",
        "name": "test service 4",
        "id": "04",
        "version": "0.0.1",
        "title": "testing",
        "description": "test",
        "protocol": "test",
        "operations": [
            {
                "_id": "99",
                "oName": "test op 52222222222",
                "sid": "04",
                "name": "test op 52222222222",
                "oid": "99",
                "description": "testing",
                "returntype": "test",
                "parameters": [
                    {
                        "oName": "Param1",
                        "name": "Param1",
                        "pid": "011",
                        "type": "582",
                        "description": "testing",
                        "value": ""
                    },
                    {
                        "oName": "Param2",
                        "name": "Param2",
                        "pid": "012",
                        "type": "58222",
                        "description": "testing",
                        "value": ""
                    }
                ]
            }
        ]
    }
]

I have been able to use $elemMatch in order to update fields in operations, but when I try to do the same thing (modified) for parameters it does not seem to work. I was wondering what other approach should I look into trying in order to be able to successfully update fields in a specific parameter, looking it up by its pid.

The update code I currently have and does not work looks like this:

var oid = req.params.operations;
var pid = req.params.parameters;

collection.update({"parameters":{"$elemMatch": {"pid": pid}}},{"$set": {"parameters.$.name":req.body.name, "parameters.$.description": req.body.description,"parameters.$.oName": req.body.oName,"parameters.$.type": req.body.type} }, function(err, result) {
        if (err) {
            console.log('Error updating service: ' + err);
            res.send({'error':'An error has occurred'});
        } else {
            // console.log('' + result + ' document(s) updated');
            res.send(result);
        }
    });

解决方案

As @wdberkeley mentioned in his comment:

MongoDB doesn't support matching into more than one level of an array. Consider altering your document model so each document represents an operation, with information common to a set of operations duplicated in the operation documents.

I concur with the above and would recommend redesigning your schema as MongoDB engine does not support multiple positional operators ( See Multiple use of the positional $ operator to update nested arrays)

However, if you know the index of the operations array that has the parameters object to be updated beforehand then the update query will be:

db.collection.update(
    {
        "_id" : "04", 
        "operations.parameters.pid": "011"
    }, 
    {
        "$set": { 
            "operations.0.parameters.$.name": "foo",
            "operations.0.parameters.$.description": "bar", 
            "operations.0.parameters.$.type": "foo" 
        }
    }
)

EDIT:

If you would like to create the $set conditions on the fly i.e. something which would help you get the indexes for the objects and then modify accordingly, then consider using MapReduce.

Currently this seems to be not possible using the aggregation framework. There is an unresolved open JIRA issue linked to it. However, a workaround is possible with MapReduce. The basic idea with MapReduce is that it uses JavaScript as its query language but this tends to be fairly slower than the aggregation framework and should not be used for real-time data analysis.

In your MapReduce operation, you need to define a couple of steps i.e. the mapping step (which maps an operation into every document in the collection, and the operation can either do nothing or emit some object with keys and projected values) and reducing step (which takes the list of emitted values and reduces it to a single element).

For the map step, you ideally would want to get for every document in the collection, the index for each operations array field and another key that contains the $set keys.

Your reduce step would be a function (which does nothing) simply defined as var reduce = function() {};

The final step in your MapReduce operation will then create a separate collection operations that contains the emitted operations array object along with a field with the $set conditions. This collection can be updated periodically when you run the MapReduce operation on the original collection. Altogether, this MapReduce method would look like:

var map = function(){
    for(var i = 0; i < this.operations.length; i++){
        emit( 
            {
                "_id": this._id, 
                "index": i 
            }, 
            {
                "index": i, 
                "operations": this.operations[i],            
                "update": {
                    "name": "operations." + i.toString() + ".parameters.$.name",
                    "description": "operations." + i.toString() + ".parameters.$.description",
                    "type": "operations." + i.toString() + ".parameters.$.type"
                }                    
            }
        );
    }
};

var reduce = function(){};

db.collection.mapReduce(
    map,
    reduce,
    {
        "out": {
            "replace": "operations"
        }
    }
);

Querying the output collection operations from the MapReduce operation will typically give you the result:

db.operations.findOne()

Output:

{
    "_id" : {
        "_id" : "03",
        "index" : 0
    },
    "value" : {
        "index" : 0,
        "operations" : {
            "_id" : "96",
            "oName" : "test op 52222222222",
            "sid" : "04",
            "name" : "test op 52222222222",
            "oid" : "99",
            "description" : "testing",
            "returntype" : "test",
            "parameters" : [ 
                {
                    "oName" : "Param1",
                    "name" : "foo",
                    "pid" : "011",
                    "type" : "foo",
                    "description" : "bar",
                    "value" : ""
                }, 
                {
                    "oName" : "Param2",
                    "name" : "Param2",
                    "pid" : "012",
                    "type" : "58222",
                    "description" : "testing",
                    "value" : ""
                }
            ]
        },
        "update" : {
            "name" : "operations.0.parameters.$.name",
            "description" : "operations.0.parameters.$.description",
            "type" : "operations.0.parameters.$.type"
        }
    }
}

You can then use the cursor from the db.operations.find() method to iterate over and update your collection accordingly:

var oid = req.params.operations;
var pid = req.params.parameters;
var cur = db.operations.find({"_id._id": oid, "value.operations.parameters.pid": pid });

// 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.name] = req.body.name;
    update["$set"][doc.value.update.description] = req.body.description;
    update["$set"][doc.value.update.type] = req.body.type;

    db.collection.update(
        {
            "_id" : oid, 
            "operations.parameters.pid": pid
        }, 
        update 
    );
};

这篇关于更新阵列MongoDB的内部嵌套数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆