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

Rap*_*ter 20 r mongodb nosql rmongodb

这个问题是密切相关的这一个,我会考虑在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
Run Code Online (Sandbox Code Playgroud)

基本上,我想知道

  1. 如果可以在涉及"嵌套度"大于1的数组/文档结构的更新方案中多次使用MongoDB的位置$运算符(详细信息),或者换句话说不同:

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

    而不是"只"能够使用$ 一次顶级数组并绑定为"更高级别"上的数组使用显式索引:

    { <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)
Run Code Online (Sandbox Code Playgroud)

示例文档

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)
Run Code Online (Sandbox Code Playgroud)

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

这有效,但它涉及第二级数组的显式索引queries(嵌套在数组的subdoc元素中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
Run Code Online (Sandbox Code Playgroud)

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

现在,我想0用位置$运算符替换显式,就像我做的那样,让服务器找到array paths(paths.$.queries)所需的subdoc元素.

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

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

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

q <- list("_id"="abcd", paths.path="home", paths.queries.name="query1")
Run Code Online (Sandbox Code Playgroud)

我想翻译成"普通的MongoDB"语法,查询选择器看起来有点像这样

{ _id: abcd, paths.path: home, paths.queries.name: query1 }
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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
Run Code Online (Sandbox Code Playgroud)

我不确定它是否不起作用,因为MongoDB不支持这个或者我没有得到R语法.

Sam*_*aye 19

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

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

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


Iva*_*tov 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);
            }
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

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


小智 5

正如 @FooBar 在已接受答案的评论中提到的,该功能于 2017 年通过 MongoDB 3.6 实现。

为此,您必须将位置过滤器arrayFilters条件结合使用。
应用于您的示例:

updateOne(
  { "paths.home": "home" },
  { $push : { 
      "paths.$.queries.$[q].requests": { time: "2022-11-15" } 
    }
  },
  { arrayFilters: [{ "q.name": "name" }] }
)
Run Code Online (Sandbox Code Playgroud)

位置运算符$指的是过滤器{ "paths.home": "home" }。然后,位置过滤器$[q]引用 arrayFilter { "q.name": "name" }

使用此方法,您可以根据需要添加任意数量的位置过滤器,只要将条件放入arrayFilters.

但是,查看rmongodb的文档,目前无法使用arrayFilters。或者,您可以使用另一个实现了此功能的 R 包,例如Mongolite