6.2 R利剑NoSQL 之 MongoDB

问题

R如何连接MongoDB?

引言

MongoDB作为一种文档型的NoSQL数据库,使用起来非常灵活,回避了关系型数据库前期的复杂数据库设计。MongoDB存储基于JSON格式,同时用Javascript作为数据库操作语言,给了使用者无限想象的空间,可以通过编程在MongoDB服务器中解决非常复杂的条件查询的问题。本节将介绍rmongodb包,让R程序轻松连接MongoDB。

6.2.1 MongoDB环境准备

首先是环境准备,这里我选择了Linux Ubuntu操作系统,大家可以根据自己的使用习惯选择顺手的Linux。

  • Linux: Ubuntu 12.04.2 LTS 64bit server
  • MongoDB: v2.4.9
  • IP: 192.168.1.199

关于MongoDB的安装和配置,请参考附录D。查看MongoDB服务器环境。使用/etc/init.d/mongodb命令,启动MongoDB,默认端口: port=27017

~ sudo /etc/init.d/mongodb start  # 启动mongodb
Rather than invoking init scripts through /etc/init.d, use the service(8)
utility, e.g. service mongodb start

Since the script you are attempting to invoke has been converted to an
Upstart job, you may also use the start(8) utility, e.g. start mongodb
mongodb start/running, process 1878

~ ps -aux|grep mongo  # 查看系统进程
mongodb   1878  3.3  0.4 348168 37488 ?        Ssl  10:19   0:01 /usr/bin/mongod --config /etc/mongodb.conf

~ cat /var/log/mongodb/mongodb.log   # 查看启动日志
Mon Mar 31 10:19:28.764 [initandlisten] MongoDB starting : pid=1878 port=27017 dbpath=/var/lib/mongodb 64-bit host=rbook
Mon Mar 31 10:19:28.764 [initandlisten] db version v2.4.9
Mon Mar 31 10:19:28.764 [initandlisten] git version: 52fe0d21959e32a5bdbecdc62057db386e4e029c

Mon Mar 31 10:19:28.764 [initandlisten] build info: Linux ip-10-2-29-40 2.6.21.7-2.ec2.v1.2.fc8xen #1 SMP Fri Nov 20 17:48:28 EST 2009 x86_64 BOOST_LIB_VERSION=1_49
Mon Mar 31 10:19:28.764 [initandlisten] allocator: tcmalloc
Mon Mar 31 10:19:28.764 [initandlisten] options: { config: "/etc/mongodb.conf", dbpath: "/var/lib/mongodb", logappend: "true", logpath: "/var/log/mongodb/mongodb.log" }
Mon Mar 31 10:19:28.853 [initandlisten] journal dir=/var/lib/mongodb/journal
Mon Mar 31 10:19:28.853 [initandlisten] recover : no journal files present, no recovery needed
Mon Mar 31 10:19:28.872 [initandlisten] waiting for connections on port 27017
Mon Mar 31 10:19:28.872 [websvr] admin web console waiting for connections on port 28017
MongoDB运行时配置:
  • 进程号:pid=1878
  • 端口:port=27017
  • 数据文件目录:dbpath=/var/lib/mongodb
  • 主机名:host=rbook
  • IP: 192.168.1.199

使用MongoDB的命令行客户端程序mongo,打开Mongo Shell。Mongo Shell的简单操作主要有:查看数据库、切换数据库、查看数据集。

~ mongo    #打开mongo shell
MongoDB shell version: 2.0.6
connecting to: test

> show dbs     #进入mongo shell, 列表显示数据库
db      0.0625GB
feed    0.0625GB
foobar  0.0625GB
local   (empty)

> use foobar     #切换数据库
switched to db foobar

> show collections    #列表显示数据集
blog
system.indexes

接下来,我们使用R语言的MongoDB客户端rmongodb远程访问MongoDB服务器进行测试。

6.2.2 rmongodb函数库

rmongodb是R语言访问MongoDB数据库的客户端通信接口程序,rmongodb提供了153个函数,对应MongoDB的各种操作。比起别的NoSQL数据库程序包来说,这个项目真是工程浩大啊。虽然函数很多,但是用法都是比较简单的,对R语言支持足够灵活,代码也比较简洁。我们不一一列出153个rmongodb函数,仅挑选一些常用的函数介绍。感兴趣的读者可以从rmongodb的官方文档中,找到所有的函数。

mongo<-mongo.create()  #建立mongo连接
mongo.is.connected(mongo)  #查看接连是否正常
buf <- mongo.bson.buffer.create()  #创建一个BSON对象缓存
mongo.bson.buffer.append(buf, "name", "Echo")  #给对象buf增加element

#增加对象类型的element

score <- c(5, 3.5, 4)
names(score) <- c("Mike", "Jimmy", "Ann")
mongo.bson.buffer.append(buf, "score", score)

# 增加数组类型的element

mongo.bson.buffer.start.array(buf, "comments")
mongo.bson.buffer.append(buf, "0", "a1")
mongo.bson.buffer.append(buf, "1", "a2")
mongo.bson.buffer.append(buf, "2", "a3")

mongo.bson.buffer.finish.object(buf) # 关闭数组类型的element
b <- mongo.bson.from.buffer(buf)  # 取出缓存数据
ns="db.blog"  # 数据库.数据集

# 插入一条记录

mongo.insert(mongo,ns,b) 
db.blog.insert(b)

# 创建查询对象query

buf <- mongo.bson.buffer.create()
mongo.bson.buffer.append(buf, "name", "Echo")
query <- mongo.bson.from.buffer(buf)

# 创建查询返回值对象

buf <- mongo.bson.buffer.create()
mongo.bson.buffer.append(buf, "name", 1)
fields <- mongo.bson.from.buffer(buf)

# 执行单条记录查询

mongo.find.one(mongo, ns, query, fields)
db.blog.findOne({query},{fields})

# 执行列表记录查询

mongo.find(mongo, ns, query, fields)
db.blog.find({query},{fields})

# 创建修改器对象objNew

buf <- mongo.bson.buffer.create()
mongo.bson.buffer.start.object(buf, "$inc")
mongo.bson.buffer.append(buf, "age", 1L)
mongo.bson.buffer.finish.object(buf)
objNew <- mongo.bson.from.buffer(buf)

# 执行修改操作

mongo.update(mongo, ns, query, objNew)
db.blog.update({query},{objNew})

# 单行代码修改操作

mongo.update(mongo, ns, query, list(name="Echo", age=25))
db.blog.update({query},{objNew})

# 删除所选对象

mongo.remove(mongo, ns, query)
db.blog.remove({query},{objNew})
mongo.destroy(mongo) # 销毁mongo连接

6.2.3 rmongodb基本使用操作

R语言的客户端环境

  • Win7 64bit
  • R: 3.0.1 x86_64-w64-mingw32/x64 b4bit

上面先介绍了rmongodb函数库的一些基本函数,但我们还没有安装rmongodb类库。

~ R    # 启动R程序
> install.packages("rmongodb")    #安装rmongodb
> library(rmongodb)    # 加载类库

然后,通过mongo.create()函数,与MongoDB数据库服务器建立连接。如果是本地连接,mongo.create()不需要参数。下面例子使用远程连接,增加host参数指定MongDB服务器的IP地址。

> mongo<-mongo.create(host="192.168.1.199")    #远程连接mongodb server

使用mongo.is.connected()函数,检查是否连接正常。这条语句在开发时会经常使用到。在用R语言建模时,如果对象或者函数使用错误,连接会被自动断开。由于MongoDB的异常机制,断开时不会有提示。大家要手动使用这条命令测试连接是否正常。

> print(mongo.is.connected(mongo))    #查看是否连接正常
[1] TRUE

接下来,定义两个变量,db和ns。db是我们需要使用的数据库,ns是数据库+数据集。

> db<-"foobar"    #定义db
> ns<-"foobar.blog"  # 定义db.collection

下面我们创建一个JSON对象,并把这个对象存入MongoDB。

{
    "_id" : ObjectId("51663e14da2c51b1e8bc62eb"),
    "name" : "Echo",
    "age" : 22,
    "gender" : "Male",
    "score" : {
            "Mike" : 5,
            "Jimmy" : 3.5,
            "Ann" : 4
    },
    "comments" : [
            "a1",
            "a2",
            "a3"
    ]
}

组织bson类型数据

> buf <- mongo.bson.buffer.create()
> mongo.bson.buffer.append(buf, "name", "Echo")
> mongo.bson.buffer.append(buf, "age", 22L)
> mongo.bson.buffer.append(buf, "gender", 'Male')

#对象类型

> score <- c(5, 3.5, 4)
> names(score) <- c("Mike", "Jimmy", "Ann")
> mongo.bson.buffer.append(buf, "score", score)

#数组类型

> mongo.bson.buffer.start.array(buf, "comments")
> mongo.bson.buffer.append(buf, "0", "a1")
> mongo.bson.buffer.append(buf, "1", "a2")
> mongo.bson.buffer.append(buf, "2", "a3")
> mongo.bson.buffer.finish.object(buf)
> b <- mongo.bson.from.buffer(buf)
> mongo.insert(mongo,ns,b)  # 插入mongodb

# 单条显示插入的数据

> buf <- mongo.bson.buffer.create()
> mongo.bson.buffer.append(buf, "name", "Echo")
> query <- mongo.bson.from.buffer(buf)
> print(mongo.find.one(mongo, ns, query))
然后,分别使用修改器$inc,$set,$push进行操作。首先使用$inc修改器,修改给age加1。
> buf <- mongo.bson.buffer.create()
> mongo.bson.buffer.start.object(buf, "$inc")
> mongo.bson.buffer.append(buf, "age", 1L)
> mongo.bson.buffer.finish.object(buf)
> objNew <- mongo.bson.from.buffer(buf)
> mongo.update(mongo, ns, query, objNew)
> print(mongo.find.one(mongo, ns, query))

使用$set修改器,修改age=1。

> buf <- mongo.bson.buffer.create()
> mongo.bson.buffer.start.object(buf, "$set")
> mongo.bson.buffer.append(buf, "age", 1L)
> mongo.bson.buffer.finish.object(buf)
> objNew <- mongo.bson.from.buffer(buf)
> mongo.update(mongo, ns, query, objNew)
> print(mongo.find.one(mongo, ns, query))

使用$push修改器,给comments数组追加”Orange”数据。

> buf <- mongo.bson.buffer.create()
> mongo.bson.buffer.start.object(buf, "$push")
> mongo.bson.buffer.append(buf, "comments", "Orange")
> mongo.bson.buffer.finish.object(buf)
> objNew <- mongo.bson.from.buffer(buf)
> mongo.update(mongo, ns, query, objNew)
> print(mongo.find.one(mongo, ns, query))

使用简化修改语句,给对象重新赋值。

> mongo.update(mongo, ns, query, list(name="Echo", age=25))
> print(mongo.find.one(mongo, ns, query))

最后删除对象,并断开连接。

> mongo.remove(mongo, ns, query)    # 删除对象
> mongo.destroy(mongo)    # 销毁mongo连接

6.2.4 rmongodb性能测试的案例

我们已经清楚了rmongodb的使用,接下来,我们做一个性能测试案例:批量插入数据,使用修改器批量修改数据,并比较3种修改器的速度。首先批量插入数据函数。

> library(stringr)    # 加载stringr包,用来实现字符串操作
> batch_insert<-function(arr=1:10,ns){    # 批量插入数据
+   mongo_insert<-function(x){
+     buf <- mongo.bson.buffer.create()
+     mongo.bson.buffer.append(buf, "name", str_c("Dave",x))
+     mongo.bson.buffer.append(buf, "age", x)
+     mongo.bson.buffer.start.array(buf, "comments")
+     mongo.bson.buffer.append(buf, "0", "a1")
+     mongo.bson.buffer.append(buf, "1", "a2")
+     mongo.bson.buffer.append(buf, "2", "a3")
+     mongo.bson.buffer.finish.object(buf)
+     return(mongo.bson.from.buffer(buf))
+   }
+   mongo.insert.batch(mongo, ns, lapply(arr,mongo_insert))
+}

批量修改,$inc修改器函数。

> batch_inc<-function(data,ns){
+    for(i in data){
+      buf <- mongo.bson.buffer.create()
+      mongo.bson.buffer.append(buf, "name", str_c("Dave",i))
+      criteria <- mongo.bson.from.buffer(buf)
+      buf <- mongo.bson.buffer.create()
+      mongo.bson.buffer.start.object(buf, "$inc")
+      mongo.bson.buffer.append(buf, "age", 1L)
+      mongo.bson.buffer.finish.object(buf)
+      objNew <- mongo.bson.from.buffer(buf)
+      mongo.update(mongo, ns, criteria, objNew)
+    }
+}

批量修改,$set修改器函数

> batch_set<-function(data,ns){
+    for(i in data){
+      buf <- mongo.bson.buffer.create()
+      mongo.bson.buffer.append(buf, "name", str_c("Dave",i))
+      criteria <- mongo.bson.from.buffer(buf)
+      buf <- mongo.bson.buffer.create()
+      mongo.bson.buffer.start.object(buf, "$set")
+      mongo.bson.buffer.append(buf, "age", 1L)
+      mongo.bson.buffer.finish.object(buf)
+      objNew <- mongo.bson.from.buffer(buf)
+      mongo.update(mongo, ns, criteria, objNew)
+    }
+  }

批量修改,$push修改器函数

> batch_push<-function(data,ns){
+    for(i in data){
+      buf <- mongo.bson.buffer.create()
+      mongo.bson.buffer.append(buf, "name", str_c("Dave",i))
+      criteria <- mongo.bson.from.buffer(buf)
+      buf <- mongo.bson.buffer.create()
+      mongo.bson.buffer.start.object(buf, "$push")
+      mongo.bson.buffer.append(buf, "comments", "Orange")
+      mongo.bson.buffer.finish.object(buf)
+      objNew <- mongo.bson.from.buffer(buf)
+      mongo.update(mongo, ns, criteria, objNew)
+    }
  }

上面3个修改器程序中都用的是for循环的写法,因此for循环的性能损耗对三者应该是一样的,我们就不考虑这个影响因素了。下面执行程序,比较3种修改速度。

> ns="foobar.blog"    # 指定表
> data=1:1000    # 循环次数
> mongo.remove(mongo, ns)    # 清空数据
[1] TRUE
> system.time(batch_insert(data, ns))    # 批量插入
user  system elapsed
0.25    0.00    0.28
> system.time(batch_inc(data, ns))    # $inc修改器
user  system elapsed
0.47    0.27    2.50
> system.time(batch_set(data, ns))    # $set修改器
user  system elapsed
0.77    0.48    3.17
> system.time(batch_push(data, ns))    # $push修改器
user  system elapsed
0.81    0.41    4.23

从结果上看,$push最慢 ,3种修改器的速度分别是$push > $set > $inc。由于修改器分别针对不同的类型进行操作,push是对数组操作,set是对任意值操作,inc是对数字操作,所以以上的测试结果仅供参考。

在1.6节中,我们介绍了用R语言处理JSON数据,这里我们又打通R语言和MongoDB的连接,同时MongoDB的数据存储格式又是JSON格式的,从三者关系来看,基于JSON的半结构化的数据处理基础已经完整了,R也将在半结构化的数据处理领域占有一席之地。

results matching ""

    No results matching ""