MongoDB Ruby 教程
在本教程中,我们展示了如何在 Ruby 中使用 MongoDB。 在 ZetCode 上有一个简洁的 Ruby 教程。
MongoDB 是 NoSQL 跨平台的面向文档的数据库。 它是可用的最受欢迎的数据库之一。 MongoDB 由 MongoDB Inc.开发,并作为免费和开源软件发布。
MongoDB 中的记录是一个文档,它是由字段和值对组成的数据结构。 MongoDB 文档与 JSON 对象相似。 字段的值可以包括其他文档,数组和文档数组。 MongoDB 将文档存储在集合中。 集合类似于关系数据库中的表以及行中的文档。
安装 MongoDB
以下命令可用于在基于 Debian 的 Linux 上安装 MongoDB。
$ sudo apt-get install mongodb
该命令将安装 MongoDB 随附的必要包。
$ sudo service mongodb status
mongodb start/running, process 975
使用sudo service mongodb status
命令,我们检查mongodb
服务器的状态。
$ sudo service mongodb start
mongodb start/running, process 6448
mongodb
服务器由sudo service mongodb start
命令启动。
$ sudo gem install mongo
MongoDB Ruby 驱动程序通过sudo gem install mongo
命令安装。
建立数据库
mongo
工具是 MongoDB 的交互式 JavaScript Shell 界面,它为系统管理员提供了一个界面,并为开发者提供了一种直接测试数据库查询和操作的方法。
$ mongo testdb
MongoDB shell version: 2.4.9
connecting to: testdb
> db
testdb
> db.cars.insert({name: "Audi", price: 52642})
> db.cars.insert({name: "Mercedes", price: 57127})
> db.cars.insert({name: "Skoda", price: 9000})
> db.cars.insert({name: "Volvo", price: 29000})
> db.cars.insert({name: "Bentley", price: 350000})
> db.cars.insert({name: "Citroen", price: 21000})
> db.cars.insert({name: "Hummer", price: 41400})
> db.cars.insert({name: "Volkswagen", price: 21600})
我们创建一个testdb
数据库,并在cars
集合中插入八个文档。
列出数据库集合
在数据库中Mongo::Client
的collections
方法列出可用的集合。
list_collections.rb
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::FATAL
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')
client.collections.each { |coll| puts coll.name }
`client.close`
该示例连接到testdb
数据库并检索其所有集合。
require 'mongo'
我们包含mongo
驱动程序。
Mongo::Logger.logger.level = ::Logger::FATAL
默认日志记录级别为::Logger::DEBUG
,其中包含许多调试信息。 为了使输出更具可读性,我们选择::Logger::FATAL
调试级别。
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')
Mongo::Client
用于连接到 MongoDB 服务器。 我们指定 URL 和数据库名称。 27017 是 MongoDB 服务器监听的默认端口。
client.collections.each { |coll| puts coll.name }
我们遍历集合列表,并将其名称打印到控制台。
`client.close`
最后,我们关闭连接。 通常,不建议应用调用close
。 连接很昂贵,正在重用。 但是,由于它是一个一次性程序,而不是长时间运行的可重用连接的应用,因此我们确实调用了该方法。
$ ./list_collections.rb
test
cars
这是list_collections.rb
程序的示例输出。
服务器选择超时
:server_selection_timeout
是选择操作服务器的超时时间(以秒为单位)。 当我们无法连接到数据库服务器时,将引发Mongo::Error::NoServerAvailable
。
server_selection_timeout.rb
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::DEBUG
begin
client = Mongo::Client.new([ '127.0.0.1:2717' ], :database => "testdb",
:server_selection_timeout => 5)
client[:cars].find.each { |doc| puts doc }
client.close
rescue Mongo::Error::NoServerAvailable => e
p "Cannot connect to the server"
p e
end
该示例的端口号错误。 默认情况下,服务器选择超时为三十秒。 我们将其设置为五秒钟。
rescue Mongo::Error::NoServerAvailable => e
未建立连接且超时已过期时,抛出Mongo::Error::NoServerAvailable
。
$ ./server_selection_timeout.rb
D, [2016-05-02T15:32:20.231750 #8070] DEBUG -- : MONGODB | Adding 127.0.0.1:2717 to the cluster.
D, [2016-05-02T15:32:20.232486 #8070] DEBUG -- : MONGODB | Connection refused - connect(2)
D, [2016-05-02T15:32:20.732627 #8070] DEBUG -- : MONGODB | Connection refused - connect(2)
D, [2016-05-02T15:32:21.232724 #8070] DEBUG -- : MONGODB | Connection refused - connect(2)
...
调试日志记录级别在尝试连接到服务器时会提供这些消息。
数据库统计
dbstats
命令获取数据库的统计信息。
dbstats.rb
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::FATAL
client = Mongo::Client.new([ '127.0.0.1:27017' ])
db = client.use("testdb")
db.command({"dbstats" => 1}).documents[0].each do |key, value|
puts "#{key}: #{value}"
end
`client.close`
该示例连接到testdb
数据库并显示其统计信息。 数据库对象的command
方法用于执行命令。
db = client.use("testdb")
use
方法选择testdb
数据库。
db.command({"dbstats" => 1}).documents[0].each do |key, value|
puts "#{key}: #{value}"
end
command
方法执行dbstats
命令并解析返回的哈希值。
$ ./dbstats.rb
db: testdb
collections: 4
objects: 21
avgObjSize: 43.23809523809524
dataSize: 908
storageSize: 16384
numExtents: 4
indexes: 2
indexSize: 16352
fileSize: 201326592
nsSizeMB: 16
dataFileVersion: {"major"=>4, "minor"=>5}
ok: 1.0
这是dbstats.rb
程序的输出。
读取数据
find
方法在集合中查找文档。
read_all.rb
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::FATAL
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')
client[:cars].find.each { |doc| puts doc }
`client.close`
在示例中,我们遍历cars
集合的所有数据。
client[:cars].find.each { |doc| puts doc }
传递空查询将返回所有文档。 我们使用each
方法遍历:cars
集合的文档。
$ ./read_all.rb
{"_id"=>1, "name"=>"Audi", "price"=>52642}
{"_id"=>2, "name"=>"Mercedes", "price"=>57127}
{"_id"=>3, "name"=>"Skoda", "price"=>9000}
{"_id"=>4, "name"=>"Volvo", "price"=>29000}
{"_id"=>5, "name"=>"Bentley", "price"=>350000}
{"_id"=>6, "name"=>"Citroen", "price"=>21000}
{"_id"=>7, "name"=>"Hummer", "price"=>41400}
{"_id"=>8, "name"=>"Volkswagen", "price"=>21600}
这是read_all.rb
示例的输出。
计数文档
count
方法返回集合中匹配文档的数量。
count_documents.rb
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::FATAL
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')
docs = client[:cars].find
puts "There are #{docs.count} documents"
`client.close`
该示例计算:cars
集合中的文档数。
docs = client[:cars].find
我们从cars
集合中检索所有文档。
puts "There are #{docs.count} documents"
我们打印退回文档的数量。
$ ./count_documents.rb
There are 8 documents
cars
集合中有八个文档。
读取文档
find
方法采用可选的filter
参数,该参数用于过滤传入的数据。
read_one.rb
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::FATAL
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')
client[:cars].find(:name => 'Volkswagen').each do |doc|
puts doc
end
`client.close`
该示例从:cars
集合中读取一个文档。
client[:cars].find(:name => 'Volkswagen').each do |doc|
find
方法仅显示包含大众汽车的文档。
$ ./read_one.rb
{"_id"=>8, "name"=>"Volkswagen", "price"=>21600}
这是示例的输出。
查询运算符
可以使用 MongoDB 查询运算符(例如$gt
,$lt
或$ne
)过滤数据。
read_op.rb
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::FATAL
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')
puts client[:cars].find("price" => {'$lt' => 30000}).to_a
puts "**************************"
client[:cars].find("price" => {'$gt' => 30000}).each do |doc|
puts doc
end
`client.close`
该示例打印所有汽车价格低于 30,000 的文档,然后打印所有汽车价格高于 30,000 的文档。
puts client[:cars].find("price" => {'$lt' => 30000}).to_a
$lt
运算符用于获取价格低于 30,000 的汽车。
$ ./read_op.rb
{"_id"=>3, "name"=>"Skoda", "price"=>9000}
{"_id"=>4, "name"=>"Volvo", "price"=>29000}
{"_id"=>6, "name"=>"Citroen", "price"=>21000}
{"_id"=>8, "name"=>"Volkswagen", "price"=>21600}
**************************
{"_id"=>1, "name"=>"Audi", "price"=>52642}
{"_id"=>2, "name"=>"Mercedes", "price"=>57127}
{"_id"=>5, "name"=>"Bentley", "price"=>350000}
{"_id"=>7, "name"=>"Hummer", "price"=>41400}
这是示例的输出。
$and
和$or
逻辑运算符可用于组合多个表达式。
read_and_or.rb
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::FATAL
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')
puts client[:cars].find('$or' => [{:name => "Audi"}, {:name => "Skoda" }]).to_a
puts "*************************************"
puts client[:cars].find('$and' => [{:price => {'$gt' => 20000}},
{:price => {'$lt' => 50000 }}]).to_a
client.close
该示例同时显示了$or
和$and
运算符。
puts client[:cars].find('$or' => [{:name => "Audi"}, {:name => "Skoda" }]).to_a
$or
运算符用于返回名称为 Audi 或 Skoda 的文档。
puts client[:cars].find('$and' => [{:price => {'$gt' => 20000}},
{:price => {'$lt' => 50000 }}]).to_a
$and
运算符检索价格介于 20,000 和 50,000 之间的汽车。
$ ./read_and_or.rb
{"_id"=>1, "name"=>"Audi", "price"=>52642}
{"_id"=>3, "name"=>"Skoda", "price"=>9000}
*************************************
{"_id"=>4, "name"=>"Volvo", "price"=>29000}
{"_id"=>6, "name"=>"Citroen", "price"=>21000}
{"_id"=>7, "name"=>"Hummer", "price"=>41400}
{"_id"=>8, "name"=>"Volkswagen", "price"=>21600}
这是示例的输出。
投影
投影确定要在结果集中的每个文档中包括或排除的字段。
projection.rb
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::FATAL
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')
cursor = client[:cars].find({}, { :projection => {:_id => 0} })
cursor.each { |doc| puts doc }
`client.close`
该示例从输出中排除_id
字段。
cursor = client[:cars].find({}, { :projection => {:_id => 0} })
我们在find
方法的第二个参数中指定:projection
选项。
$ ./projection.rb
{"name"=>"Audi", "price"=>52642}
{"name"=>"Mercedes", "price"=>57127}
{"name"=>"Skoda", "price"=>9000}
{"name"=>"Volvo", "price"=>29000}
{"name"=>"Bentley", "price"=>350000}
{"name"=>"Citroen", "price"=>21000}
{"name"=>"Hummer", "price"=>41400}
{"name"=>"Volkswagen", "price"=>21600}
这是示例的输出。 尚未包含_id
。
限制数据输出
limit
方法指定要返回的文档数,skip
方法指定要跳过的文档数。
skip_limit.rb
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::FATAL
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')
docs = client[:cars].find().skip(2).limit(5)
docs.each do |doc|
puts doc
end
`client.close`
该示例从testdb.cars
集合中读取,跳过前两个文档,并将输出限制为五个文档。
docs = client[:cars].find().skip(2).limit(5)
skip
方法跳过前两个文档,limit
方法将输出限制为五个文档。
$ ./skip_limit.rb
{"_id"=>3, "name"=>"Skoda", "price"=>9000}
{"_id"=>4, "name"=>"Volvo", "price"=>29000}
{"_id"=>5, "name"=>"Bentley", "price"=>350000}
{"_id"=>6, "name"=>"Citroen", "price"=>21000}
{"_id"=>7, "name"=>"Hummer", "price"=>41400}
这是示例的输出。
聚合
聚合计算集合中数据的聚合值。
sum_all_cars.rb
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::FATAL
agr = [{"$group" => {:_id => 1, :all => { "$sum" => "$price" } }}];
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')
client[:cars].aggregate(agr).each { |doc| puts doc }
该示例计算集合中所有汽车的价格。
agr = [{"$group" => {:_id => 1, :all => { "$sum" => "$price" } }}];
$sum
运算符计算并返回数值的总和。 $group
运算符通过指定的标识符表达式对输入文档进行分组,并将累加器表达式(如果指定)应用于每个组。
client[:cars].aggregate(agr).each { |doc| puts doc }
aggregate
方法将聚合操作应用于cars
集合。
$ ./sum_all_cars.rb
{"_id"=>1, "all"=>609727}
所有价格的总和是 619,369。
我们可以使用$match
运算符来选择要汇总的特定汽车。
sum_two_cars.rb
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::FATAL
agr = [{"$match" => {"$or" => [ { :name => "Audi" }, { :name => "Volvo" }]}},
{"$group" => {:_id => 1, :sumOfTwo => { "$sum" => "$price" } }}];
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')
client[:cars].aggregate(agr).each { |doc| puts doc }
`client.close`
该示例计算奥迪和沃尔沃汽车的价格总和。
agr = [{"$match" => {"$or" => [ { :name => "Audi" }, { :name => "Volvo" }]}},
{"$group" => {:_id => 1, :sumOfTwo => { "$sum" => "$price" } }}];
该表达式使用$match
,$or
,$group
和$sum
运算符执行任务。
$ ./sum_two_cars.rb
{"_id"=>1, "sumOfTwo"=>81000}
两辆车的价格之和为 81,642。
插入文档
insert_one
方法将单个文档插入到集合中。
insert_doc.rb
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::FATAL
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')
doc = { :_id => 9, :name => "Toyota", :price => 37600 }
client[:cars].insert_one doc
`client.close`
该示例将一辆汽车插入cars
集合。
doc = { :_id => 9, :name => "Toyota", :price => 37600 }
这是要插入的文档。
client[:cars].insert_one doc
insert_one
方法将文档插入到集合中。
> db.cars.find({_id:9})
{ "_id" : 9, "name" : "Toyota", "price" : 37600 }
我们用mongo
工具确认插入。
插入许多文档
insert_many
方法将多个文档插入一个集合中。
create_collection.rb
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::FATAL
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')
result = client[:continents].insert_many([
{ :_id => BSON::ObjectId.new, :name => 'Africa' },
{ :_id => BSON::ObjectId.new, :name => 'America' },
{ :_id => BSON::ObjectId.new, :name => 'Antarctica' },
{ :_id => BSON::ObjectId.new, :name => 'Australia' },
{ :_id => BSON::ObjectId.new, :name => 'Asia' },
{ :_id => BSON::ObjectId.new, :name => 'Europe' }
])
puts "#{result.inserted_count} documents were inserted"
`client.close`
该示例创建一个大洲集合并将六个文档插入其中。
result = client[:continents].insert_many([
{ :_id => BSON::ObjectId.new, :name => 'Africa' },
{ :_id => BSON::ObjectId.new, :name => 'America' },
{ :_id => BSON::ObjectId.new, :name => 'Antarctica' },
{ :_id => BSON::ObjectId.new, :name => 'Australia' },
{ :_id => BSON::ObjectId.new, :name => 'Asia' },
{ :_id => BSON::ObjectId.new, :name => 'Europe' }
])
使用insert_many
方法将六个记录的数组插入到新集合中。 BSON::ObjectId.new()
创建一个新的ObjectID
,这是用于标识文档的唯一值,而不是整数。
puts "#{result.inserted_count} documents were inserted"
返回结果的inserted_count
给出成功插入的文档数。
> db.continents.find()
{ "_id" : ObjectId("57263c0f81365b266b17358c"), "name" : "Africa" }
{ "_id" : ObjectId("57263c0f81365b266b17358d"), "name" : "America" }
{ "_id" : ObjectId("57263c0f81365b266b17358e"), "name" : "Antarctica" }
{ "_id" : ObjectId("57263c0f81365b266b17358f"), "name" : "Australia" }
{ "_id" : ObjectId("57263c0f81365b266b173590"), "name" : "Asia" }
{ "_id" : ObjectId("57263c0f81365b266b173591"), "name" : "Europe" }
continents
集合已成功创建。
修改文档
delete_one
方法用于删除文档,update_one
用于更新文档。
mofify.js
#!/usr/bin/ruby
require 'mongo'
Mongo::Logger.logger.level = ::Logger::FATAL
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')
client[:cars].delete_one({:name => "Skoda"})
client[:cars].update_one({:name => "Audi"}, '$set' => {:price => 52000})
`client.close`
该示例删除包含 Skoda 的文档并更新 Audi 的价格。
client[:cars].delete_one({:name => "Skoda"})
delete_one
删除Skoda
的文档。
client[:cars].update_one({:name => "Audi"}, '$set' => {:price => 52000})
通过update_one
方法将 Audi 的价格更改为 52,000。 $set
运算符用于更改价格。
> db.cars.find()
{ "_id" : 1, "name" : "Audi", "price" : 52000 }
{ "_id" : 2, "name" : "Mercedes", "price" : 57127 }
{ "_id" : 4, "name" : "Volvo", "price" : 29000 }
{ "_id" : 5, "name" : "Bentley", "price" : 350000 }
{ "_id" : 6, "name" : "Citroen", "price" : 21000 }
{ "_id" : 7, "name" : "Hummer", "price" : 41400 }
{ "_id" : 8, "name" : "Volkswagen", "price" : 21600 }
{ "_id" : 9, "name" : "Toyota", "price" : 37600 }
我们使用mongo
工具确认更改。
在本教程中,我们使用了 MongoDB 和 Ruby。