Rails3 対応 MongoDB ORM、Mongoid 詳解―永続化
今回は、データベースへの Insert、Update、Delete です。
Mongoid は ActiveRecord スタイルのデータベースへの永続化メソッドをサポートしています。永続化戦略の項は、実際に実行されるデータベースクエリについて、注意して見てください。
作成
新しいドキュメントを作成し、データベースに格納するには、Document.create や Document.create! を使います。後者は、バリデーションが失敗したときに例外を上げます。
person = Person.create(:first_name => "Syd", :last_name => "Vicious") person = Person.create!(:first_name => "Emmanuel", :last_name => "Zorg")
保存
ドキュメントをデータベースに格納するには、Document.save または Document.save! を使います。後者は、バリデーションが失敗したときに例外を上げます。
person = Person.new(:first_name => "Syd", :last_name => "Vicious") person.save # or person.upsert person = Person.new(:first_name => "Emmanuel", :last_name => "Zorg") person.save!
アトリビュートの更新
新しいアトリビュートをドキュメントに書き込み、保存するには、Document.update_attrributes と Document.update_attributes! を使います。後者は、バリデーションが失敗したときに例外を上げます。
person = Person.new(:first_name => "Syd", :last_name => "Vicious") person.update_attributes(:first_name => "Nancy") person = Person.new(:first_name => "Emmanuel", :last_name => "Zorg") person.update_attributes!(:first_name => "Nancy")
削除
データベースからドキュメントを削除するには、Document#destroy、 Document#delete、Document.destroy_all、Document.delete_all を使います。最初の2つは、1つのドキュメントを削除し、後の2つは、与えた条件に合う全てのドキュメントを削除します。これらの削除メソッドは、独自に定義した全てのコールバックを無視するのに気をつけてください。これらのメソッドはまた、関連から呼ぶこともできます。
person = Person.create(:first_name => "Syd", :last_name => "Vicious") person.destroy person = Person.create(:first_name => "Syd", :last_name => "Vicious") person.delete Person.destroy_all(:conditions => { :first_name => "Syd", :last_name => "Vicious" }) Person.delete_all(:conditions => { :first_name => "Syd", :last_name => "Vicious" }) person.addresses.delete_all person.addresses.destroy_all(:conditions => { :street => "Bond St" })
セーフモードでの永続化
Mongoid は、全体設定のオプションで、セーフモードでの永続化を提供していますが、もし、デフォルトの false にしている場合でも、永続化メソッドの前に safely を追加するだけで、1クエリ毎にセーフモードを使うことができます。
Person.safely.create(:title => "King") Person.safely.delete_all person.safely.save person.safely.destroy
修飾子
Mongoid は $inc 修飾子をサポートしています。
person = Person.find(id) person.inc(:score, 100) person.safely.inc(:score, 100) # Update in safe mode.
永続化戦略と裏で走るデータベースクエリ
これからの例では、以下のモデルに基づいて、説明します。(裏で走るデータベースクエリの読み方については、拙作「ハンズオンで分かる MongoDB チュートリアル」を参考にしてください。)
class Person include Mongoid::Document field :first_name field :middle_initial field :last_name embeds_one :email embeds_many :addresses end class Address include Mongoid::Document field :street field :post_code field :state embedded_in :person, :inverse_of => :addresses end class Email include Mongoid::Document field :address embedded_in :person, :inverse_of => :email end
新しいドキュメントの作成
シナリオ:新しいルートドキュメントを保存する。
ドキュメントを、全てのフィールドとともに、コレクションに挿入します。
Mongoid では:
person = Person.new(:first_name => "Dudley") person.save
MongoDB のクエリでは:
db.people.insert({ "first_name" : "Dudley" }, true);
シナリオ:新しいルートドキュメントを、子供とともに保存する。
ドキュメントを、全てのフィールドと、子要素とともに、コレクションに挿入します。save はどちらから呼んでも問題ありません。
Mongoid では:
person = Person.new(:first_name => "Dudley") address = Address.new(:street => "Upper Street") person.addresses << address person.save # address.save でも同様の結果になる
MongoDB のクエリでは:
db.people.insert( { "first_name" : "Dudley", "addresses" : [ { "street" : "Upper Street" } ] }, true );
シナリオ:既に存在するルートドキュメントの中に、新しい embeds_one を保存する。
ルートドキュメントの中へ、新しいエンベッドされたアトリビュートを、アトミックに更新します。これは子要素から呼ばれないといけません。
Mongoid では:
person = Person.where(:first_name => "Dudley").first email = Email.new(:address => "dudley@moore.com") person.email = email email.save
MongoDB のクエリでは :(person.id = "4baa56f1230048567300485c" だとして)
db.people.update( { "_id" : "4baa56f1230048567300485c" }, { "$set" : { "email" : { "address" : "dudley@moore.com" } } }, false, true );
シナリオ:既に存在するルートドキュメントの中に、新しい embeds_many を保存する。
ルートドキュメントの中の配列へ、新しいエンベッドされたアトリビュートを、アトミックに追加します。
Mongoid では:
person = Person.where(:first_name => "Dudley").first address = Address.new(:street => "Upper Street") person.addresses << address address.save
MongoDB のクエリでは: (person.id = "4baa56f1230048567300485c" だとして)
db.people.update( { "_id" : "4baa56f1230048567300485c" }, { "$push" : { "addresses" : { "street" : "Upper Street" } } }, false, true );
既にあるドキュメントを更新する
既存のドキュメントを更新するとき、Mongoid のダーティーアトリビュートモジュールは、変更のあったものだけ更新するようになっています。save メソッドは、更新されたドキュメントから呼ばなければなりません。(これは今後のリリースで変更される予定です。ドキュメントツリー全体を確認してください。)
シナリオ:ルートドキュメントを更新する。
ダーティーアトリビュートを、アトミックに更新します。
Mongoid では:
person = Person.where(:first_name => "Dudley").first person.last_name = "Moore" person.save
MongoDB のクエリでは: (person.id = "4baa56f1230048567300485c" だとして)
db.people.update( { "_id" : "4baa56f1230048567300485c" }, { "$set" : { "last_name" : "Moore" } }, false, true );
シナリオ:既にある embeds_one を更新する。
既にエンベッドされているドキュメントを、アトミックに更新します。
Mongoid では:
person = Person.where(:first_name => "Dudley").first email = person.email email.address = "dudley@moore.org" email.save
MongoDB のクエリでは: (person.id = "4baa56f1230048567300485c" だとして)
db.people.update( { "_id" : "4baa56f1230048567300485c" }, { "$set" : { "email.address" : "dudley@moore.org" } }, false, true );
シナリオ:既にある embeds_many を更新する。
既にエンベッドされているドキュメントを、配列のその部分だけ、アトミックに更新します。
Mongoid では:
person = Person.where(:first_name => "Dudley").first address = person.addresses.first address.street = "Clerkenwell Road" address.save
MongoDB のクエリでは: (person.id = "4baa56f1230048567300485c" だとして)
db.people.update( { "_id" : "4baa56f1230048567300485c" }, { "$set" : { "addresses.0.street" : "Clerkenwell Road" } }, false, true );
混合した永続化
Mongoid の永続化では、永続化コマンドが実行され、全ての更新は、その下の階層のエンベッドされたものの更新も同様に、アトミックです。このため、ドキュメントツリー全体を扱うのに便利で、1回のデータベース呼び出しで済みます。ベータ8リリースでは、削除はこういう風に動きませんので、別々に呼び出してください。
シナリオ:ルートドキュメントと、子要素を、新しい子要素を追加した上で、更新する。
Mongoid では:
person = Person.where(:first_name => "Dudley").first person.last_name = "Moore" person.email.address = "d.moore@gmail.com" person.addresses.build(:street => "Upper Street") person.save
MongoDB のクエリでは: (person.id = "4baa56f1230048567300485c" だとして)
db.people.update( { "_id" : "4baa56f1230048567300485c" }, { "$set" : { "last_name" : "Moore", "email.address" : "d.moore@gmail.com" }, "$push" : { "addresses" : { "street" : "Upper Street" } } }, false, true );
ドキュメントの削除
シナリオ:既にあるルートドキュメントを削除する。
ドキュメントの id でコレクションから削除します。
Mongoid では:
person = Person.where(:first_name => "Dudley").first person.delete # destroy メソッドでも動きます
MongoDB のクエリでは: (person.id = "4baa56f1230048567300485c" だとして)
db.people.remove({ "_id" : "4baa56f1230048567300485c" }, true);
シナリオ:既にある embeds_one を削除する。
エンベッドされたドキュメントをアトミックに更新します。
Mongoid では:
person = Person.where(:first_name => "Dudley").first email = person.email email.delete # もしくは destroy
MongoDB のクエリでは: (person.id = "4baa56f1230048567300485c" だとして)
db.people.update( { "_id" : "4baa56f1230048567300485c" }, { "$unset" : { "email" : true } }, false, true );
シナリオ:既にある embeds_many を削除する。
エンベッドされた配列のドキュメントをアトミックに更新します。
Mongoid では:
person = Person.where(:first_name => "Dudley").first address = person.addresses.first address.delete # or destroy The MongoDB query: (Assume person.id = "4baa56f1230048567300485c") (Assume address.id = "4baa56f1230048567300485d")
MongoDB のクエリでは: (person.id = "4baa56f1230048567300485c"、address.id = "4baa56f1230048567300485d"だとして)
db.people.update( { "_id" : "4baa56f1230048567300485c" }, { "$pull" : { "addresses" : { "_id" : "4baa56f1230048567300485d" } } }, false, true );
永続化は以上です。