【问题标题】:Multiple use of the positional `$` operator to update nested arrays多次使用位置 `$` 运算符来更新嵌套数组
【发布时间】:2013-01-29 02:22:42
【问题描述】:

这个问题与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. 如果可以在涉及“嵌套度”大于 1 的数组/文档结构的更新场景中多次使用 MongoDB 的定位 $ 运算符 (details):

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

    可以将$ once 用于顶级数组,而不是“仅”使用“更高级别”的数组的显式索引:

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

  2. 如果可能的话,相应的 R 语法会是什么样子。

您将在下面找到一个可重现的示例。我尽量简洁。


代码示例

数据库连接

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 的子文档元素中):

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 (paths.$.queries) 的所需子文档元素。

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

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

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

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

我猜想翻译成“plain MongoDB”语法,查询选择器看起来有点像这样

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

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

> !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 语法。

【问题讨论】:

    标签: r mongodb rmongodb nosql


    【解决方案1】:

    位置运算符只支持一层深度,并且只支持第一个匹配元素。

    这里有一个 JIRA 可跟踪您想要的那种行为:https://jira.mongodb.org/browse/SERVER-831

    我不确定它是否允许不止一场比赛,但我相信这将是由于它需要如何工作的动态。

    【讨论】:

    • 太好了,谢谢指点!!所以我想这是一个更好的设计选择,然后在更高程度的嵌套发挥作用时对数组采取相当简单的做法,而不是依靠文档来实现这样的场景,对吧?这也是该问题的最佳答案所建议的:stackoverflow.com/questions/4121666/…
    • @Rappster 没错,请记住,大多数操作的数组处理也在内存中,即 $pull、$push 等等。因此,不要对它们发疯通常是一个不错的设计选择
    • 该功能已于2017年实现jira.mongodb.org/browse/SERVER-831
    【解决方案2】:

    如果您可以从 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);
                }
            }
        }
    });
    

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

    【讨论】:

    • 上面的操作是原子的吗?
    • 嗨@MaulikSoneji,该操作不是原子的,因为它由两个不同的操作组成——读和写。 MongoDB 的行为方式取决于引擎和您正在运行的版本。看这里docs.mongodb.com/manual/tutorial/iterate-a-cursor/…我希望这个信息有帮助
    猜你喜欢
    • 1970-01-01
    • 2017-01-15
    • 2016-07-12
    • 1970-01-01
    • 2019-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多