Rails3 対応 MongoDB ORM、Mongoid 詳解―インストール
まずは、インストールの解説です。
$ gem install mongoid --pre
Rails3 対応の最新版は執筆時点で 2.0.0.beta.16 で、ベータ版なので、--pre オプションを使ってインストールします。2.0.0 リリースの際には、--pre を取って下さい。
そして、mongo ドライバー自体のパフォーマンスを上げるために、bson_ext をインストールしましょう。
$ gem install bson_ext
bson_ext には、boost が必要です。MongoDB のインストール時にインストールされていると思いますので、問題ないと思います。もし、インストールできなくても動きますので、気にしないで下さい。
Rails 3 でのインストール
Rails3 は Bundler を使用しており、必要な Gem を、Gemfile というファイルに書くことになります。
gem "mongoid", "2.0.0.beta.16" gem "bson_ext", "1.0.4"
2.0.0 リリースの暁には、バージョンの部分を描き直してください。
$ bundle install vendor/bundle
で、vendor/bundle 以下に、Mongoid や依存している Gem がインストールされ、Rails が使用します。Git を使用している方は、.gitignore に vendor/bundle を記入するのを忘れないようにしましょう。
Gemfile を書き換えた後、アップデートをかけるには、
$ bundle update
とします。
設定
Mongoid の設定をするためには、次のコマンドを打ちます。
$ rails generate mongoid:config
すると、config/mongoid.yml が生成され、デフォルトの ORM が Mongoid になるので、rails generate model Foo などとした際に、Mongoid のモデルが作成できるようになります。
それでは、config/mongoid.yml を、自分の環境に合わせて書き換えましょう。
defaults: &defaults host: localhost slaves: - host: slave1.local port: 27018 - host: slave2.local port: 27019 autocreate_indexes: false allow_dynamic_fields: true include_root_in_json: false parameterize_keys: true persist_in_safe_mode: false raise_not_found_error: true reconnect_time: 3 development: <<: *defaults database: hoge_development test: <<: *defaults database: hoge_test # set these environment variables on your prod server production: <<: *defaults host: <%= ENV['MONGOID_HOST'] %> port: <%= ENV['MONGOID_PORT'] %> username: <%= ENV['MONGOID_USERNAME'] %> password: <%= ENV['MONGOID_PASSWORD'] %> database: <%= ENV['MONGOID_DATABASE'] %>
host はそのままですね、接続する MongoDB が動いているホストです。
port が省略されていますが、デフォルトでは 27017 番になります。
slaves の項は、スレイブのサーバーがある時です、開発用にはないと思うので、削除したら良いです。
その他のパラメーターは以下を意味します。
autocreate_indexes | デフォルトは false です。true にした場合は、モデルクラスがロードされた際に毎回インデックスが付与されます。 development や test 以外の環境では推奨されません。 |
allow_dynamic_fields | モデルクラスにフィールドとして設定されてない属性が呼び出されたときに、オブジェクトに属性を付け加えます。デフォルトは true で、false にした場合は、フィールドとして設定されてない属性に値をセットしたときに、例外があがります。スキーマレスな MongoDB ならではの機能です。 |
include_root_in_json | デフォルトは false です。true にした場合は、モデルで #to_json が呼ばれたときに、JSONのトップレベルにドキュメント名が付き、関連のそれぞれのトップレベルにもドキュメント名が付きます。JSON API を作る際に変更が必要になるかも知れません。 |
parameterize_keys | キーの特殊文字をSEOフレンドリーな文字へと置換します。デフォルトは true です。 |
persist_in_safe_mode | 全てのデータベース操作をセーフモードで行ないます。MongoDB の採用している GridFS の機能で、データを保存する際、クライアントとサーバでそれぞれMD5ハッシュを生成し、一致しなかった場合は例外を上げます。デフォルトは false です。true にした場合はパフォーマンスが落ちますので気をつけて下さい。 |
raise_not_found_error | id で検索した際に、ドキュメントがなかった場合に、Mongoid::Error::DocumentNotFound 例外を上げます。デフォルトは true です。false にした場合は、例外を上げずに nil を返します。 |
reconnect_time | データベースとつながらない際に、再接続を試みる最大時間を設定します。デフォルトは3秒です。 |
skip_version_check | MongoHQ や MongoMachine などを使用しているときに、システムのコレクションが接続を許さないせいで、認証の問題が起きた場合に true にして下さい。 |
ログ
Rails のデフォルトのログ機能を使いたくない場合に、独自のログ機能を設定できます。
config/application.rb
module Hoge class Application < Rails::Application ... config.mongoid.logger = Logger.new($stdout, :warn) ... end end
ActiveRecord の削除
これから Mongoid を使用するので、ActiveRecord はロードする必要がありません。
config/application.rb の先頭部分を以下のように書き換えます。
#require 'rails/all' require "action_controller/railtie" require "action_mailer/railtie" require "active_resource/railtie"
他言語
デフォルトで Mongoid は、英語の国際化ファイルが追加されております。他の言語を追加するには、config/initializers/mongoid.rb を作成し以下の内容を追加します。
# adds Spanish Mongoid.add_language("es")
現在のところ、以下の言語に対応しています。
- es: スペイン語
- fr: フランス語
- it: イタリア語
- pl: ポーランド語
- pt: ポルトガル語
- sv: スウェーデン語
これら全てを有効にしたい場合は、
# include all language that Mongoid knows about Mongoid.add_language("*")
とします。
Rails 3 以外の設定
Mongoid.configure ブロックを使用して設定することができます。
Mongoid.configure do |config| dbname = "hoge_development" host = "localhost" config.master = Mongo::Connection.new.db(dbname) config.slaves = [ Mongo::Connection.new(host, 27018, :slave_ok => true).db(dbname) ] config.persist_in_safe_mode = false end
YAML ファイル、config/mongoid.yml を作成してロードすることもできます。
file_name = File.join(File.dirname(__FILE__), "..", "config", "mongoid.yml") @settings = YAML.load(ERB.new(File.new(file_name).read).result) Mongoid.configure do |config| config.from_hash(@settings[ENV['RACK_ENV']]) end
インストールについては以上になります。
Rails3 対応 MongoDB ORM、Mongoid 詳解―前説
つい先日 1.6.0 がリリースされ、MongoDB の時代がいよいよキタ!って感じです。MongoDB 自体のインストール・操作などは下記の記事を参考にしてください。
この記事では、Rails3 にふさわしい ORM として、Mongoid を紹介します。
MongoDB の Ruby ドライバーは、mongo ですが、これは素のドライバーで、Rails などと使用するときは、クラス・オブジェクトに自動でマッピングしてくれる ORM を使用したいところです。
その候補として、
があります、
この内、プロダクションで使われているのは、MongoMapper と Mongoid です。Rails2 では、MongoMapper がよく使われていて、他のプラグインの対応も良かったようですが、Rails3 においては対応が遅れ気味ですし、インターフェイスも Rails2 時代の古いものです。最大の弱点はドキュメントが貧弱なことです。
その点、Mongoid は、Rails3 対応に向けて活発に開発が進んでおり、インターフェイスも Rails3 の AcitiveRecord のベースになっている Arel 風味ですし、公式サイトでのドキュメントの充実ぶりは中々のものです。その上 Devise, CarrierWave といった Rails 定番プラグインも対応しているので、私は Rails3 向けには断然、Mongoid をオススメします。
Sinatra などの軽いフレームワークには、mongo にしか依存していない Candy がいいかもしれません。ただし、Candy は Ruby 1.9 以上対応なので気をつけてください。
というわけで、これから、公式サイトのドキュメントを翻訳し、若干の解説を行ないます。
ハンズオンで分かる MongoDB チュートリアル
前回(と言ってももう2ヶ月前か……1.5.8まででちゃってますね。(追記:丁度今日、1.6.0 安定版がリリースされました!)例によってビルドはクソ長いので注意して下さい)、「Mac OS X で MongoDB を動かす」で、Mac OS X での MongoDB インストール、起動、停止、デーモン化をしました。
今回は、付属のシェルで簡単なCRUD操作をしてみたいと思います。なので、OSやプログラミング言語は関係ありません。素の MongoDB を学ぶ目的で作成しました。SQL 知らないと ORM が満足に使えないように、今回の CRUD 操作を知っておけば、各言語用の ORM で悩むことが少なくなると思います。
なお、今回もいちいちイラッ☆とくる語りが入ります。
復習しておきましょうか、MongoDB っていうのは、
- ドキュメント指向ストレージで
- インデックスをサポートしていて
- レプリケーションができて高可用性に富んでいて
- 自動データ分割してくれて
- 多彩な検索クエリが使えて
- 更新がアトミックで速くて
- Map/Reduce も使えたりしちゃったりして
- GridFS っていう何でもつっこめるファイルシステムを使っていて
- 商用サポートもある
といった特徴を持つNoSQLデータベースです。個人的に、Rails 3 との相性は抜群だと思いますね。
そして、MongoDB での用語を説明しておきます。
RDBMS | MongoDB |
---|---|
データベース | データベース |
テーブル | コレクション |
レコード | ドキュメント |
データベースはコレクションを含み、コレクションはドキュメントを含むという関係です。
なんで用語を変えたのでしょうか? それはドキュメント指向データベースだからです! ドキュメントっつーのはアレだ、カラムとか気にしないで何でもぶっこめるっつーこった。すばらしい!が、ちょっと不安ですね〜。アレがないコレがないとかありそうですねー。まー、そこはアプリで頑張るっつーことで、次いきましょう!
では、シェルを起動します。
$ mongo MongoDB shell version: 1.4.0 url: test connecting to: test type "help" for help >
ほい、シェルが立ち上がりましたねー。引数省略したので、localhost の test データベースにつながったようです。
ちなみに、正式に書くと、
$ mongo localhost:27017/test
となります。外のサーバに接続するときはこの形式でつないでください。
じゃあ、ちょっくら何が入っているか覗いてみましょうか。show dbs コマンドでデータベース一覧を出します。
> show dbs admin local
へー、admin と local ってデータベースがあるんすねー。これは管理用っぽいから今は触らないようにしておきましょうねー。私は覗いてびっくりしましたが、皆さんも何が起きるかドキドキしながら覗くといいですよー。
つーか、test データベースにつながってるって言ったのに、testデータベースないじゃん!と気づいたあなた!お目が高い!そうなんですよー、どうやら、MongoDB はドキュメントの作成時にコレクションもデータベースも作成するっぽいんですよー。LazyLoadっぽいなー。
データベースを変更してみましょう。use を使います。
> use mysql
switched to db mysql
はい、mysql データベースに変更しました。え?ややこしい?気にすんな!
じゃあ、どんなテーブルコレクションが入ってるか調べてみましょう。show collections コマンドを使います。
> show collections >
はい、空でした! 当たり前っすね。じゃあ、これから CRUD を説明して行きます。意外と長丁場ですよ、覚悟して下さい!
Create
ドキュメントの作成には insert() メソッドを使います。
> db.things.insert({x: 1})
はい、これで things テーブルコレクションに x が 1 というレコードドキュメントが作成できましたよー。del タグそろそろうざいっすかね、サーセンフヒヒ
あ、コレクション名を thing じゃなくて things にしたのは、Rails風です。
コレクションが出来ているかどうかだけ確認しておきましょうか。
> show collections
things
system.indexes
おお、インデックスも作られている!インデックスサポートはマジだったか。
やはりここは、ドキュメント指向っぽいとこを見ておきましょうか。
> db.things.insert({y: "string"})
MongoDB△ なんでも入る!ガバマンっすね!……ただいま不適切な表現があったことをおわびします……
ところがこれだけじゃないんです!
> db.things.insert({x: 2, a: ["apple", "banana"]}) > db.things.insert({x: 3, b: {p: "hoge", q: "fuga"}})
中に配列やハッシュをぶち込めるんです!かっけー。このドキュメントは配列やハッシュが「エンベッド」されてるといいます。
あ、MongoDB の♪もっといいとこ見てみたい♪そぉれ、いっき♪いっき♪いっき♪いっき♪……
> for (var i = 0; i < 10; i++) db.things.insert({n: i, m: 5});
うぇ……このシェル JavaScript が使えやがる……恐ろしいな……
Retrieve
おまたせ!やっとコレクションの中身が見れますよ! ちなみに CRUD の R を Read とせずに Retrieve としたのは、MongoDB の力から言うとこちらの方が相応しいかなと思ってのことです。KVS とは違うのだよ KVS とは!
じゃあ、早速。find() メソッドで検索します。
> db.things.find() { "_id" : ObjectId("4c598d011546827805e1aa53"), "x" : 1 } { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" } { "_id" : ObjectId("4c5991101546827805e1aa55"), "x" : 2, "a" : [ "apple", "banana" ] } { "_id" : ObjectId("4c5991191546827805e1aa56"), "x" : 3, "b" : { "p" : "hoge", "q" : "fuga" } } { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa58"), "n" : 1, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa59"), "n" : 2, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5a"), "n" : 3, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5b"), "n" : 4, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5c"), "n" : 5, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5d"), "n" : 6, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5e"), "n" : 7, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5f"), "n" : 8, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa60"), "n" : 9, "m" : 5 }
すげぇ、そのまま飲み込んでるよ僕のエクスカリバー……
_id ってのは、auto increment 列みたいなもので、これがまさにアイデンティティになるわけですね。NoSQL の一種ですからね。これが主キーです。
じゃあ、今のクエリの変種をば、カーソルを使います。
> var cursor = db.things.find() > while (cursor.hasNext()) printjson(cursor.next()); { "_id" : ObjectId("4c598d011546827805e1aa53"), "x" : 1 } { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" } { "_id" : ObjectId("4c5991101546827805e1aa55"), "x" : 2, "a" : [ "apple", "banana" ] } { "_id" : ObjectId("4c5991191546827805e1aa56"), "x" : 3, "b" : { "p" : "hoge", "q" : "fuga" } } { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa58"), "n" : 1, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa59"), "n" : 2, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5a"), "n" : 3, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5b"), "n" : 4, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5c"), "n" : 5, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5d"), "n" : 6, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5e"), "n" : 7, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5f"), "n" : 8, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa60"), "n" : 9, "m" : 5 }
さっきの find() だけでドキュメントが表示されてたのは、シェルがいい具合に評価してくれてたみたいですね。find() で帰ってくるのはカーソルオブジェクトですね。
printjson() メソッドで表示してますが、これは BSON 使ってるからですかね。BSON というのはですね、Binary JSON の略でして、MongoDB はバイナリ形式の JSON でデータを持ってるんですね。
forEach() メソッド使って、こんなんもできますよー、
> db.things.find().forEach(printjson) (略)
カーソルって使いでがありますねー、[] もサポートしてるんですね。
> var cursor = db.things.find(); > printjson(cursor[4]); { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0, "m" : 5 }
もできます。
あと似たようなので、こんなんも。toArray() メソッドを使っています。
> var arr = db.things.find().toArray(); > arr[4] { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0, "m" : 5 }
JavaScript シェルが自由すぎる……
シェルの多機能さはこれぐらいにして、ORMで発行されそうなクエリをやっていきますよー。
SELECT * FROM things WHERE y="string"
SQLで言うとこんな感じのクエリは、
> db.things.find({y: "string"}) { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" }
こうです。簡単ですねー。
じゃあ、SQLでいうカラム指定の場合
SELECT n FROM things WHERE m=5
は、
> db.things.find({m: 5}, {n: true}) { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0 } { "_id" : ObjectId("4c5991b41546827805e1aa58"), "n" : 1 } { "_id" : ObjectId("4c5991b41546827805e1aa59"), "n" : 2 } { "_id" : ObjectId("4c5991b41546827805e1aa5a"), "n" : 3 } { "_id" : ObjectId("4c5991b41546827805e1aa5b"), "n" : 4 } { "_id" : ObjectId("4c5991b41546827805e1aa5c"), "n" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5d"), "n" : 6 } { "_id" : ObjectId("4c5991b41546827805e1aa5e"), "n" : 7 } { "_id" : ObjectId("4c5991b41546827805e1aa5f"), "n" : 8 } { "_id" : ObjectId("4c5991b41546827805e1aa60"), "n" : 9 }
なるほどなるほど、
db.collections.find([query[, cols]])
って感じの関数なんですねー。
一つだけ引きたい場合はどうするんでしょ?
SELECT * FROM things WHERE m=5 LIMIT 1;
的な。
> db.things.findOne({m: 5}) { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0, "m" : 5 }
できますねー。
エンベッドされた値で検索できるのですかね?
まずは配列でやってみましょう。
> db.things.find({a: ["apple", "banana"]}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "x" : 2, "a" : [ "apple", "banana" ] } > db.things.find({a: ["apple"]}) (ドキュメントなし) > db.things.find({a: "apple"}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "x" : 2, "a" : [ "apple", "banana" ] }
できますねー。
次はハッシュ。
> db.foo.find({b: {p: "hoge", q: "fuga"}}) { "_id" : ObjectId("4c5991191546827805e1aa56"), "x" : 3, "b" : { "p" : "hoge", "q" : "fuga" } } > db.foo.find({b: {p: "hoge"}}) (ドキュメントなし) > db.foo.find({"b.p": "hoge"}) { "_id" : ObjectId("4c5991191546827805e1aa56"), "x" : 3, "b" : { "p" : "hoge", "q" : "fuga" } }
なるほどねー。最後の "b.p" みたいなのは、ドットノーテーションと言います。
それではここからは SQL と対応するクエリを駆け足でやっていきましょう。
AND
SELECT * FROM things WHERE n=4 AND m=5;
> db.things.find({n: 4, m: 5}) { "_id" : ObjectId("4c5991b41546827805e1aa5b"), "n" : 4, "m" : 5 }
OR → $or
※ これは MongoDB 1.5.3 からの機能となります。
SELECT * FROM things WHERE x=2 OR n=2;
> db.things.find({$or: [{ x: 1 }, {n: 2}]}) { "_id" : ObjectId("4c598d011546827805e1aa53"), "x" : 1 } { "_id" : ObjectId("4c5991b41546827805e1aa59"), "n" : 2, "m" : 5 }
<, <=, >, >= → $lt, $lte, $gt, $gte
SELECT * FROM things WHERE n<2; SELECT * FROM things WHERE n<=2; SELECT * FROM things WHERE n>7; SELECT * FROM things WHERE n>=7;
> db.things.find({n: {$lt: 2}}) { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa58"), "n" : 1, "m" : 5 } > db.things.find({n: {$lte: 2}}) { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa58"), "n" : 1, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa59"), "n" : 2, "m" : 5 } > db.things.find({n: {$gt: 7}}) { "_id" : ObjectId("4c5991b41546827805e1aa5f"), "n" : 8, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa60"), "n" : 9, "m" : 5 } > db.things.find({n: {$gte: 7}}) { "_id" : ObjectId("4c5991b41546827805e1aa5e"), "n" : 7, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5f"), "n" : 8, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa60"), "n" : 9, "m" : 5 }
<>, != → $ne
SELECT * FROM things WHERE m <> 5;
> db.things.find({m: {$ne: 5}}) { "_id" : ObjectId("4c598d011546827805e1aa53"), "x" : 1 } { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" } { "_id" : ObjectId("4c5991101546827805e1aa55"), "x" : 2, "a" : [ "apple", "banana" ] } { "_id" : ObjectId("4c5991191546827805e1aa56"), "x" : 3, "b" : { "p" : "hoge", "q" : "fuga" } }
IN → $in
SELECT * FROM things WHERE n IN (2, 4, 6);
> db.things.find({n: {$in: [2, 4, 6]}}) { "_id" : ObjectId("4c5991b41546827805e1aa59"), "n" : 2, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5b"), "n" : 4, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5d"), "n" : 6, "m" : 5 }
NOT IN → $nin
SELECT * FROM things WHERE n NOT IN (2, 4, 6);
> db.things.find({n: {$nin: [2, 4, 6]}}) { "_id" : ObjectId("4c598d011546827805e1aa53"), "x" : 1 } { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" } { "_id" : ObjectId("4c5991101546827805e1aa55"), "x" : 2, "a" : [ "apple", "banana" ] } { "_id" : ObjectId("4c5991191546827805e1aa56"), "x" : 3, "b" : { "p" : "hoge", "q" : "fuga" } } { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa58"), "n" : 1, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5a"), "n" : 3, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5c"), "n" : 5, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5e"), "n" : 7, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5f"), "n" : 8, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa60"), "n" : 9, "m" : 5 }
IS NULL, IS NOT NULL → $exists
SELECT * FROM WHERE x IS NOT NULL; SELECT * FROM WHERE n IS NULL;
> db.things.find({x: {$exists: true}}) { "_id" : ObjectId("4c598d011546827805e1aa53"), "x" : 1 } { "_id" : ObjectId("4c5991101546827805e1aa55"), "x" : 2, "a" : [ "apple", "banana" ] } { "_id" : ObjectId("4c5991191546827805e1aa56"), "x" : 3, "b" : { "p" : "hoge", "q" : "fuga" } } > db.things.find({n: {$exists: false}}) { "_id" : ObjectId("4c598d011546827805e1aa53"), "x" : 1 } { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" } { "_id" : ObjectId("4c5991101546827805e1aa55"), "x" : 2, "a" : [ "apple", "banana" ] } { "_id" : ObjectId("4c5991191546827805e1aa56"), "x" : 3, "b" : { "p" : "hoge", "q" : "fuga" } }
LIMIT → limit()
SELECT * FROM things LIMIT 3;
> db.things.find().limit(3) { "_id" : ObjectId("4c598d011546827805e1aa53"), "x" : 1 } { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" } { "_id" : ObjectId("4c5991101546827805e1aa55"), "x" : 2, "a" : [ "apple", "banana" ] }
OFFSET → skip()
SELECT * FROM things OFFSET 4 LIMIT 3;
> db.things.find().skip(4).limit(3) { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa58"), "n" : 1, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa59"), "n" : 2, "m" : 5 }
ORDER BY → sort(), $slice
SELECT * FROM things ORDER BY n LIMIT 2; SELECT * FROM things ORDER BY n DESC LIMIT 2; SELECT * FROM things ORDER BY n OFFSET 3 LIMIT 2; SELECT * FROM things ORDER BY n DESC OFFSET 3 LIMIT 2;
> db.things.find().limit(2).sort({n: 1}) { "_id" : ObjectId("4c598d011546827805e1aa53"), "x" : 1 } { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" } > db.things.find().limit(2).sort({n: -1}) { "_id" : ObjectId("4c5991b41546827805e1aa60"), "n" : 9, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5f"), "n" : 8, "m" : 5 } > db.things.find().skip(3).limit(2).sort({n: 1}) { "_id" : ObjectId("4c5991191546827805e1aa56"), "x" : 3, "b" : { "p" : "hoge", "q" : "fuga" } } { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0, "m" : 5 } > db.things.find().skip(3).limit(2).sort({n: -1}) { "_id" : ObjectId("4c5991b41546827805e1aa5d"), "n" : 6, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5c"), "n" : 5, "m" : 5 }
※ $slice は MongoDB 1.5.1 からの機能になります。
> db.things.find({}, {n: {$slice: 2}}) > db.things.find({}, {n: {$slice: -2}}) > db.things.find({}, {n: {$slice: [3, 2]}}) > db.things.find({}, {n: {$slice: [-3, 2]}})
COUNT → count()
SELECT COUNT(*) FROM things;
> db.things.find().count() 14
ここから先は MongoDB に特有の機能を紹介します。
$where
JavaScript を評価して検索します。
> db.things.find({$where: "this.n > 7"}) { "_id" : ObjectId("4c5991b41546827805e1aa5f"), "m" : 5, "n" : 8 } { "_id" : ObjectId("4c5991b41546827805e1aa60"), "m" : 5, "n" : 9 } > db.things.find("this.n > 7") { "_id" : ObjectId("4c5991b41546827805e1aa5f"), "m" : 5, "n" : 8 } { "_id" : ObjectId("4c5991b41546827805e1aa60"), "m" : 5, "n" : 9 } > f = function() { return this.n > 7; }; db.things.find(f) { "_id" : ObjectId("4c5991b41546827805e1aa5f"), "m" : 5, "n" : 8 } { "_id" : ObjectId("4c5991b41546827805e1aa60"), "m" : 5, "n" : 9 }
$mod
字面の通りモジュロ(割り算の余り)ですねー。
> db.things.find({n: {$mod: [3, 0]}}) { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5a"), "n" : 3, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5d"), "n" : 6, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa60"), "n" : 9, "m" : 5 }
$all
$in に似ていますが、エンベッドされた配列に対するもので、さらに、指定した配列の全ての値を含まなければなりません。
> db.things.find({a: {$all: ["apple", "banana"]}}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "x" : 2, "a" : [ "apple", "banana" ] } > db.things.find({a: {$all: ["apple"]}}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "x" : 2, "a" : [ "apple", "banana" ] } > db.things.find({a: {$all: ["apple", "nothing"]}}) (ドキュメントなし)
$size
エンベッドされた配列のサイズで検索します。
> db.things.find({a: {$size: 2}}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "x" : 2, "a" : [ "apple", "banana" ] } > db.things.find({a: {$size: 1}}) (ドキュメントなし)
$type
BSON型の型番号で検索します。
型と型番号の対応は以下のようになります。
型名 | 型番号 |
---|---|
Double | 1 |
String | 2 |
Object | 3 |
Array | 4 |
Binary data | 5 |
Object id | 7 |
Boolean | 8 |
Date | 9 |
Null | 10 |
Regular expression | 11 |
JavaScript code | 13 |
Symbol | 14 |
JavaScript code with scope | 15 |
32-bit integer | 16 |
Timestamp | 17 |
64-bit integer | 18 |
Min key | 255 |
Max key | 127 |
> db.foo.find({y: {$type: 2}}) { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" } > db.foo.find({x: {$type: 2}}) (ドキュメントなし)
正規表現
なんと!正規表現でも検索できます!
> db.things.find({y: /^str/}) { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" } > db.things.find({y: /^str.*/}) { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" } > db.things.find({y: /^str.*$/}) { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" }
なお、上記3つの例は全てインデックスを使っているそうですが、後者の2つは全ドキュメント検索するのでスピードが遅いそうです。
$elemMatch
エンベッドされた配列の中を検索します。
> db.things.insert({x: [{a: 1, b: 3}, 7, {b: 99}, {a: 11}]}) > db.things.find({x: {$elemMatch: {a: 1, b: {$gt: 1}}}}) { "_id" : ObjectId("4c5a0f602e6af24434f87cfe"), "x" : [ { "a" : 1, "b" : 3 }, 7, { "b" : 99 }, { "a" : 11 } ] } // ドットノーテーションでも同じことができます。 > db.things.find({"x.a": 1, "x.b": {$gt: 1}}) { "_id" : ObjectId("4c5a0f602e6af24434f87cfe"), "x" : [ { "a" : 1, "b" : 3 }, 7, { "b" : 99 }, { "a" : 11 } ] }
$not
否定を表すメタ演算子です。他の演算子と組み合わせて使います。
> db.things.find({n: {$not: {$mod: [2, 0]}}}) { "_id" : ObjectId("4c598d011546827805e1aa53"), "x" : 1 } { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" } { "_id" : ObjectId("4c5991101546827805e1aa55"), "x" : 2, "a" : [ "apple", "banana" ] } { "_id" : ObjectId("4c5991191546827805e1aa56"), "x" : 3, "b" : { "p" : "hoge", "q" : "fuga" } } { "_id" : ObjectId("4c5991b41546827805e1aa58"), "n" : 1, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5a"), "n" : 3, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5c"), "n" : 5, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5e"), "n" : 7, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa60"), "n" : 9, "m" : 5 } { "_id" : ObjectId("4c5a0f602e6af24434f87cfe"), "x" : [ { "a" : 1, "b" : 3 }, 7, { "b" : 99 }, { "a" : 11 } ] }
ふー、疲れた。これで Retrieve は一通り終わりです。group() もあるんですが、Map/Reduce が絡むので、また後日。
なんかこの辺でテンション下がってきました。平常運転でやろうかなと思います。
Update
更新がまた多彩なんですよねー。よっし、がんばるぞぅ、おう。みんなも頑張って読んで!
save()
最後に作成したドキュメントがややこしすぎるので、もっと簡素な奴に更新してみましょうか。
> var doc = db.things.find().toArray()[14] { "_id" : ObjectId("4c5a0f602e6af24434f87cfe"), "x" : [ { "a" : 1, "b" : 3 }, 7, { "b" : 99 }, { "a" : 11 } ] } > doc.x = {a: 1, b: 2, c: 3} { "a" : 1, "b" : 2, "c" : 3 } > db.foo.save(doc) > doc = db.things.find().toArray()[14] { "_id" : ObjectId("4c5a0f602e6af24434f87cfe"), "x" : { "a" : 1, "b" : 2, "c" : 3 } }
はい、更新できました。簡単すね。
もちろん JavaScript 風に値をいじれます。
> doc.x.c++ 3 > db.things.save(doc) > doc = db.things.find().toArray()[14] { "_id" : ObjectId("4c5a0f602e6af24434f87cfe"), "x" : { "a" : 1, "b" : 2, "c" : 4 } }
ORM 触ってるのと、あんまかわんないような気がしますね。
update()
db.collection.update(criteria, objNew[, upsert[, multi]])
引数を解説すると、
- criteria - 検索条件
- objNew - 更新オブジェクトまたは修飾子
- upsert - update + insert の合成語。ドキュメントがあれば更新し、なければ新規に作成します。
- multi - 検索条件に複数該当する場合に全部更新するかどうか決めます。デフォルトは最初の1レコードのみです。
> db.things.find({x: 1}) { "_id" : ObjectId("4c598d011546827805e1aa53"), "x" : 1 } > db.things.update({x: 1}, {x: 255}) > db.things.find({x: 1}) (ドキュメントなし) > db.things.find({x: 255}) { "_id" : ObjectId("4c598d011546827805e1aa53"), "x" : 255 }
できました。でも、objNew と丸ごと入れ替わっちゃうので、複雑なオブジェクトなんかの場合、使いでが悪いです。
次から説明する、修飾子と一緒に使えば、そこらへん楽になります。
$inc
値をインクリメントします。
> db.things.update({x: 255}, {$inc: {x: 1}}) > db.things.find({x: 255}) (ドキュメントなし) > db.things.find({x: 256}) { "_id" : ObjectId("4c598d011546827805e1aa53"), "x" : 256 }
save() の例でインクリメントしましたが、こちらはアトミックに更新することができます。分散環境を考えると便利ですね。
$set
update() を普通に使うとobjNewに破壊的に更新されてしまいますが、この $set を使うことで、差分更新ができます。
> db.things.find({n: 9}) { "_id" : ObjectId("4c5991b41546827805e1aa60"), "n" : 9, "m" : 5 } > db.things.update({n: 9}, {$set: {m: 10}}) > db.things.find({n: 9}) { "_id" : ObjectId("4c5991b41546827805e1aa60"), "n" : 9, "m" : 10 }
$unset
フィールド自体を削除します。
> db.things.update({n: 9}, {$unset: {m: 1}}) > db.things.find({n: 9}) { "_id" : ObjectId("4c5991b41546827805e1aa60"), "n" : 9 }
$push
配列フィールドに値を追加します。
> db.things.find({x: 2}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "x" : 2, "a" : [ "apple", "banana" ] } > db.things.update({x: 2}, {$push: {a: "peach"}}) > db.things.find({x: 2}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "apple", "banana", "peach" ], "x" : 2 }
$pushAll
配列フィールドに配列を追加します。
> db.things.update({x: 2}, {$pushAll: {a: ["orange", "grape"]}}) > db.things.find({x: 2}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "apple", "banana", "peach", "orange", "grape" ], "x" : 2 }
$addToSet
ワンモアセッ!……すいません、つい古き良きビリーの声が聞こえてきました。この記事書くために寝てないんすよ。許して♡
配列フィールドに値を追加するのは $push と同じなんですが、重複になるときは追加しません。
> db.things.find({x: 2}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "apple", "banana", "peach", "orange", "grape" ], "x" : 2 } > db.things.update({x: 2}, {$addToSet: {a: "grape"}}) > db.things.find({x: 2}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "apple", "banana", "peach", "orange", "grape" ], "x" : 2 } > db.things.update({x: 2}, {$addToSet: {a: "strawberry"}}) > db.things.find({x: 2}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "apple", "banana", "peach", "orange", "grape", "strawberry" ], "x" : 2 }
$pop
配列フィールドの末尾を削除します。-1を指定した場合は、先頭が削除されます。
> db.things.find({x: 2}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "apple", "banana", "peach", "orange", "grape", "strawberry" ], "x" : 2 } > db.things.update({x: 2}, {$pop: {a: 1}}) > db.things.find({x: 2}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "apple", "banana", "peach", "orange", "grape" ], "x" : 2 } > db.things.update({x: 2}, {$pop: {a: -1}}) > db.things.find({x: 2}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "banana", "peach", "orange", "grape" ], "x" : 2 }
$pull
配列フィールドから、指定した値を削除します。
> db.things.find({x: 2}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "banana", "peach", "orange", "grape" ], "x" : 2 } > db.things.update({x: 2}, {$pull: {a: "orange"}}) > db.things.find({x: 2}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "banana", "peach", "grape" ], "x" : 2 }
$pullAll
$pushAll の逆ですね。配列フィールドから指定した配列の値を全部削除します。
> db.things.find({x: 2}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "banana", "peach", "grape" ], "x" : 2 } > db.things.update({x: 2}, {$pullAll: {a: ["peach", "grape"]}}) > db.things.find({x: 2}) { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "banana" ], "x" : 2 }
$
ちょっとややこしいのですが、ポジショナルオペレーターというらしいです。検索条件でマッチした配列の要素を表します。
> db.things.insert({title: "foo", comments: [{by: "joe", votes: 3}, {by: "jane", votes: 7}]}) > db.things.find({"comments.by": "joe"}) { "_id" : ObjectId("4c5a37442e6af24434f87d00"), "title" : "foo", "comments" : [ { "by" : "joe", "votes" : 3 }, { "by" : "jane", "votes" : 7 } ] } > db.things.update({"comments.by": "joe"}, {$inc: {"comments.$.votes": 1}}) > db.things.find({"comments.by": "joe"}) { "_id" : ObjectId("4c5a37442e6af24434f87d00"), "title" : "foo", "comments" : [ { "by" : "joe", "votes" : 4 }, { "by" : "jane", "votes" : 7 } ] }
なんか使いどころが難しそうですね。
以上で Update は終わりです!
Delete
いろいろ削除する前に foo コレクション全体が現状どうなっているか確認してみましょう。
> db.things.find() { "_id" : ObjectId("4c5991191546827805e1aa56"), "x" : 3, "b" : { "p" : "hoge", "q" : "fuga" } } { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa58"), "n" : 1, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa59"), "n" : 2, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5a"), "n" : 3, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5b"), "n" : 4, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5c"), "n" : 5, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5d"), "n" : 6, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5e"), "n" : 7, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5f"), "n" : 8, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa60"), "n" : 9} { "_id" : ObjectId("4c5a0f602e6af24434f87cfe"), "x" : { "a" : 1, "b" : 2, "c" : 4 } } { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" } { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "banana" ], "x" : 2 } { "_id" : ObjectId("4c598d011546827805e1aa53"), "x" : 256 } { "_id" : ObjectId("4c5a37442e6af24434f87d00"), "title" : "foo", "comments" : [ { "by" : "joe", "votes" : 4 }, { "by" : "jane", "votes" : 7 } ] }
いっぱい作りましたねー。
じゃあ、まずは、x = 256 のレコードを削除してみたいと思います。
> db.things.remove({x: 256}) > db.things.find({x: 256}) (ドキュメントなし)
削除できました。
では、次に、n > 4 のドキュメントを削除してみます。
> db.things.remove({n: {$gt: 4}, $atomic: true}) > db.things.find() { "_id" : ObjectId("4c5991191546827805e1aa56"), "x" : 3, "b" : { "p" : "hoge", "q" : "fuga" } } { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa58"), "n" : 1, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa59"), "n" : 2, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5a"), "n" : 3, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5b"), "n" : 4, "m" : 5 } { "_id" : ObjectId("4c5a0f602e6af24434f87cfe"), "x" : { "a" : 1, "b" : 2, "c" : 4 } } { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" } { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "banana" ], "x" : 2 } { "_id" : ObjectId("4c5a37442e6af24434f87d00"), "title" : "foo", "comments" : [ { "by" : "joe", "votes" : 4 }, { "by" : "jane", "votes" : 7 } ] }
綺麗に削除できましたね。$atomic 修飾子は、その名の通りアトミックに複数レコードを削除します。自動分散機構があるもんで、気を使ってますね。
オブジェクトが得られているときには、こうやって削除します。
> doc = db.things.findOne({x: 3}) { "_id" : ObjectId("4c5991191546827805e1aa56"), "x" : 3, "b" : { "p" : "hoge", "q" : "fuga" } } > db.things.remove({_id: doc._id}) > db.things.find() { "_id" : ObjectId("4c5991b41546827805e1aa57"), "n" : 0, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa58"), "n" : 1, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa59"), "n" : 2, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5a"), "n" : 3, "m" : 5 } { "_id" : ObjectId("4c5991b41546827805e1aa5b"), "n" : 4, "m" : 5 } { "_id" : ObjectId("4c5a0f602e6af24434f87cfe"), "x" : { "a" : 1, "b" : 2, "c" : 4 } } { "_id" : ObjectId("4c598e261546827805e1aa54"), "y" : "string" } { "_id" : ObjectId("4c5991101546827805e1aa55"), "a" : [ "banana" ], "x" : 2 } { "_id" : ObjectId("4c5a37442e6af24434f87d00"), "title" : "foo", "comments" : [ { "by" : "joe", "votes" : 4 }, { "by" : "jane", "votes" :
_id 指定ですね。
では最後に、綺麗サッパリ、全ドキュメントを削除します。
> db.things.remove({})
> db.things.find()
(ドキュメントなし)
はい、これで CRUD は終わりです。
後片付け
things コレクションを削除しましょう。
> show collections things system.indexes > db.things.drop() true > show collections system.indexes
データベースも削除しましょう。
> db.dropDatabase() { "dropped" : "mysql.$cmd", "ok" : 1 } > show dbs admin local
はい、これで終了です!お疲れ様でしたー。
(アサマシ貼りたいけど、MongoDBの本、洋書でさえまだ出てないw)
(あ、今クソ貧乏なんで、この記事に感動した方は babie.tanaka@gmail.com 宛に Amazon ギフト券500円分とか贈ってもらえると助かります!)
Mac OS X で MongoDB を動かす
MongoDB は、いわゆる NoSQL データベースの一つです。
公式サイトの文言をそのまま信じるなら、
- ドキュメント指向ストレージで
- インデックスをサポートしていて
- レプリケーションができて高可用性に富んでいて
- 自動データ分割してくれて
- 多彩な検索クエリが使えて
- 更新がアトミックで速くて
- Map/Reduce も使えたりしちゃったりして
- GridFS っていう何でもつっこめるファイルシステムを使っていて
- 商用サポートもある
といった特徴を持つプロダクトです。
私は、自動分散と、単純なKVSにはない豊富な検索クエリに惹かれて、使ってみようかと思いました。インデックスがあるのもいいですね! というか、MongoHQ ですよ! Amazon EC2 のサーバーとつなげられるらしい、自動スケールアウト管理不要サービスです。最初無料からスタートして安いんですよねー。これが決め手です。
では、インストールしましょうか。幸いなことに macports にありました。楽ちんですね。
$ sudo port install mongodb
今コマンド実行した?早速実行した?はい、君、終了〜。なんと、依存ライブラリ boost のビルドに6時間、mongodb のビルドに1時間かかってしまうのです!(MacBook (Early 2008) 調べ) 会社の人は退社間際、自宅の人は出社前・寝る前にやるのが吉ですね。
起動に必要なディレクトリとファイルを作っておきましょう。
$ sudo mkdir /var/lib/mongodb $ sudo touch /var/log/mongodb.log
ディレクトリパスはお好みで。macports なので /opt/local/ を頭につけたい人もいるかもしれませんね。
起動は、
$ /opt/local/bin/mongod --dbpath /var/lib/mongodb --logpath /var/log/mongodb.log
ログを追記式にしたいときは、--logappend を加えて下さい。付けないと、起動毎にログがリフレッシュします。私は、開発環境なので、ディスク占有しない方がいいかなー、と思って外しました。
停止は、
$ kill -2 (プロセスID) または、 $ kill -15 (プロセスID)
です。
マニュアルによると、SIGINT(2) か SIGTERM(15) で停止だそうです。CTRL-C とか kill -9 とかは厳禁ですよ!
Mac 起動時に自動でスタートして欲しいですよね? OS X なので launchd/launchctl で起動・停止するように設定します。
/Library/LaunchDaemons/org.mongodb.mongod.plist
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>org.mongodb.mongod</string> <key>RunAtLoad</key> <true/> <key>ProgramArguments</key> <array> <string>/opt/local/bin/mongod</string> <string>--dbpath</string> <string>/var/lib/mongodb</string> <string>--logpath</string> <string>/var/log/mongodb.log</string> </array> </dict> </plist>
launchd/launchctl は停止時は SIGTERM を送るそうなので安心ですね。
オーナーやパーミッションも変更しておきましょう。
$ sudo chown root:wheel org.mongodb.mongod.plist $ sudo chmod 644 org.mongodb.mongod.plist $ ls -l org.mongodb.mongod.plist -rw-r--r-- 1 root wheel 496 6 2 04:30 org.mongodb.mongod.plist
これで(RunAtLoad が true なので)次回Macの起動時に自動で起動されるはずです。
手動で起動・停止するのは、
$ sudo launchctl load /Library/LaunchDaemons/org.mongodb.mongod.plist $ sudo launchctl unload /Library/LaunchDaemons/org.mongodb.mongod.plist
です。plist はフルパスじゃないといけませんよ。
うまく起動したら、チュートリアルを見ながら、クライアントインターフェイスで遊んでみましょう。
$ mongo
それでは、今日はここまで!
続き書きました → ハンズオンで分かる MongoDB チュートリアル