CRUD 操作
項目一覧
ドキュメントの保存
Mongoid は、アクティブ レコードやデータマッパーなどの他の Ruby マッパーに慣れるために、期待されるすべての CRUD 操作をサポートしています。 Mongoid と MongoDB の他のマッパーとの違いは、毎回ドキュメント全体をデータベースに書き込むのではなく、一般的な永続化操作が変更されたフィールドのみに対してアトミックな更新を実行することです。
永続性セクションでは、ドキュメント化された コマンドを実行するときに実行されるデータベース操作の例を示します。
標準
Mongoid の標準永続メソッドは、他のマッピング フレームワークにある一般的なメソッドの形式になります。 次の表は、すべての標準操作を例とともに示しています。
操作 | 例 | |||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ドキュメントの属性を string キーとその値を Mongoized 形式(db に保存する方法)で ``Hash`` として返します。 属性ハッシュには、すべての埋め込みドキュメントの属性とその埋め込みドキュメントなども含まれます。 埋め込み関連付けが空の場合、返されるハッシュにはそのキーは表示されません。 |
| |||||||||||||||||||||||
検証またはサーバー エラーが発生した場合はエラーが発生します。 属性のハッシュを渡して、指定された属性を持つ単一のドキュメントを作成するか、ハッシュの配列を渡して複数のドキュメントを作成します。 ハッシュが 1 つ渡されると、対応するドキュメントが返されます。 ハッシュの配列が渡されると、ハッシュに対応するドキュメントの配列が返されます。 ブロックが に与えられた場合 検証エラーやサーバーエラーなど、ドキュメントの保存に問題が発生した場合は、例外が発生し、ドキュメントは返されません。 ただし、ハッシュの配列が渡され、以前のドキュメントが正常に保存された場合、それらのドキュメントはデータベースに残ります。 |
| |||||||||||||||||||||||
1 つまたは複数のドキュメントをインスタンス化し、検証に合格したらデータベースに挿入します。
検証エラーが発生した場合、それぞれのドキュメントは挿入されず、挿入されたドキュメントとともに返されます。 を使用する |
| |||||||||||||||||||||||
変更された属性をデータベースに不可分的に保存し、新しい場合はドキュメントを挿入します。 検証に失敗した場合、またはサーバーエラーが発生した場合は例外が発生します。 変更された属性が保存されている場合は true を返し、そうでない場合は例外が発生します。 |
| |||||||||||||||||||||||
変更された属性をデータベースに不可分的に保存し、新しい場合はドキュメントを挿入します。 変更された属性が保存されている場合は true を返します。 検証エラーが発生した場合は false を返します。 ドキュメントが検証に合格したが保存中にサーバー エラーが発生した場合は、例外が発生します。 を渡す を渡す |
| |||||||||||||||||||||||
データベース内のドキュメント属性を更新します。 検証に合格した場合は true を返し、合格していない場合は false を返します。 |
| |||||||||||||||||||||||
検証に失敗した場合は、データベース内のドキュメント属性を更新し、エラーが発生します。 |
| |||||||||||||||||||||||
検証をバイパスして、単一の属性を更新します。 |
| |||||||||||||||||||||||
ドキュメントに対して アップサート による MongoDB 置換を実行します。 ドキュメントがデータベースに存在し、 |
| |||||||||||||||||||||||
ドキュメントの update_at タイムスタンプを更新し、オプションとして、提供された時間フィールドを 1 つ追加します。 これにより、すべてのノードへのアクセスが累積されます。
破棄されたドキュメントにアクセスしようとすると、 |
| |||||||||||||||||||||||
コールバックを実行せずにデータベースからドキュメントを削除します。 ドキュメントが永続化されていない場合、Mongoid は同じドキュメントをデータベースから削除しようとします。 |
| |||||||||||||||||||||||
破棄コールバックの実行中にデータベースからドキュメントを削除します。 ドキュメントが永続化されていない場合、Mongoid は同じドキュメントをデータベースから削除しようとします。 |
| |||||||||||||||||||||||
コールバックを実行せずに、データベースからすべてのドキュメントを削除します。 |
| |||||||||||||||||||||||
コールバックの実行中にデータベースからすべてのドキュメントを削除します。 これは、すべてのドキュメントをメモリに読み込むため、コストのかかる操作になる可能性があります。 |
|
Mongoid は、次の永続性関連の属性を提供します。
属性 | 例 | |||||||
---|---|---|---|---|---|---|---|---|
戻り値 |
| |||||||
戻り値 |
|
アトミック
Mongoid は、Mongoid ドキュメントのメソッドとして MongoDB 更新演算子を公開します。 これらのメソッドが使用される場合、コールバックは呼び出されず、検証は実行されません。 サポートされている更新演算子は次のとおりです。
操作 | 例 | |||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
フィールドでアトミック $addToSet を実行します。 |
| |||||||||||||||||||||||||||||||||||||||||||
フィールドに対してアトミック $bit を実行します。 |
| |||||||||||||||||||||||||||||||||||||||||||
フィールドに対してアトミック $inc を実行します。 |
| |||||||||||||||||||||||||||||||||||||||||||
フィールドでアトミック $pop を実行します。 |
| |||||||||||||||||||||||||||||||||||||||||||
フィールドでアトミック $pull を実行します。 |
| |||||||||||||||||||||||||||||||||||||||||||
フィールドに対してアトミック $pullAll を実行します。 |
| |||||||||||||||||||||||||||||||||||||||||||
フィールドでアトミック $push を実行します。 |
| |||||||||||||||||||||||||||||||||||||||||||
フィールドでアトミックな $rename を実行します。 |
| |||||||||||||||||||||||||||||||||||||||||||
モデル インスタンスの属性を更新し、インスタンスがすでに永続化されている場合は、検証をバイパスしてフィールドに対してアトミック $set を実行します。
|
| |||||||||||||||||||||||||||||||||||||||||||
フィールドに対してアトミック $unset を実行します。 |
|
これらのメソッドは検証をスキップするため、無効なドキュメントをデータベースに保存するだけでなく、アプリケーションに無効なドキュメントが存在する可能性があることに注意してください(その後、検証が失敗するため、 save
呼び出しによる保存は失敗します)。
アトミック操作のグループ化
アトミック操作は、ドキュメントで#atomically
メソッドを使用してグループ化できます。 All operations inside the block given to #atomically
are sent to the cluster in a single atomic command. 例:
person.atomically do person.inc(age: 1) person.set(name: 'Jake') end
#atomically
ブロックはネストできます。 デフォルトの動作では、ブロックが終了するとすぐに各ブロックによって実行された変更が書込まれます。
person.atomically do person.atomically do person.inc(age: 1) person.set(name: 'Jake') end raise 'An exception' # name and age changes are still persisted end
この動作は、 join_context: true
オプションを#atomically
に指定するか、 join_contexts
構成オプションをtrue
に設定することでグローバルに変更できます。 When context joining is enabled, nested #atomically
blocks are joined with the outer blocks, and only the outermost block (or the first block where join_contexts
is false) actually writes changes to the cluster. 例:
person.atomically do person.atomically(join_context: true) do person.inc(age: 1) person.set(name: 'Jake') end raise 'An exception' # name and age changes are not persisted end
コンテキスト結合動作は、Mongoid 構成でjoin_context
オプションを設定することで、デフォルトでグローバルに有効になります。 この場合、 #atomically
ブロックでjoin_context: false
を指定すると、独立した永続化コンテキストの動作を取得できます。
変更がクラスターにまだ保存されていない#atomically
ブロックで例外が発生した場合、Mongoid モデルで保留中の属性の変更はすべて元に戻されます。 例:
person = Person.new(name: 'Tom') begin person.atomically do person.inc(age: 1) person.set(name: 'Jake') person.name # => 'Jake' raise 'An exception' end rescue Exception person.name # => 'Tom' end
このセクションで説明されるアトミック操作は一度に 1 つのドキュメントに適用されるため、複数のドキュメントで呼び出される#atomically
ブロックをネストしても、異なるドキュメントへの変更は不可分的にまとめられます。 ただし、MongoDB は サーバー バージョン 4.0 以降、複数のドキュメント間でアトミックな永続性を提供するマルチドキュメントトランザクションを提供しています。
再読み込み
データベースからドキュメントの最新バージョンを取得するには、 reload
メソッドを使用します。 ドキュメントの属性に対する保存されていない変更はすべて失われます。
band = Band.create!(name: 'foo') # => #<Band _id: 6206d06de1b8324561f179c9, name: "foo", description: nil, likes: nil> band.name = 'bar' band # => #<Band _id: 6206d06de1b8324561f179c9, name: "bar", description: nil, likes: nil> band.reload # => #<Band _id: 6206d06de1b8324561f179c9, name: "foo", description: nil, likes: nil>
ドキュメントが再読み込みされると、その埋め込み関連付けもすべて同じクエリで再読み込みされます(埋め込みドキュメントはサーバー上の親ドキュメントに保存されているため)。 ドキュメントが関連付けを参照している場合、ロードされた関連付けの は再読み込みされず、値がクリアされ、次回のアクセス時にこれらの関連付けがデータベースから読み込まれるようになります。
注意
割り当てなどの関連付けに対する一部の操作では、新しいドキュメントが保持されます。 このような場合、再読み込みによって元に戻すことができない変更はありません。 次の例では、空の配列 の関連付けへの割り当てはすぐに永続され、再読み込みしてもドキュメントは変更されません。
# Assuming band has many tours, which could be referenced: band = Band.create!(tours: [Tour.create!]) # ... or embedded: band = Band.create!(tours: [Tour.new]) # This writes the empty tour list into the database. band.tours = [] # There are no unsaved modifications in band at this point to be reverted. band.reload # Returns the empty array since this is what is in the database. band.tours # => []
モデルにシャードキーが定義されている場合、シャードキー値はリロード クエリに含まれます。
データベースに一致するドキュメントが含まれていない場合、Mongoid は通常Mongoid::Errors::DocumentNotFound
を発生させます。 ただし、構成オプションraise_not_found_error
がfalse
に設定されており、データベースに一致するドキュメントが含まれていない場合、Mongoid は現在のドキュメントを、属性がデフォルト値に設定されている新しく作成されたドキュメントに置き換えます。 重要な点は、次の例に示すように、これにより通常、ドキュメントの_id
が変更されることです。
band = Band.create! # => #<Band _id: 6206d00de1b8324561f179c7, name: "foo", description: nil, likes: nil> Mongoid.raise_not_found_error = false band.destroy band.reload # => #<Band _id: 6206d031e1b8324561f179c8, name: nil, description: nil, likes: nil>
このため、 raise_not_found_error
がfalse
に設定されている場合にreload
を使用することは推奨されません。
保存されていないドキュメントの再読み込み
reload
ドキュメントがまだ保存されていない場合に呼び出すことができます。 この場合、 reload
はドキュメントに指定されたid
値(およびシャードキーが定義されている場合はシャードキー値)を使用してfind
クエリを実行します。
existing = Band.create!(name: 'Photek') # Unsaved document band = Band.new(id: existing.id) band.reload band.name # => "Photek"
フィールド値へのアクセス
Mongoid は、フィールド値にアクセスするいくつかの方法を提供します。
注意
ゲッターとセッター
推奨される方法は、宣言されたフィールドごとに生成された getter メソッドと setter メソッドを使用することです。
class Person include Mongoid::Document field :first_name end person = Person.new person.first_name = "Artem" person.first_name # => "Artem"
このメカニズムを使用するには、各フィールドを明示的に宣言するか、モデル クラスで動的フィールドを有効にする必要があります。
カスタム ゲッターとセッター
getter メソッドと setter メソッドを明示的に定義して、値の変換や別のフィールド名での値の保存など、フィールドの読み取りや書き込みの際のカスタム動作を提供することができます。 この場合、 read_attribute
メソッドとwrite_attribute
メソッドを使用して値を直接読み取りと属性ハッシュに書込むことができます。
class Person include Mongoid::Document def first_name read_attribute(:fn) end def first_name=(value) write_attribute(:fn, value) end end person = Person.new person.first_name = "Artem" person.first_name # => "Artem" person.attributes # => {"_id"=>BSON::ObjectId('606477dc2c97a628cf47075b'), "fn"=>"Artem"}
read_attribute
& write_attribute
read_attribute
メソッドとwrite_attribute
メソッドは明示的に使用することもできます。 フィールドでストレージ フィールド名が指定されている場合、 read_attribute
とwrite_attribute
の両方で、宣言されたフィールド名または操作のストレージ フィールド名のいずれかを受け入れることに注意してください。
class Person include Mongoid::Document field :first_name, as: :fn field :last_name, as: :ln end person = Person.new(first_name: "Artem") # => #<Person _id: 60647a522c97a6292c195b4b, first_name(fn): "Artem", last_name(ln): nil> person.read_attribute(:first_name) # => "Artem" person.read_attribute(:fn) # => "Artem" person.write_attribute(:last_name, "Pushkin") person # => #<Person _id: 60647a522c97a6292c195b4b, first_name(fn): "Artem", last_name(ln): "Pushkin"> person.write_attribute(:ln, "Medvedev") person # => #<Person _id: 60647a522c97a6292c195b4b, first_name(fn): "Artem", last_name(ln): "Medvedev">
read_attribute
およびwrite_attribute
では使用済み名のフィールドを定義する必要はありませんが、 write_attribute
を使用してフィールド値を書込むと、それぞれのフィールドは次のいずれかに定義されません。
person.write_attribute(:undefined, "Hello") person # => #<Person _id: 60647b212c97a6292c195b4c, first_name(fn): "Artem", last_name(ln): "Medvedev"> person.attributes # => {"_id"=>BSON::ObjectId('60647b212c97a6292c195b4c'), "first_name"=>"Artem", "last_name"=>"Medvedev", "undefined"=>"Hello"} person.read_attribute(:undefined) # => "Hello" person.undefined # raises NoMethodError
read_attribute
が欠落しているフィールドにアクセスするために使用されると、 nil
が返されます。
ハッシュ アクセス
Mongoid モデル[]
[]=
Hash
インスタンスは、属性への スタイルのアクセスを提供するために メソッドと メソッドを定義します。[]
はread_attribute
のエイリアスであり、 []=
はwrite_attribute
のエイリアスです。動作の詳細については、 read_attribute および write_attributeのセクションを参照してください。
class Person include Mongoid::Document field :first_name, as: :fn field :last_name, as: :ln end person = Person.new(first_name: "Artem") person["fn"] # => "Artem" person[:first_name] # => "Artem" person[:ln] = "Medvedev" person # => #<Person _id: 606483742c97a629bdde5cfc, first_name(fn): "Artem", last_name(ln): "Medvedev"> person["last_name"] = "Pushkin" person # => #<Person _id: 606483742c97a629bdde5cfc, first_name(fn): "Artem", last_name(ln): "Pushkin">
一括属性書込み (write)
複数のフィールド値を一度に設定する場合でも、これを実現するさまざまな方法があります。
# Get the field values as a hash. person.attributes # Set the field values in the document. Person.new(first_name: "Jean-Baptiste", middle_name: "Emmanuel") person.attributes = { first_name: "Jean-Baptiste", middle_name: "Emmanuel" } person.write_attributes( first_name: "Jean-Baptiste", middle_name: "Emmanuel", )
ダーティ トラッキング
Mongoid は、アクティブ モデルの API をミラーリングする API を使用して、変更されたフィールドまたは「ダーティ」フィールドの追跡をサポートします。 モデル内の定義されたフィールドが変更された場合、モデルはダーティとしてマークされ、追加の 動作が有効になります。
変更の表示
モデルの変更内容を確認するには、さまざまな方法があります。 変更は、ドキュメントがインスタンス化されてから、新しいドキュメントとして、またはデータベースからのロードによって保存されるまで記録されます。 永続化操作により、変更がクリアされます。
class Person include Mongoid::Document field :name, type: String end person = Person.first person.name = "Alan Garner" # Check to see if the document has changed. person.changed? # true # Get an array of the names of the changed fields. person.changed # [ :name ] # Get a hash of the old and changed values for each field. person.changes # { "name" => [ "Alan Parsons", "Alan Garner" ] } # Check if a specific field has changed. person.name_changed? # true # Get the changes for a specific field. person.name_change # [ "Alan Parsons", "Alan Garner" ] # Get the previous value for a field. person.name_was # "Alan Parsons"
注意
ドキュメントにchanges
changed_attributes
関連付けを設定しても、 ハッシュまたは ハッシュは変更されません。これは、参照されているか埋め込まれているかにかかわらず、すべての関連付けに当てはまります。 参照された関連付けの _id(s) フィールドを変更すると、変更がchanges
とchanged_attributes
ハッシュに表示されることに注意してください。
変更をリセットする
リセット メソッドを呼び出すと、フィールドの変更をその前の値にリセットできます。
person = Person.first person.name = "Alan Garner" # Reset the changed name back to the original person.reset_name! person.name # "Alan Parsons"
永続性
Mongoid は、永続性操作の主要としてダーティ トラッキングを使用します。 保存のたびにドキュメント全体を書込む他のフレームワークとは異なり、ドキュメントの変更を検討し、変更された内容のみをアトミックに更新します。 変更が行われていない場合、Mongoid はModel#save
の呼び出しでデータベースにアクセスしません。
以前の変更の表示
ドキュメントが永続化された後、 Model#previous_changes
を呼び出すと、以前の変更内容を確認できます。
person = Person.first person.name = "Alan Garner" person.save # Clears out current changes. # View the previous changes. person.previous_changes # { "name" => [ "Alan Parsons", "Alan Garner" ] }
コンテナ フィールドの更新
MONGOID-2951 まで注意してください が解決された場合、値をデータベースに永続化するには、コンテナ フィールドを含むすべてのフィールドを に割り当てる必要があります。
たとえば、次のようなセットに追加しても機能しません。
class Band include Mongoid::Document field :tours, type: Set end band = Band.new band.tours # => #<Set: {}> band.tours << 'London' # => #<Set: {"London"}> band.tours # => #<Set: {}>
代わりに、フィールド値はモデル外で変更され、次のようにモデルに戻される必要があります。
class Band include Mongoid::Document field :tours, type: Set end band = Band.new tours = band.tours # => #<Set: {}> tours << 'London' # => #<Set: {"London"}> band.tours = tours # => #<Set: {"London"}> band.tours # => #<Set: {"London"}>
読み取り専用ドキュメント
ドキュメントを読み取り専用としてマークするには、 Mongoid.legacy_readonly
機能フラグの値に応じて、次の 2 つの方法があります。
このフラグがオフの場合、そのドキュメントはドキュメントで#readonly!
メソッドが呼び出されたときに読み取り専用としてマークされます。 このフラグがオフになっている読み取り専用ドキュメントでは、保存、更新、削除、破棄など、任意の永続化操作を実行しようとすると ReadonlyDocument エラーが発生します。 再読み込みしても読み取り専用状態はリセットされないことに注意してください。
band = Band.first band.readonly? # => false band.readonly! band.readonly? # => true band.name = "The Rolling Stones" band.save # => raises ReadonlyDocument error band.reload.readonly? # => true
このフラグをオンにすると、そのドキュメントがプロジェクションされた場合(つまり#only
または#without
を使用している場合)、ドキュメントは読み取り専用としてマークされます。 このフラグがオンになっている読み取り専用ドキュメントは、削除可能または破棄可能ではありません( ReadonlyDocument
エラーが発生します)が、保存と更新は可能になります。 読み取り専用ステータスは、ドキュメントを再読み込みするとリセットされます。
class Band include Mongoid::Document field :name, type: String field :genre, type: String end band = Band.only(:name).first band.readonly? # => true band.destroy # => raises ReadonlyDocument error band.reload.readonly? # => false
[readConcern] の上書き readonly?
ドキュメントを読み取り専用にする別の方法は、読み取り専用を上書きすることです。 メソッド:
class Band include Mongoid::Document field :name, type: String field :genre, type: String def readonly? true end end band = Band.first band.readonly? # => true band.destroy # => raises ReadonlyDocument error