MongoDB Ruby
最后修改日期 2021 年 1 月 28 日
在本教程中,我们将展示如何在 Ruby 中使用 MongoDB。 ZetCode 上有一个简明的 Ruby 教程。
MongoDB 中的 记录 是一个文档,它是由字段和值对组成的数据结构。 MongoDB 文档 类似于 JSON 对象。 字段的值可以包括其他文档、数组和文档数组。 MongoDB 将文档存储在集合中。 集合 类似于关系数据库中的表,文档类似于行。
$ sudo gem install mongo
使用 sudo gem install mongo
命令安装 MongoDB Ruby 驱动程序。
创建数据库
mongo
工具是一个与 MongoDB 交互的 JavaScript shell 界面,它为系统管理员提供了一个接口,也为开发人员提供了一种直接与数据库测试查询和操作的方式。
$ mongo testdb MongoDB shell version v4.4.3 ... > 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
方法列出数据库中可用的集合。
#!/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 cars
服务器选择超时
:server_selection_timeout
是选择服务器进行操作的超时时间(以秒为单位)。 当我们无法连接到数据库服务器时,会引发 Mongo::Error::NoServerAvailable
。
#!/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
该示例具有错误的端口号。 默认情况下,服务器选择超时时间为 30 秒。 我们将其设置为 5 秒。
rescue Mongo::Error::NoServerAvailable => e
当连接未建立且超时已过期时,会抛出 Mongo::Error::NoServerAvailable
。
$ ./server_selection_timeout.rb D, [2021-01-28T11:51:05.109837 #200040] DEBUG -- : MONGODB | Topology type 'unknown' initializing. D, [2021-01-28T11:51:05.110081 #200040] DEBUG -- : MONGODB | There was a change in the members of the 'Unknown' topology. D, [2021-01-28T11:51:05.110254 #200040] DEBUG -- : MONGODB | Server 127.0.0.1:2717 initializing. ...
调试日志记录级别在尝试连接到服务器时提供这些消息。
数据库统计信息
dbstats
命令获取数据库的统计信息。
#!/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: 1 views: 0 objects: 8 avgObjSize: 54.5 dataSize: 436.0 storageSize: 36864.0 indexes: 1 indexSize: 36864.0 totalSize: 73728.0 scaleFactor: 1.0 fsUsedSize: 222740852736.0 fsTotalSize: 483050881024.0 ok: 1.0
读取数据
find
方法在集合中查找文档。
#!/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"=>BSON::ObjectId('60129621ce69ba1028119242'), "name"=>"Audi", "price"=>52642.0} {"_id"=>BSON::ObjectId('60129627ce69ba1028119243'), "name"=>"Mercedes", "price"=>57127.0} {"_id"=>BSON::ObjectId('6012962fce69ba1028119244'), "name"=>"Skoda", "price"=>9000.0} {"_id"=>BSON::ObjectId('60129634ce69ba1028119245'), "name"=>"Volvo", "price"=>29000.0} {"_id"=>BSON::ObjectId('6012963ace69ba1028119246'), "name"=>"Bentley", "price"=>350000.0} {"_id"=>BSON::ObjectId('6012963ece69ba1028119247'), "name"=>"Citroen", "price"=>21000.0} {"_id"=>BSON::ObjectId('60129643ce69ba1028119248'), "name"=>"Hummer", "price"=>41400.0} {"_id"=>BSON::ObjectId('60129647ce69ba1028119249'), "name"=>"Volkswagen", "price"=>21600.0
计数文档
count
方法返回集合中匹配文档的数量。
#!/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
读取一个文档
find
方法接受一个可选的 filter 参数,该参数用于过滤传入的数据。
#!/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"=>BSON::ObjectId('60129647ce69ba1028119249'), "name"=>"Volkswagen", "price"=>21600.0}
查询运算符
可以使用 $gt
、$lt
或 $ne
等 MongoDB 查询运算符来过滤数据。
#!/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
该示例打印价格低于 30000 的所有文档,然后打印价格高于 30000 的所有文档。
puts client[:cars].find("price" => {'$lt' => 30000}).to_a
$lt
运算符用于获取价格低于 30000 的汽车。
$ ./read_op.rb {"_id"=>BSON::ObjectId('6012962fce69ba1028119244'), "name"=>"Skoda", "price"=>9000.0} {"_id"=>BSON::ObjectId('60129634ce69ba1028119245'), "name"=>"Volvo", "price"=>29000.0} {"_id"=>BSON::ObjectId('6012963ece69ba1028119247'), "name"=>"Citroen", "price"=>21000.0} {"_id"=>BSON::ObjectId('60129647ce69ba1028119249'), "name"=>"Volkswagen", "price"=>21600.0} ************************** {"_id"=>BSON::ObjectId('60129621ce69ba1028119242'), "name"=>"Audi", "price"=>52642.0} {"_id"=>BSON::ObjectId('60129627ce69ba1028119243'), "name"=>"Mercedes", "price"=>57127.0} {"_id"=>BSON::ObjectId('6012963ace69ba1028119246'), "name"=>"Bentley", "price"=>350000.0} {"_id"=>BSON::ObjectId('60129643ce69ba1028119248'), "name"=>"Hummer", "price"=>41400.0}
$and
和 $or
逻辑运算符可用于组合多个表达式。
#!/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
运算符用于返回名称为奥迪或斯柯达的文档。
puts client[:cars].find('$and' => [{:price => {'$gt' => 20000}}, {:price => {'$lt' => 50000 }}]).to_a
$and
运算符检索价格在 20000 到 50000 之间的汽车。
$ ./read_and_or.rb {"_id"=>BSON::ObjectId('60129621ce69ba1028119242'), "name"=>"Audi", "price"=>52642.0} {"_id"=>BSON::ObjectId('6012962fce69ba1028119244'), "name"=>"Skoda", "price"=>9000.0} ************************************* {"_id"=>BSON::ObjectId('60129634ce69ba1028119245'), "name"=>"Volvo", "price"=>29000.0} {"_id"=>BSON::ObjectId('6012963ece69ba1028119247'), "name"=>"Citroen", "price"=>21000.0} {"_id"=>BSON::ObjectId('60129643ce69ba1028119248'), "name"=>"Hummer", "price"=>41400.0} {"_id"=>BSON::ObjectId('60129647ce69ba1028119249'), "name"=>"Volkswagen", "price"=>21600.0}
投影 (Projections)
投影确定要包含或排除结果集中每个文档中的哪些字段。
#!/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.0} {"name"=>"Mercedes", "price"=>57127.0} {"name"=>"Skoda", "price"=>9000.0} {"name"=>"Volvo", "price"=>29000.0} {"name"=>"Bentley", "price"=>350000.0} {"name"=>"Citroen", "price"=>21000.0} {"name"=>"Hummer", "price"=>41400.0} {"name"=>"Volkswagen", "price"=>21600.0}
_id
尚未包含。
限制数据输出
limit
方法指定要返回的文档数量,skip
方法指定要跳过的文档数量。
#!/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"=>BSON::ObjectId('6012962fce69ba1028119244'), "name"=>"Skoda", "price"=>9000.0} {"_id"=>BSON::ObjectId('60129634ce69ba1028119245'), "name"=>"Volvo", "price"=>29000.0} {"_id"=>BSON::ObjectId('6012963ace69ba1028119246'), "name"=>"Bentley", "price"=>350000.0} {"_id"=>BSON::ObjectId('6012963ece69ba1028119247'), "name"=>"Citroen", "price"=>21000.0} {"_id"=>BSON::ObjectId('60129643ce69ba1028119248'), "name"=>"Hummer", "price"=>41400.0}
聚合
聚合计算集合中数据的聚合值。
#!/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
运算符根据指定的标识符表达式对输入文档进行分组,并对每个组应用指定的累加器表达式。$sum
运算符用于计数。
client[:cars].aggregate(agr).each { |doc| puts doc }
aggregate
方法将聚合操作应用于 cars
集合。
$ ./sum_all_cars.rb {"_id"=>1, "all"=>581769.0}
所有价格的总和为 581769。
我们可以使用 $match
运算符来选择要聚合的特定汽车。
#!/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"=>81642.0}
两辆车的总价为 81642。
插入文档
insert_one
方法将单个文档插入到集合中。
#!/usr/bin/ruby require 'mongo' Mongo::Logger.logger.level = ::Logger::FATAL client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb') doc = { :_id => BSON::ObjectId.new, :name => "Toyota", :price => 37600 } client[:cars].insert_one doc client.close
该示例将一辆汽车插入到 cars
集合中。
doc = { :_id => BSON::ObjectId.new, :name => "Toyota", :price => 37600 }
这是要插入的文档。
client[:cars].insert_one doc
insert_one
方法将文档插入到集合中。
> db.cars.find({name:"Toyota"}) { "_id" : ObjectId("60129c9e4c9be8109fc53ddc"), "name" : "Toyota", "price" : 37600 }
我们使用 mongo
工具确认插入。
插入多个文档
insert_many
方法将多个文档插入到集合中。
#!/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
该示例创建一个 continents 集合并将六个文档插入其中。
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("60129bbd4c9be8102bc1ee37"), "name" : "Africa" } { "_id" : ObjectId("60129bbd4c9be8102bc1ee38"), "name" : "America" } { "_id" : ObjectId("60129bbd4c9be8102bc1ee39"), "name" : "Antarctica" } { "_id" : ObjectId("60129bbd4c9be8102bc1ee3a"), "name" : "Australia" } { "_id" : ObjectId("60129bbd4c9be8102bc1ee3b"), "name" : "Asia" } { "_id" : ObjectId("60129bbd4c9be8102bc1ee3c"), "name" : "Europe" }
continents
集合已成功创建。
修改文档
delete_one
方法用于删除文档,update_one
用于更新文档。
#!/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
方法更改为 52000。 $set
运算符用于更改价格。
> db.cars.find() { "_id" : ObjectId("60129621ce69ba1028119242"), "name" : "Audi", "price" : 52000 } { "_id" : ObjectId("60129627ce69ba1028119243"), "name" : "Mercedes", "price" : 57127 } { "_id" : ObjectId("60129634ce69ba1028119245"), "name" : "Volvo", "price" : 29000 } { "_id" : ObjectId("6012963ace69ba1028119246"), "name" : "Bentley", "price" : 350000 } { "_id" : ObjectId("6012963ece69ba1028119247"), "name" : "Citroen", "price" : 21000 } { "_id" : ObjectId("60129643ce69ba1028119248"), "name" : "Hummer", "price" : 41400 } { "_id" : ObjectId("60129647ce69ba1028119249"), "name" : "Volkswagen", "price" : 21600 } { "_id" : ObjectId("60129c9e4c9be8109fc53ddc"), "name" : "Toyota", "price" : 37600 }
我们使用 mongo
工具确认更改。
在本教程中,我们使用 MongoDB 和 Ruby。