はじめに
本記事では、
本記事の構成は、
Mongoクエリ言語の特徴
Mongoクエリ言語には以下の特徴があります。
- コレクションの各メソッドを用いてCRUD操作をします
- メソッドの引数にはJSON形式のデータを渡します
- 変数が使えます
- 制御構造が使えます
- カーソルが使えます
- ドキュメントの要素に簡単にアクセスができます
Mongoクエリ言語はMongoDBのコマンドラインインターフェースであるMongoDBシェルから使用できます。シェルは以下のコマンドから起動します。
$ mongo MongoDB shell version: 2.2.1 connecting to: test >
コレクションの各メソッドを用いてCRUD操作をします
たとえば、
> db.col1.insert( { "name" : "mongo" } )更新、
メソッドの引数にはJSON形式のデータを渡します
データを挿入する場合には、
> db.col1.insert( { "string" : "hello" , "array" : [ "sun", "mon", "tue" ] } )参照する場合は、
> db.col1.find( { "name" => /mongo/ } )変数が使えます
MongoDBのシェルではJavaScriptの文法で、
> data = { "string" : "hello" , "array" : [ "sun", "mon", "tue" ] }
> db.col1.insert( data )
> condition = { "name" => /mongo/ }
> db.col1.find( condition )制御構造が使えます
MongoDBのシェルではJavaScriptの制御構造が使えます。たとえば、
> for (var i = 1; i <= 20; i++) db.col1.insert( { x : 4 , j : i } )カーソルが使えます
検索結果に対するカーソルを定義して、
> var c = db.col1.find() > while ( c.hasNext() ) printjson( c.next() )
ドキュメントの要素に簡単にアクセスができます
たとえば以下のようなドキュメントを挿入したとします。
> db.col1.insert( { "key1" : "hello" , "array1" : [ "sun", "mon", "tue" ] } )すると、
> doc=db.col1.findOne() // <- ドキュメントを変数に格納 > printjson(doc["key1"]) // <- キー"key1"の値を表示 "hello" > printjson(doc["array1"][2]) // <- キーarrayの配列の2番目の要素を表示 "tue"
MongoDBのデータ構造
MongoDBでは、
RDBとMondoDBのデータ構造の比較
MongoDBのデータ構造とRDBのデータ構造の比較を以下の表に記載します。
| RDBでの呼称 | MongoDBでの呼称 |
|---|---|
| database | database |
| table | collection |
| row | document |
| column | field |
| index | index |
| primary key | _id field |
※ MongoDBでは、
RDBとMongoDBの大きな違いは、
しかし、
この特徴を生かして、
MySQLと比較したクエリ
それでは、
以下に、
データベース操作
| MySQL | MongoDB |
|---|---|
| > SHOW DATABASES; | > show dbs |
| > USE testdb; | > use testdb |
| > CREATE DATABASE testdb; | useコマンドで自動生成 |
| > DROP DATABASE testdb; | > use testdb > db.dropDatabase() |
| > DESC testdb; | なし |
[参考]
useコマンドでデータベースを選択するだけでは、コレクション操作
MongoDBのコレクションは、
| MySQL | MongoDB |
|---|---|
| > CREATE TABLE products (...); | 自動生成 または > db.createCollection("products") |
| > ALTER TABLE products ADD ...; | 自動生成 |
| > SHOW TABLES; | > show collections |
| > DROP TABLE products; | > db. |
| > TRUNCATE TABLE products; | > db. |
remove()はコレクションが残ります。drop()はコレクションごと削除します。
show collectionsで確認できます。
> db.products.remove() //コレクションの中のすべてのオブジェクトを削除します > show collections //確認、productsはまだある > db.products.drop() //コレクションを削除します > show collections //確認、productsはない
[参考]
コレクションは、ドキュメント操作
MongoDBのドキュメントは、
選択
| MySQL | MongoDB |
|---|---|
| > SELECT * FROM products; | > db. |
| > SELECT name FROM products WHERE category='A' AND stock ORDER BY name; | > db. |
| > SELECT COUNT(*) FROM products; | > db. |
| > SELECT COUNT(*) FROM products WHERE name LIKE "%camera%" OR category='C'; | > db. |
| > SELECT COUNT(stock) FROM products; | > db. |
| > SELECT * FROM products LIMIT 1; | > db. |
| > SELECT * FROM products LIMIT 5; | > db. |
MongoDBでは、
> it
一度にすべてのドキュメントを表示したい場合は、
> db.products.find().toArray() または > db.products.find().toArray().forEach(printjsononeline)
なお、
> DBQuery.shellBatchSize = 100
挿入
| MySQL | MongoDB |
|---|---|
| > INSERT INTO products VALUES('A',"camera case",5); | > db. |
更新
| MySQL | MongoDB |
|---|---|
| > UPDATE products SET stock=10 WHERE category='A'; | > db. |
| > UPDATE products SET stock=stock+10 WHERE category='A'; | > db. |
update(condition, operation, upsert, multi)
upsert:trueのとき、
multi :trueのとき、
削除
| MySQL | MongoDB |
|---|---|
| > DELETE FROM products WHERE name="camera case"; | > db. |
以上のように、
次のページでは、
Mongoクエリ言語を使ってみよう
それでは、
MongoDBの実行環境がローカルに無い場合でも、
想定する利用シーン
今回は、
テスト結果の入力(国語)
はじめに、
| name | subject | score |
|---|---|---|
| fujisaki | Japanese | 80 |
| watanabe | Japanese | 70 |
| hayashida | Japanese | 60 |
今回利用するデータベース名は、
データベース"school_
> use school_exam
switched to db school_exam
> db.scores.insert({ name: "fujisaki", subject: "Japanese", score: 80 })
> db.scores.insert({ name: "watanabe", subject: "Japanese", score: 70 })
> db.scores.insert({ name: "hayashida", subject: "Japanese", score: 60 })
>
入力結果を確認します。
> db.scores.find()
{ "_id": ObjectId("50bf552cb085f3c09c23da5c"), "name": "fujisaki", "subject": "Japanese", "score": 80 }
{ "_id": ObjectId("50bf552cb085f3c09c23da5d"), "name": "watanabe", "subject": "Japanese", "score": 70 }
{ "_id": ObjectId("50bf552cb085f3c09c23da5e"), "name": "hayashida", "subject": "Japanese", "score": 60 }
>
入力したドキュメントすべてに自動的に"_id"フィールドが追加され、
> db.scores.find({},{ "_id": 0 })
{ "name": "fujisaki", "subject": "Japanese", "score": 80 }
{ "name": "watanabe", "subject": "Japanese", "score": 70 }
{ "name": "hayashida", "subject": "Japanese", "score": 60 }
>
"_id"以外のフィールドのみ取得することができました。
テスト結果の入力(数学)
次に、
| name | subject | score |
|---|---|---|
| fujisaki | Mathematics | 70 |
| watanabe | Mathematics | 80 |
| hayashida | Mathematics | 30 |
3つのドキュメントを入力して、
> db.scores.insert({ name: "fujisaki", subject: "Mathematics", score: 70 })
> db.scores.insert({ name: "watanabe", subject: "Mathematics", score: 80 })
> db.scores.insert({ name: "hayashida", subject: "Mathematics", score: 0 })
>
> db.scores.find({},{ "_id": 0 })
{ "name": "fujisaki", "subject": "Japanese", "score": 80 }
{ "name": "watanabe", "subject": "Japanese", "score": 70 }
{ "name": "hayashida", "subject": "Japanese", "score": 60 }
{ "name": "fujisaki", "subject": "Mathematics", "score": 70 }
{ "name": "watanabe", "subject": "Mathematics", "score": 80 }
{ "name": "hayashida", "subject": "Mathematics", "score": 0 }
>
"subject"フィールドが"Mathematics"と一致するドキュメントのみ表示します。
> db.scores.find({ "subject": "Mathematics" },{ "_id": 0 })
{ "name": "fujisaki", "subject": "Mathematics", "score": 70 }
{ "name": "watanabe", "subject": "Mathematics", "score": 80 }
{ "name": "hayashida", "subject": "Mathematics", "score": 0 }
>
よく見ると、
まず、
> db.scores.find({ "subject": "Mathematics" })
{ "_id": ObjectId("50bf5765b085f3c09c23da6d"), "name": "fujisaki", "subject": "Mathematics", "score": 70 }
{ "_id": ObjectId("50bf5769b085f3c09c23da6e"), "name": "watanabe", "subject": "Mathematics", "score": 80 }
{ "_id": ObjectId("50bf576eb085f3c09c23da6f"), "name": "hayashida", "subject": "Mathematics", "score": 0 }
>
対象ドキュメントのObjectIdは、
> db.scores.update(
{ "_id": ObjectId("50bf576eb085f3c09c23da6f") },
{ $set : { "score": 30 } }, false, true
)
>
> db.scores.find({ "subject": "Mathematics" },{ "_id": 0 })
{ "name": "fujisaki", "subject": "Mathematics", "score": 70 }
{ "name": "watanabe", "subject": "Mathematics", "score": 80 }
{ "name": "hayashida", "subject": "Mathematics", "score": 30 }
>
正しく更新ができました。
次に、
> db.scores.update(
{ "name": "hayashida", "subject": "Mathematics" },
{ $set : { "score": 30 } }, false, true
)
>
> db.scores.find({ "subject": "Mathematics" },{ "_id": 0 })
{ "name": "fujisaki", "subject": "Mathematics", "score": 70 }
{ "name": "watanabe", "subject": "Mathematics", "score": 80 }
{ "name": "hayashida", "subject": "Mathematics", "score": 30 }
>
こちらの方法でも、
テスト結果の入力(英語)
最後に入力するはずだった英語の先生は、
| name | subject | score |
|---|---|---|
| fujisaki | English | 0~100(ランダム) |
| watanabe | English | 0~100(ランダム) |
| hayashida | English | 0~100(ランダム) |
第1回 使ってみようMongoDBでも軽く触れましたが、
> students = [ "fujisaki", "watanabe", "hayashida" ] > for(var i=0; i
JavaScriptを使って、
ところで、
ObjectIdにより削除する方法もありますが、
> db.scores.remove({ "name": "hayashida" , "subject": "English" })
>
> db.scores.find({ "subject": "English" },{ "_id": 0 })
{ "name": "fujisaki", "subject": "English", "score": 97 }
{ "name": "watanabe", "subject": "English", "score": 73 }
>
正しく削除できました。
以上で、
集計処理
最後に、
集計処理には、
まずは、
> db.scores.aggregate(
{ $project: { "subject": 1, "score": 1 } },
{ $group : { "_id" : "$subject",
"average": { "$avg": "$score" } } }
)
{ "result": [
{ "_id": "English", "average": 85 },
{ "_id": "Mathematics", "average": 60 },
{ "_id": "Japanese", "average": 70 }
],
"ok": 1
}
※"_id"はSQLのGROUP BY句での集計キーに相当します。
次に、
> db.scores.aggregate(
{ $sort : { "subject": 1, "score": -1 } },
{ $group: { "_id" : "$subject",
"top-ranker" : {"$first": "$name" },
"high-score" : {"$first": "$score" } } }
)
{ "result": [
{ "_id": "English", "top-ranker": "fujisaki", "high-score": 97 },
{ "_id": "Mathematics", "top-ranker": "watanabe", "high-score": 80 },
{ "_id": "Japanese", "top-ranker": "fujisaki", "high-score": 80 }
],
"ok": 1
}
※他に、
※$sortのキー"score"の値である"-1"は、
次に、
> db.scores.aggregate(
{ $sort : { "subject": 1, "score": -1 } },
{ $group: { "_id" : "$subject",
"bottom-ranker": {"$last": "$name" },
"low-score" : {"$last": "$score" } } }
)
{ "result": [
{ "_id": "English", "bottom-ranker": "watanabe", "low-score": 73 },
{ "_id": "Mathematics", "bottom-ranker": "hayashida", "low-score": 30 },
{ "_id": "Japanese", "bottom-ranker": "hayashida", "low-score": 60 }
],
"ok": 1
}
※他に、
最後に、
> db.scores.aggregate(
{ $project: { "name": 1, "score": 1 } },
{ $group : { "_id" : "$name",
"average": { "$avg": "$score" }
} }
)
{ "result": [
{ "_id": "hayashida", "average": 45 },
{ "_id": "watanabe", "average": 74.33333333333333 },
{ "_id": "fujisaki", "average": 82.33333333333333 }
],
"ok": 1
}
次のページでは、
RubyからMongoDBを触ってみる
MongoDBではアプリケーションから利用するためのたくさんのドライバが用意されています。公式ドキュメントのDriversのページに一覧があります。今回は、
[準備]
以下の環境を前提としています。
- Ruby 1.
9.3-p194 - MongoDB 2.
2.1
MongoDBのRubyドライバのインストール
MongoDBのRubyドライバは、
以下のコマンドでRubyドライバをインストールします。
# gem install mongo
また、
# gem install bson
[注意]
version 0.
BSONシリアライズの性能を向上させたい場合は、
# 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__idを指定して検索します。_idは環境によって変わるため、値を変更してください。以下は筆者の環境で実施した結果です。
irb> coll.find( { "_id" => BSON::ObjectId('4f7b20b4e4d30b35c9000049') } ).to_a
=> [{"_id"=>BSON::ObjectId('50c7732f3e91d8106f000001'), "name"=>"fujisaki", "age"=>31}][注意]
MongoDBの_idは単なる文字列ではないため、キーが"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上で送信されるパケットサイズ
キー"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_
$ 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クエリ言語を説明しました。実際にコマンドを打ってみて、
次回は、


