【问题标题】:Updating a Nested array in Mongo在 Mongo 中更新嵌套数组
【发布时间】:2015-04-20 04:53:38
【问题描述】:

我在更新嵌套数组时遇到很多问题,我创建了一个非常简单的测试,但它似乎不起作用,有没有人遇到过这个:

数据:

 [ { "A": { "B": [ { "C": [ 1, 2, 3 ] }, { "C": [ 1, 2, 3 ] } ] } }, { "A": { "B": [ { "C": [ 1, 3 ] }, { "C": [ 1, 3 ] } ] } } ]

发现:

db.arrayQuery.find({"A.B.C": { $in: [1] }})

然后更新:

db.arrayQuery.update({"A.B.C": { $in: [1] }},{$pull : { "A.B.C" : 1}},{multi: true})

我收到了cannot use the part (B of A.B.C) to traverse the element 我在这里阅读了一些问题,建议我只使用{$pull : { "C" : 1}},我不再收到错误消息,但没有任何反应。

【问题讨论】:

    标签: mongodb mongodb-query


    【解决方案1】:

    Mongo $elemMatch用于本例,查询如下

    db.arrayQuery.update({"A.B":{"$elemMatch":{"C":{"$in":[1]}}}},{"$pull":{"A.B.$.C":{"$in":[1]}}},false,true)
    

    【讨论】:

    • 感谢您的快速回答,但是这只会删除第一个元素,而不是全部,我添加了 multi:true 但没有这样做。另外,你添加的真假标志是什么意思?
    • 第一个false 表示upsert 和第二个true 表示multi true 所以这种情况下所有C 包含1 全部删除,因为最后一个true
    • @GMelo 检查此docs.mongodb.org/manual/reference/operator/update/pull mongo pull 运算符从现有数组中删除与指定查询匹配的一个或多个值的所有实例
    • 感谢您的帮助,您是否针对这两条记录运行了此代码并且对您有用?因为对我来说它只从第一个集合中删除了一个元素而不是第二个,所以我的输出是: {"A": {"B":[{"C":[2,3],"C":[1, 2,3]}] }} {"A": {"B":[{"C":[3],"C":[1,3]}] }} 它只从第一个 C 中删除元素在每个 B. 谢谢!!
    • @GMelo 实际上你给定的数据是错误的,现在我正确编辑了它,并且根据我的代码它只删除单个匹配的文档,我将尝试解决多个数组值。
    【解决方案2】:

    MongoDB 不支持匹配多个级别的数组,因为位置运算符只支持一个级别的深度并且只支持第一个匹配元素。为此有一张 JIRA 票。有关其他类似问题,请参阅 Multiple use of the positional $ operator to update nested arrays。

    这里有几个选项;考虑通过展平结构来修改您的架构,以便每个文档仅代表一个数组级别,即:

    A : {
        B: {
            C: [...]
        }
    }
    

    另一个选项是使用 MapReduce 操作生成一个集合,该集合包含的文档具有每个对应的 arrayQuery 文档的数组索引位置值的属性。 MapReduce 的基本思想是它使用 JavaScript 作为其查询语言,但这往往比聚合框架慢得多,不应用于实时数据分析。

    在您的 MapReduce 操作中,您需要定义几个步骤,即映射步骤(将每个 arrayQuery B 数组映射到集合中的每个文档,并且该操作可以不做任何事情或发出带有键和投影值)和归约步骤(获取发射值列表并将其归约为单个元素)。

    对于映射步骤,理想情况下,您希望获取集合中每个文档、每个 B 数组字段的索引和另一个包含 $pull 键的键。

    您的 reduce 步骤将是一个简单定义为 var reduce = function() {}; 的函数(什么都不做)

    MapReduce 操作的最后一步将创建一个名为arrayUpdates 的单独集合,其中包含发出的操作数组对象以及具有$pull 条件的字段。当您对原始集合运行 MapReduce 操作时,可以定期更新此集合。 总而言之,这个 MapReduce 方法看起来像:

    var map = function(){
        for(var i = 0; i < this.A.B.length; i++){
            emit( 
                {
                    "_id": this._id, 
                    "index": i 
                }, 
                {
                    "index": i, 
                    A: {
                       B: this.A.B[i]
                    },            
                    "update": {
                        "value": "A.B." + i.toString() + ".C" // this projects the $pull query with the dynamic array element positions
                    }                    
                }
            );
        }
    };
    
    var reduce = function(){};
    
    db.arrayQuery.mapReduce(
        map,
        reduce,
        {
            "out": {
                "replace": "arrayUpdates"
            }
        }
    );
    

    从 MapReduce 操作查询输出集合 operations 通常会给您结果:

    db.arrayUpdates.findOne()
    

    输出:

    /* 1 */
    {
        "_id" : {
            "_id" : ObjectId("5534da99180e849972938fe8"),
            "index" : 0
        },
        "value" : {
            "index" : 0,
            "A" : {
                "B" : {
                    "C" : [ 
                        1, 
                        2, 
                        3
                    ]
                }
            },
            "update" : {
                "value" : "A.B.0.C"
            }
        }
    }
    

    然后您可以使用 db.arrayUpdates.find() 方法中的光标来迭代并相应地更新您的集合:

    var cur = db.arrayUpdates.find({"value.A.B.C": 1 });
    
    // 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 = { "$pull": {} };
        // set the update query object
        update["$pull"][doc.value.update.value] = 1;
    
        db.arrayQuery.update({ "A.B.C": 1 }, update );
    };
    

    这会为每个带有查询的文档提取 C 数组中的值 1

    db.arrayQuery.update({ "A.B.C": 1 }, update ); 
    

    例如,第一个匹配文档的对象update可以是{ $pull: {"A.B.1.C": 1} }

    因此,运行上述代码后,db.arrayQuery.find() 查询的最终结果将是:

    /* 0 */
    {
        "_id" : ObjectId("5534da99180e849972938fe8"),
        "A" : {
            "B" : [ 
                {
                    "C" : [ 
                        2, 
                        3
                    ]
                }, 
                {
                    "C" : [ 
                        2, 
                        3
                    ]
                }
            ]
        }
    }
    
    /* 1 */
    {
        "_id" : ObjectId("5534da99180e849972938fe9"),
        "A" : {
            "B" : [ 
                {
                    "C" : [ 
                        3
                    ]
                }, 
                {
                    "C" : [ 
                        3
                    ]
                }
            ]
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2017-01-05
      • 2015-09-01
      • 1970-01-01
      • 2017-01-05
      • 1970-01-01
      • 2022-01-24
      • 2021-07-20
      • 2016-02-08
      相关资源
      最近更新 更多