多次使用位置`$`运算符来更新嵌套数组

时间:2013-02-13 13:58:33

标签: r mongodb rmongodb nosql

这个问题与this one密切相关,我会考虑在NoSQL环境中给出关于模式设计的建议,但我很想理解这个:

实际问题

假设您有以下文件:

    _id : 2      abcd
    name : 2     unittest.com
    paths : 4    
        0 : 3    
            path : 2     home
            queries : 4      
                0 : 3    
                    name : 2     query1
                    url : 2      www.unittest.com/home?query1
                    requests: 4

                1 : 3    
                    name : 2     query2
                    url : 2      www.unittest.com/home?query2
                    requests: 4

基本上,我想知道

  1. 如果可以多次使用MongoDB的位置$运算符(details),或者在涉及具有“嵌套度”的数组/文档结构的更新方案中使用不同的方式大于1:

    { <update operator>: { "paths.$.queries.$.requests" : value } }不起作用

    而不是“仅”能够对顶级数组使用$ 一次并绑定在“更高级别”上使用数组的显式索引:

    { <update operator>: { "paths.$.queries.0.requests" : value } })(工作

  2. 如果可能的话,相应的R语法将如何显示。

  3. 下面你会找到一个可重复的例子。我试着尽量简洁。


    代码示例

    数据库连接

    require("rmongodb")
    db  <- "__unittest" 
    ns  <- paste(db, "hosts", sep=".")
    # CONNCETION OBJECT
    con <- mongo.create(db=db)
    # ENSURE EMPTY DB
    mongo.remove(mongo=con, ns=ns)
    

    示例文件

    q <- list("_id"="abcd")
    b <- list("_id"="abcd", name="unittest.com")
    mongo.insert(mongo=con, ns=ns, b=b)
    q <- list("_id"="abcd")
    b <- list("$push"=list(paths=list(path="home")))
    mongo.update(mongo=con, ns, criteria=q, objNew=b)
    q <- list("_id"="abcd", paths.path="home")
    b <- list("$push"=list("paths.$.queries"=list(
        name="query1", url="www.unittest.com/home?query1")))
    mongo.update(mongo=con, ns, criteria=q, objNew=b)
    b <- list("$push"=list("paths.$.queries"=list(
        name="query2", url="www.unittest.com/home?query2")))
    mongo.update(mongo=con, ns, criteria=q, objNew=b)
    

    使用显式位置索引(工作)更新嵌套数组

    这可行,但它涉及第二级数组queries显式索引(嵌套在数组paths的subdoc元素中):

    q <- list("_id"="abcd", paths.path="home", paths.queries.name="query1")
    b <- list("$push"=list("paths.$.queries.0.requests"=list(time="2013-02-13")))
    > mongo.bson.from.list(b)
        $push : 3    
            paths.$.queries.0.requests : 3   
                time : 2     2013-02-13
    
    mongo.update(mongo=con, ns, criteria=q, objNew=b)
    res <- mongo.find.one(mongo=con, ns=ns, query=q)
    > res
        _id : 2      abcd
        name : 2     unittest.com
        paths : 4    
            0 : 3    
                path : 2     home
                queries : 4      
                    0 : 3    
                        name : 2     query1
                        requests : 4     
                            0 : 3    
                                time : 2     2013-02-13
    
    
                        url : 2      www.unittest.com/home?query1
    
                    1 : 3    
                        name : 2     query2
                        url : 2      www.unittest.com/home?query2
    

    更新具有位置$索引的嵌套数组(不起作用)

    现在,我想将显式0替换为位置$运算符,就像我一样,以便让服务器找到所需的数组paths的subdoc元素( paths.$.queries)。

    AFAIU documentation,这应该起作用,因为关键是指定一个“正确”的查询选择器:

      
        

    位置$运算符,当与update()方法一起使用时,充当更新查询选择器的第一个匹配项的占位符:

      

    我认为我指定了一个查询选择器,确实找到了正确的嵌套元素(由于paths.queries.name="query1"部分):

    q <- list("_id"="abcd", paths.path="home", paths.queries.name="query1")
    

    我想翻译成“普通MongoDB”语法,查询选择器看起来有点像

    { _id: abcd, paths.path: home, paths.queries.name: query1 }
    

    对我来说似乎是一个有效的查询选择器。实际上它确实匹配了所需的元素/ doc:

    > !is.null(mongo.find.one(mongo=con, ns=ns, query=q))
    [1] TRUE
    

    我的想法是,如果它在顶级工作,为什么它不适用于更高级别(只要查询选择器指向正确的嵌套组件)?

    但是,服务器似乎不喜欢嵌套或多次使用$

    b <- list("$push"=list("paths.$.queries.$.requests"=list(time="2013-02-14")))
    > mongo.bson.from.list(b)
        $push : 3    
            paths.$.queries.$.requests : 3   
                time : 2     2013-02-14
    
    > mongo.update(mongo=con, ns, criteria=q, objNew=b)
    [1] FALSE
    

    我不确定它是否不起作用,因为MongoDB不支持此功能,或者我没有正确使用R语法。

2 个答案:

答案 0 :(得分:18)

位置运算符仅支持一级深度,仅支持第一个匹配元素。

JIRA可以跟踪您想要的行为:https://jira.mongodb.org/browse/SERVER-831

我不确定它是否会允许多场比赛,但我相信这将是由于它将如何运作的动态。

答案 1 :(得分:5)

如果您可以从MongoDB shell执行查询,您可以利用MongoDB游标的 forEach 函数(http://docs.mongodb.org/manual/reference/method/cursor.forEach/)来绕过此限制

以下是3个嵌套数组的示例:

var collectionNameCursor = db.collection_name.find({...});

collectionNameCursor.forEach(function(collectionDocument) {
    var firstArray = collectionDocument.firstArray;
    for(var i = 0; i < firstArray.length; i++) {
        var secondArray = firstArray[i].secondArray;
        for(var j = 0; j < secondArray.length; j++) {
            var thirdArray = secondArray[j].thirdArray;
            for(var k = 0; k < thirdArray.length; k++) {
                //... do some logic here with thirdArray's elements
                db.collection_name.save(collectionDocument);
            }
        }
    }
});

请注意,这更像是一次性解决方案,而不是生产代码,但如果您必须编写修复脚本,它将会完成这项工作。