用rmongodb在R中运行高级MongoDB查询

由于MySQL让我疯狂,我试图让自己熟悉我的第一个“NoSQL”DBMS,它碰巧是MongoDB。 我通过rmongodb连接到它。

我用rmongodb玩的越多,就运行高级查询而言出现的问题就越多。

首先,我介绍一些示例数据,然后详细介绍我无法正确指定的不同类型的查询。

示例数据

这个例子取自MongoDB网站,并已简化了一些。

pkg <- "rmongodb"
if (!require(pkg, character.only=TRUE)) {
    install.packages(pkg)
    require(pkg, character.only=TRUE)   
}

# Connect to DB
db <- "test"
ns <- "posts"
mongo <- mongo.create(db=db)

# Insert document to collection 'test.users'
b <- mongo.bson.from.list(list(
    "_id"="alex", 
    name=list(first="Alex", last="Benisson"),
    karma=1.0,
    age=30,
    test=c("a", "b")
))
mongo.insert(mongo, "test.users", b)

# Insert document to collection 'test.posts'
b <- mongo.bson.from.list(list(
        "_id"="abcd",
        when=mongo.timestamp.create(strptime("2011-09-19 02:00:00",
            "%Y-%m-%d %H:%M:%s"), increment=1),
        author="alex",
        title="Some title",
        text="Some text.",
        tags=c("tag.1", "tag.2"),
        votes=5,
        voters=c("jane", "joe", "spencer", "phyllis", "li"),
        comments=list(
            list(
                who="jane", 
                when=mongo.timestamp.create(strptime("2011-09-19 04:00:00",
                    "%Y-%m-%d %H:%M:%s"), increment=1),
                comment="Some comment."
            ),
            list(
                who="meghan", 
                when=mongo.timestamp.create(strptime("2011-09-20 13:00:00",
                    "%Y-%m-%d %H:%M:%s"), increment=1),
                comment="Some comment."
            )
        )
    )
)
b
mongo.insert(mongo, "test.posts", b)

与插入JSON / BSON对象有关的两个问题:

  • 文件'test.posts',字段voters :在这种情况下使用c()是否正确?
  • 文档'test.posts',字段comments :什么是正确的方式来指定, c()list()
  • 顶级查询:他们工作的一种享受

    顶级查询工作得很好:

    # Get all posts by 'alex' (only titles)
    res <- mongo.find(mongo, "test.posts", query=list(author="alex"), 
        fields=list(title=1L))
    out <- NULL
    while (mongo.cursor.next(res))
        out <- c(out, list(mongo.bson.to.list(mongo.cursor.value(res))))
    
    > out
    [[1]]
                           _id                      title 
                         "abcd"            "No Free Lunch" 
    

    问题1:基本子级查询

    如何运行一个简单的“子级查询”(而不是顶级查询),这些子级查询需要达到JSON / BSON样式的MongoDB对象的任意深度级别? 这些子级查询使用MongoDB的点符号,我似乎无法弄清楚如何将其映射到有效的rmongodb查询

    用简单的MongoDB语法,就像

    > db.posts.find( { comments.who : "meghan" } )
    

    会工作。 但我不知道如何用rmongodb函数来做到这一点

    这是我到目前为止所尝试的

    # Get all comments by 'meghan' from 'test.posts'
    
    #--------------------
    # Approach 1)
    #--------------------
    res <- mongo.find(mongo, "test.posts", query=list(comments=list(who="meghan")))
    out <- NULL
    while (mongo.cursor.next(res))
        out <- c(out, list(mongo.bson.to.list(mongo.cursor.value(res))))
    
    > out
    NULL
    # Does not work
    
    #--------------------
    # Approach 2) 
    #--------------------
    buf <- mongo.bson.buffer.create()
    mongo.bson.buffer.start.object(buf, "comments")
    mongo.bson.buffer.append(buf, "who", "meghan")
    mongo.bson.buffer.finish.object(buf)
    query <- mongo.bson.from.buffer(buf)
    res <- mongo.find(mongo, "test.posts", query=query)
    out <- NULL
    while (mongo.cursor.next(res))
        out <- c(out, list(mongo.bson.to.list(mongo.cursor.value(res))))
    
    > out
    NULL
    # Does not work
    

    问题2:使用$运算符进行查询

    这些工作

    查询1

    buf <- mongo.bson.buffer.create()
    mongo.bson.buffer.start.object(buf, "age")
    mongo.bson.buffer.append(buf, "$lte", 30)
    mongo.bson.buffer.finish.object(buf)
    criteria <- mongo.bson.from.buffer(buf)
    criteria
    
    > mongo.find.one(mongo, "test.users", query=criteria)
        _id : 2      alex
        name : 3     
            first : 2    Alex
            last : 2     Benisson
    
        karma : 1    1.000000
        age : 1      30.000000
        test : 4     
            0 : 2    a
            1 : 2    b
    

    查询2

    buf <- mongo.bson.buffer.create()
    mongo.bson.buffer.start.object(buf, "test")
    mongo.bson.buffer.append(buf, "$in", c("a", "z"))
    mongo.bson.buffer.finish.object(buf)
    criteria <- mongo.bson.from.buffer(buf)
    criteria
    mongo.find.one(mongo, "test.users", query=criteria)
    

    但是,请注意,原子集将导致返回值为NULL

    mongo.bson.buffer.append(buf, "$in", "a")
    # Instead of 'mongo.bson.buffer.append(buf, "$in", c("a", "z"))'
    

    尝试与子级别查询相同,我再次丢失

    buf <- mongo.bson.buffer.create()
    mongo.bson.buffer.start.object(buf, "name")
    mongo.bson.buffer.start.object(buf, "first")
    mongo.bson.buffer.append(buf, "$in", c("Alex", "Horst"))
    mongo.bson.buffer.finish.object(buf)
    mongo.bson.buffer.finish.object(buf)
    criteria <- mongo.bson.from.buffer(buf)
    criteria <- mongo.bson.from.buffer(buf)
    > criteria
        name : 3     
            first : 3    
                $in : 4      
                    0 : 2    Alex
                    1 : 2    Horst
    
    > mongo.find.one(mongo, "test.users", query=criteria)
    NULL
    

    c()或list()都可以。 取决于组件是否被命名以及它们是否都具有相同的类型(用于列表)。 最好的办法是看看生成的BSON,看看你是否得到你想要的。 为了最好地控制生成的对象,使用mongo.bson.buffer和对其进行操作的函数。 事实上,这就是子查询失败的原因。 'comments'被创建为子对象而不是数组。 mongo.bson.from.list()很方便,但它不会给你相同的控制权,有时候它猜测错误的是从复杂结构中产生什么。

    对另一组数据的查询可以像这样纠正:

    buf <- mongo.bson.buffer.create()
    mongo.bson.buffer.start.object(buf, "name.first")
    mongo.bson.buffer.append(buf, "$in", c("Alex", "Horst"))
    mongo.bson.buffer.finish.object(buf)
    criteria <- mongo.bson.from.buffer(buf)
    

    请注意,你肯定需要在这里使用缓冲区,因为R会扼杀点名。

    我希望这可以解决你的问题。 如果您还有其他问题,请告诉我。


    我仍然不清楚SO的首选方式是什么,一旦发布了问题,我们希望进一步阐述,可能会增加更多的问题和回答方法。

    正如我经常被告知不要在未来的编辑中炸掉我的原始问题一样,在这个“答案”中,我只是接受了Gerald Lindsly的建议并尝试将其写入实际的代码中(因为它仍然不适合我):

    准备工作

    pkg <- "rmongodb"
    if (!require(pkg, character.only=TRUE)) {
        install.packages(pkg)
        require(pkg, character.only=TRUE)   
    }
    
    # Connect to DB
    db <- "test"
    ns <- "posts"
    mongo <- mongo.create(db=db)
    
    # Make sure we start with an empty collection
    mongo.drop(mongo, paste(db, ns, sep="."))
    

    插入文件

    正如杰拉尔德在他的回答中指出的那样, mongo.bson.from.list()有时会对所产生的BSON结构做出错误的猜测,所以我试图继续显式创建BSON数组对象:

    buf <- mongo.bson.buffer.create()
    
    # 'REGULAR' APPENDING
    mongo.bson.buffer.append(buf, "_id", "abcd")
    mongo.bson.buffer.append(buf, "when", 
        mongo.timestamp.create(strptime("2011-09-19 02:00:00",
            "%Y-%m-%d %H:%M:%s"), increment=1))
    mongo.bson.buffer.append(buf, "author", "alex")
    mongo.bson.buffer.append(buf, "title", "Some title")
    mongo.bson.buffer.append(buf, "text", "Some text.")
    mongo.bson.buffer.append(buf, "tags", c("tag.1", "tag.2"))
    mongo.bson.buffer.append(buf, "votes", 5)
    # /
    
    # VOTERS ARRAY
    mongo.bson.buffer.start.array(buf, "voters")
    voters <- c("jane", "joe", "spencer", "phyllis", "li")
    i=1
    for (i in seq(along=voters)) {
        mongo.bson.buffer.append(buf, as.character(i), voters[i])
    }
    mongo.bson.buffer.finish.object(buf)
    # /
    
    # COMMENTS ARRAY
    mongo.bson.buffer.start.array(buf, "comments")
    
    mongo.bson.buffer.start.object(buf, "1")
    mongo.bson.buffer.append(buf, "who", "jane")
    mongo.bson.buffer.append(buf, "when", 
        mongo.timestamp.create(strptime("2011-09-19 04:00:00",
                "%Y-%m-%d %H:%M:%s"), increment=1))
    mongo.bson.buffer.append(buf, "comment", "some comment.")
    mongo.bson.buffer.finish.object(buf)
    
    mongo.bson.buffer.start.object(buf, "2")
    mongo.bson.buffer.append(buf, "who", "meghan")
    mongo.bson.buffer.append(buf, "when", 
        mongo.timestamp.create(strptime("2011-09-20 13:00:00",
                "%Y-%m-%d %H:%M:%s"), increment=1))
    mongo.bson.buffer.append(buf, "comment", "some comment.")
    mongo.bson.buffer.finish.object(buf)
    # /
    
    # FINALIZE
    mongo.bson.buffer.finish.object(buf)
    b <- mongo.bson.from.buffer(buf)
    > b
    _id : 2      abcd
    when : 17    i: 1, t: 1316390400
    author : 2   alex
    title : 2    Some title
    text : 2     Some text.
    tags : 4     
        0 : 2    tag.1
        1 : 2    tag.2
    
    votes : 1    5.000000
    voters : 4   
        1 : 2    jane
        2 : 2    joe
        3 : 2    spencer
        4 : 2    phyllis
        5 : 2    li
    
    comments : 4     
        1 : 3    
            who : 2      jane
            when : 17    i: 1, t: 1316397600
            comment : 2      some comment.
    
        2 : 3    
            who : 2      meghan
            when : 17    i: 1, t: 1316516400
            comment : 2      some comment.
    
    mongo.insert(mongo, "test.posts", b)
    

    基本的子级别查询

    # Get all comments by 'meghan' from 'test.posts'
    
    #--------------------
    # Approach 1)
    #--------------------
    res <- mongo.find(mongo, "test.posts", query=list(comments=list(who="meghan")))
    out <- NULL
    while (mongo.cursor.next(res))
        out <- c(out, list(mongo.bson.to.list(mongo.cursor.value(res))))
    
    > out
    NULL
    # Does not work
    
    #--------------------
    # Approach 2) 
    #--------------------
    buf <- mongo.bson.buffer.create()
    mongo.bson.buffer.start.object(buf, "comments")
    mongo.bson.buffer.append(buf, "who", "meghan")
    mongo.bson.buffer.finish.object(buf)
    query <- mongo.bson.from.buffer(buf)
    res <- mongo.find(mongo, "test.posts", query=query)
    out <- NULL
    while (mongo.cursor.next(res))
        out <- c(out, list(mongo.bson.to.list(mongo.cursor.value(res))))
    
    > out
    NULL
    # Does not work
    

    指定文件时,我仍然必须在这里做错事;-)


    关于原子查询和$ in操作符,我从第一个问题中得到Query 2,其工作如下:

    buf <- mongo.bson.buffer.create()
    mongo.bson.buffer.start.object(buf, "test")
    mongo.bson.buffer.start.array(buf, "$in")
    mongo.bson.buffer.append(buf, "a", "a")
    mongo.bson.buffer.finish.object(buf)
    mongo.bson.buffer.finish.object(buf)
    criteria <- mongo.bson.from.buffer(buf)
    criteria
    

    我想明确地开始和结束数组是否有窍门,如果数组将最终只保留一个元素。

    有一件事很有用,那就是监视mongod控制台或日志(使用-v选项启动mongod后)。 运行你的旧查询,你会看到:

    Tue Nov 20 16:09:04 [conn23] User Assertion: 12580:invalid query
    Tue Nov 20 16:09:04 [conn23] assertion 12580 invalid query ns:test.users query:{ test: { $in: "a" } }
    Tue Nov 20 16:09:04 [conn23] problem detected during query over test.users : { $err: "invalid query", code: 12580 }
    Tue Nov 20 16:09:04 [conn23] query test.users query: { test: { $in: "a" } } ntoreturn:0 keyUpdates:0 exception: invalid query code:12580 locks(micros) r:440 reslen:59 0ms
    

    运行修改的查询,它看起来很好:

    Tue Nov 20 16:10:14 [conn23] query test.users query: { test: { $in: [ "a" ] } } ntoreturn:0 keyUpdates:0 locks(micros) r:168 nreturned:1 reslen:142 0ms
    
    链接地址: http://www.djcxy.com/p/64577.html

    上一篇: Running advanced MongoDB queries in R with rmongodb

    下一篇: R error: "invalid type (NULL) for variable"