# MongoDB Ruby 教程

> 原文： [http://zetcode.com/db/mongodbruby/](http://zetcode.com/db/mongodbruby/)

在本教程中，我们展示了如何在 Ruby 中使用 MongoDB。 在 ZetCode 上有一个简洁的 [Ruby 教程](/lang/rubytutorial/)。

[Tweet](https://twitter.com/share) 

MongoDB 是 NoSQL 跨平台的面向文档的数据库。 它是可用的最受欢迎的数据库之一。 MongoDB 由 MongoDB Inc.开发，并作为免费和开源软件发布。

MongoDB 中的记录是一个文档，它是由字段和值对组成的数据结构。 MongoDB 文档与 JSON 对象相似。 字段的值可以包括其他文档，数组和文档数组。 MongoDB 将文档存储在集合中。 集合类似于关系数据库中的表以及行中的文档。

## 安装 MongoDB

以下命令可用于在基于 Debian 的 Linux 上安装 MongoDB。

```ruby
$ sudo apt-get install mongodb

```

该命令将安装 MongoDB 随附的必要包。

```ruby
$ sudo service mongodb status
mongodb start/running, process 975

```

使用`sudo service mongodb status`命令，我们检查`mongodb`服务器的状态。

```ruby
$ sudo service mongodb start
mongodb start/running, process 6448

```

`mongodb`服务器由`sudo service mongodb start`命令启动。

```ruby
$ sudo gem install mongo

```

MongoDB Ruby 驱动程序通过`sudo gem install mongo`命令安装。

## 建立数据库

`mongo`工具是 MongoDB 的交互式 JavaScript Shell 界面，它为系统管理员提供了一个界面，并为开发者提供了一种直接测试数据库查询和操作的方法。

```ruby
$ 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`

```ruby
#!/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`数据库并检索其所有集合。

```ruby
require 'mongo'

```

我们包含`mongo`驱动程序。

```ruby
Mongo::Logger.logger.level = ::Logger::FATAL

```

默认日志记录级别为`::Logger::DEBUG`，其中包含许多调试信息。 为了使输出更具可读性，我们选择`::Logger::FATAL`调试级别。

```ruby
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'testdb')

```

`Mongo::Client`用于连接到 MongoDB 服务器。 我们指定 URL 和数据库名称。 27017 是 MongoDB 服务器监听的默认端口。

```ruby
client.collections.each { |coll| puts coll.name }

```

我们遍历集合列表，并将其名称打印到控制台。

```ruby
`client.close`

```

最后，我们关闭连接。 通常，不建议应用调用`close`。 连接很昂贵，正在重用。 但是，由于它是一个一次性程序，而不是长时间运行的可重用连接的应用，因此我们确实调用了该方法。

```ruby
$ ./list_collections.rb 
test
cars

```

这是`list_collections.rb`程序的示例输出。

## 服务器选择超时

`:server_selection_timeout`是选择操作服务器的超时时间（以秒为单位）。 当我们无法连接到数据库服务器时，将引发`Mongo::Error::NoServerAvailable`。

`server_selection_timeout.rb`

```ruby
#!/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

```

该示例的端口号错误。 默认情况下，服务器选择超时为三十秒。 我们将其设置为五秒钟。

```ruby
rescue Mongo::Error::NoServerAvailable => e

```

未建立连接且超时已过期时，抛出`Mongo::Error::NoServerAvailable`。

```ruby
$ ./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`

```ruby
#!/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`方法用于执行命令。

```ruby
db = client.use("testdb")

```

`use`方法选择`testdb`数据库。

```ruby
db.command({"dbstats" => 1}).documents[0].each do |key, value|

    puts "#{key}: #{value}"
end

```

`command`方法执行`dbstats`命令并解析返回的哈希值。

```ruby
$ ./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`

```ruby
#!/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`集合的所有数据。

```ruby
client[:cars].find.each { |doc| puts doc }

```

传递空查询将返回所有文档。 我们使用`each`方法遍历`:cars`集合的文档。

```ruby
$ ./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`

```ruby
#!/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`集合中的文档数。

```ruby
docs = client[:cars].find

```

我们从`cars`集合中检索所有文档。

```ruby
puts "There are #{docs.count} documents"

```

我们打印退回文档的数量。

```ruby
$ ./count_documents.rb 
There are 8 documents

```

`cars`集合中有八个文档。

## 读取文档

`find`方法采用可选的`filter`参数，该参数用于过滤传入的数据。

`read_one.rb`

```ruby
#!/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`集合中读取一个文档。

```ruby
client[:cars].find(:name => 'Volkswagen').each do |doc|

```

`find`方法仅显示包含大众汽车的文档。

```ruby
$ ./read_one.rb 
{"_id"=>8, "name"=>"Volkswagen", "price"=>21600}

```

这是示例的输出。

## 查询运算符

可以使用 MongoDB 查询运算符（例如`$gt`，`$lt`或`$ne`）过滤数据。

`read_op.rb`

```ruby
#!/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 的文档。

```ruby
puts client[:cars].find("price" => {'$lt' => 30000}).to_a

```

`$lt`运算符用于获取价格低于 30,000 的汽车。

```ruby
$ ./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`

```ruby
#!/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`运算符。

```ruby
puts client[:cars].find('$or' => [{:name => "Audi"}, {:name => "Skoda" }]).to_a

```

`$or`运算符用于返回名称为 Audi 或 Skoda 的文档。

```ruby
puts client[:cars].find('$and' => [{:price => {'$gt' => 20000}}, 
    {:price => {'$lt' => 50000 }}]).to_a

```

`$and`运算符检索价格介于 20,000 和 50,000 之间的汽车。

```ruby
$ ./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`

```ruby
#!/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`字段。

```ruby
cursor = client[:cars].find({}, { :projection => {:_id => 0} })

```

我们在`find`方法的第二个参数中指定`:projection`选项。

```ruby
$ ./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`

```ruby
#!/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`集合中读取，跳过前两个文档，并将输出限制为五个文档。

```ruby
docs = client[:cars].find().skip(2).limit(5)

```

`skip`方法跳过前两个文档，`limit`方法将输出限制为五个文档。

```ruby
$ ./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`

```ruby
#!/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 }

```

该示例计算集合中所有汽车的价格。

```ruby
agr = [{"$group" => {:_id => 1, :all => { "$sum" => "$price" } }}];

```

`$sum`运算符计算并返回数值的总和。 `$group`运算符通过指定的标识符表达式对输入文档进行分组，并将累加器表达式（如果指定）应用于每个组。

```ruby
client[:cars].aggregate(agr).each { |doc| puts doc }

```

`aggregate`方法将聚合操作应用于`cars`集合。

```ruby
$ ./sum_all_cars.rb 
{"_id"=>1, "all"=>609727}

```

所有价格的总和是 619,369。

我们可以使用`$match`运算符来选择要汇总的特定汽车。

`sum_two_cars.rb`

```ruby
#!/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`

```

该示例计算奥迪和沃尔沃汽车的价格总和。

```ruby
agr = [{"$match" => {"$or" => [ { :name => "Audi" }, { :name => "Volvo" }]}}, 
       {"$group" => {:_id => 1, :sumOfTwo => { "$sum" => "$price" } }}];

```

该表达式使用`$match`，`$or`，`$group`和`$sum`运算符执行任务。

```ruby
$ ./sum_two_cars.rb 
{"_id"=>1, "sumOfTwo"=>81000}

```

两辆车的价格之和为 81,642。

## 插入文档

`insert_one`方法将单个文档插入到集合中。

`insert_doc.rb`

```ruby
#!/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`集合。

```ruby
doc = { :_id => 9, :name => "Toyota", :price => 37600 } 

```

这是要插入的文档。

```ruby
client[:cars].insert_one doc 

```

`insert_one`方法将文档插入到集合中。

```ruby
> db.cars.find({_id:9})
{ "_id" : 9, "name" : "Toyota", "price" : 37600 }

```

我们用`mongo`工具确认插入。

## 插入许多文档

`insert_many`方法将多个文档插入一个集合中。

`create_collection.rb`

```ruby
#!/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`

```

该示例创建一个大洲集合并将六个文档插入其中。

```ruby
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`，这是用于标识文档的唯一值，而不是整数。

```ruby
puts "#{result.inserted_count} documents were inserted"

```

返回结果的`inserted_count`给出成功插入的文档数。

```ruby
> 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`

```ruby
#!/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 的价格。

```ruby
client[:cars].delete_one({:name => "Skoda"})

```

`delete_one`删除`Skoda`的文档。

```ruby
client[:cars].update_one({:name => "Audi"}, '$set' => {:price => 52000})

```

通过`update_one`方法将 Audi 的价格更改为 52,000。 `$set`运算符用于更改价格。

```ruby
> 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。