MongoDBでゆるふわDB体験

第3回 MongoDBのクエリを使いこなそう

この記事を読むのに必要な時間:およそ 6 分

RubyからMongoDBを触ってみる

MongoDBではアプリケーションから利用するためのたくさんのドライバが用意されています。公式ドキュメントのDriversのページに一覧があります。今回は,その中の1つであるRubyのドライバを触ってみましょう。

[準備]

以下の環境を前提としています。

  • Ruby 1.9.3-p194
  • MongoDB 2.2.1

MongoDBのRubyドライバのインストール

MongoDBのRubyドライバは,Rubyのパッケージ管理システムであるgemに登録されているため,gemコマンドで簡単にインストールすることができます。

以下のコマンドでRubyドライバをインストールします。

# gem install mongo

また,BSONシリアライズのために,以下のbsonのgemもインストールする必要があります。

# gem install bson
[注意]
version 0.2より前のRubyドライバでは,BSONシリアライズ機能は同梱されているため,bsonのgemのインストールは不要です。

BSONシリアライズの性能を向上させたい場合は,以下のbson_extをインストールするとよいでしょう。C言語で書かれているため,性能が向上します。

# gem install bson_ext

インタラクティブモード(irb)で触ってみる

irbはrubyのインタラクティブモードです。対話的にrubyを実行できます。まずはこれでMongoDBとの接続を試してみましょう。

irbを起動します。

$ irb

MongoDB-Ruby-Driverをロードします。

irb> require 'mongo'
=> true

MongoDBに接続します。

irb> con = Mongo::Connection.new
=> #<Mongo::Connection:0x89422c8 @host_to_try=["localhost", 27017],・・・

データベースを指定して変数に格納します。

irb> db = con.db("school_exam")
=> #<Mongo::DB:0x893f30c @name="school_exam", ・・・

コレクションを指定して変数に格納します。

irb> coll = db["students"]
=> #<Mongo::コレクション:0x8620e64 @name="students", ・・・

コレクションにドキュメントを挿入します。

irb> coll.insert( { "name" => "fujisaki", "age" => 31  } )
=> BSON::ObjectId('50c7732f3e91d8106f000001')
irb> coll.insert( { "name" => "watanabe", "age" => 30  } )
=> BSON::ObjectId('50c773403e91d8106f000002')
irb> coll.insert( { "name" => "hayashida", "age" => 24 } )
=> BSON::ObjectId('50c773453e91d8106f000003')

コレクションの中のドキュメントの数を調べます。

irb> coll.count
=> 3

コレクションの中のドキュメントをRubyの配列に変換します。

irb> coll.find.to_a
=> [{"_id"=>BSON::ObjectId('50c7732f3e91d8106f000001'), "name"=>"fujisaki",  "age"=>31},
    {"_id"=>BSON::ObjectId('50c773403e91d8106f000002'), "name"=>"watanabe",  "age"=>30},
    {"_id"=>BSON::ObjectId('50c773453e91d8106f000003'), "name"=>"hayashida", "age"=>24}]

コレクションの中のドキュメントをループで回します。

irb> coll.find.each { |doc|
irb*    p doc
irb*    p doc["name"]
irb*    p doc["age"]
irb* }
{"_id"=>BSON::ObjectId('50c7732f3e91d8106f000001'), "name"=>"fujisaki", "age"=>31}
"fujisaki"
31
{"_id"=>BSON::ObjectId('50c773403e91d8106f000002'), "name"=>"watanabe", "age"=>30}
"watanabe"
30
{"_id"=>BSON::ObjectId('50c773453e91d8106f000003'), "name"=>"hayashida", "age"=>24}
"hayashida"
24
=> nil

キー"name"に値"wata"を含むドキュメントを正規表現で検索します。

irb> coll.find( { "name" => /wata/ } ).to_a
=> [{"_id"=>BSON::ObjectId('50c773403e91d8106f000002'), "name"=>"watanabe", "age"=>30}]
[注意]
最後にto_aを付けて配列化している理由は,to_aをつけないとカーソルのオブジェクトが返ってきて,中身が見えないためです。

_idを指定して検索します。_idは環境によって変わるため、値を変更してください。以下は筆者の環境で実施した結果です。

irb> coll.find( { "_id"  => BSON::ObjectId('4f7b20b4e4d30b35c9000049') } ).to_a
=> [{"_id"=>BSON::ObjectId('50c7732f3e91d8106f000001'), "name"=>"fujisaki", "age"=>31}]
[注意]
MongoDBの_idは単なる文字列ではないため,_idを格納するBSONオブジェクトを作成して,問い合わせる必要があります。

キーが"age"の値でソートします。

irb> coll.find.sort("age").to_a
=> [{"_id"=>BSON::ObjectId('50c773453e91d8106f000003'), "name"=>"hayashida", "age"=>24},
    {"_id"=>BSON::ObjectId('50c773403e91d8106f000002'), "name"=>"watanabe", "age"=>30},
    {"_id"=>BSON::ObjectId('50c7732f3e91d8106f000001'), "name"=>"fujisaki", "age"=>31}]

キー"name"の値が"fujisaki"であるドキュメントのキー"age"の値を更新します。

irb> coll.update( {"name" => "fujisaki"} , {"$set" => {"age" => "32"} } )
=> 88

※88 という返り値はsocket上で送信されるパケットサイズ(単位はbyte)です。環境によって変わります。詳細は省きますが、気になる人はgithubからドライバのソースを見てみましょう。

mongo-ruby-driver/lib/mongo/networking.rb

キー"name"の値が"fujisaki"であるドキュメントを削除します。

irb> coll.remove("name" => "fujisaki")
=> true

すべてのドキュメントを削除します。

irb> coll.remove()
=> true

Rubyのソースコードを書いてみる

基本的にはirbで実行したコマンドを列挙すればOKです。ソースコードは以下のようになります。

require 'mongo'

con = Mongo::Connection.new #MongoDBに接続
db = con.db("school_exam") #DBを指定
coll = db["students"] #コレクションを指定

#挿入
coll.insert( { "name" => "fujisaki", "age" => 31 } )
coll.insert( { "name" => "watanabe", "age" => 30 } )
coll.insert( { "name" => "hayashida", "age" => 24 } )

#コレクションの中のドキュメントの数
p coll.count

#コレクションの中のドキュメントをRubyの配列に変換
p coll.find().to_a

#コレクションの中のドキュメントをループで回す
coll.find.each { |doc|
  p doc
  p doc["name"]
  p doc["age"]
}

#キー"name"に値"wata"を含むドキュメントを正規表現で検索
p coll.find( { "name" => /wata/ } ).to_a

#IDを指定して参照
#_idは環境によって変わるためここではコメントアウトします
#p coll.find( { "_id"  => BSON::ObjectId('xxxx') } ).to_a

#キーが"age"の値でソート
p coll.find.sort("age").to_a

#キー"name"の値が"fujisaki"であるドキュメントのキー"age"の値を更新
coll.update( {"name" => "fujisaki"} , {"$set" => {"age" => "32"} } )

#キー"name"の値が"fujisaki"であるドキュメントの削除
coll.remove("name" => "fujisaki")

#すべて削除
#MongoDB shellから確認するため、コメントアウトします
#coll.remove()

このソースコードをmongo_sample.rbとして保存してください。実行すると以下のような出力になります。

$ ruby mongo_sample.rb
3
[{"_id"=>BSON::ObjectId('50c979fdbb050f12bc000001'), "name"=>"fujisaki", "age"=>31}, {"_id"=>BSON::ObjectId('50c979fdbb050f12bc000002'), "name"=>"watanabe", "age"=>30}, {"_id"=>BSON::ObjectId('50c979fdbb050f12bc000003'), "name"=>"hayashida", "age"=>24}]
{"_id"=>BSON::ObjectId('50c979fdbb050f12bc000001'), "name"=>"fujisaki", "age"=>31}
"fujisaki"
31
{"_id"=>BSON::ObjectId('50c979fdbb050f12bc000002'), "name"=>"watanabe", "age"=>30}
"watanabe"
30
{"_id"=>BSON::ObjectId('50c979fdbb050f12bc000003'), "name"=>"hayashida", "age"=>24}
"hayashida"
24
[{"_id"=>BSON::ObjectId('50c979fdbb050f12bc000002'), "name"=>"watanabe", "age"=>30}]
[{"_id"=>BSON::ObjectId('50c979fdbb050f12bc000003'), "name"=>"hayashida", "age"=>24}, {"_id"=>BSON::ObjectId('50c979fdbb050f12bc000002'), "name"=>"watanabe", "age"=>30}, {"_id"=>BSON::ObjectId('50c979fdbb050f12bc000001'), "name"=>"fujisaki", "age"=>31}]

シェルを起動させてfindしてみましょう。rubyからinsertできてますか!?

$ mongo 
MongoDB shell version: 2.2.1
connecting to: test
> use school_exam
switched to db school_exam
> db.students.find() //結果はみなさんの環境で確認してみてください

次回のテーマ

今回はMongoDBクエリ言語を説明しました。実際にコマンドを打ってみて,ドキュメントをinsertしfindすることで理解が早くなるかと思います。文中でも紹介しましたが,www.mongodb.org「TRY IT OUT」からOnline Shellが使えますので,まだMongoDBを触ったことがない方はぜひ試してみてください。

次回は,MongoDBのスケールアウトの仕組みであるShardingについて説明します。多くのコマンドを紹介して,実際にSharding環境を構築するまでの手順を記載する予定です。

著者プロフィール

藤崎祥見(ふじさきしょうけん)

野村総合研究所 OpenStandia所属。オープンソースのR&Dとセミナー講師を担当。

Debian,Ubuntu,Liferayのコミュニティで活動した後,MongoDBの翻訳に関わり,丸の内MongoDB勉強会を始める。

実家がお寺で,住職の資格を所持する坊主系エンジニア。

Twitter:@syokenz


渡部徹太郎(わたなべてつたろう)

野村総合研究所 OpenStandia所属。オープンソースを使ったSIやサポートの業務に従事。

藤崎と共同で丸の内MongoDB勉強会を始める。

趣味は自宅サーバ。好きなものはLinuxとRuby。

Twitter:@fetarodc


林田敦(はやしだあつし)

野村総合研究所 OpenStandia所属。オープンソースを使ったSIや製品開発業務に従事。

丸の内MongoDB勉強会では広報兼雑用係を務める。

趣味はレザークラフト,ダイビング,スキー,キャンプ,ジェットスキー,カメラ等々。作って滑って撮って潜れるエンジニア。

Facebook:Atsushi Hayashida

コメント

  • 正規表現による絞り込み検索について

    正規表現によるドキュメントの絞り込み検索のコマンド書式として、 db.col1.find( { "name" => /mongo/ } ) のような例が掲載されていますが、正しくは db.col1.find( { "name" : /mongo/ } ) となるべきではないでしょうか。

    http://docs.mongodb.org/manual/reference/operator/query/regex/#op._S_regex

    Commented : #1  Kensuke Nagae (2013/10/28, 15:48)

コメントの記入