module Mongoid::Atomic
This module contains the logic for supporting atomic operations against the database.
Constants
- UPDATES
Attributes
When MongoDB finally fully implements the positional operator, we can get rid of all indexing related code in Mongoid
.
Public Instance Methods
Add the document as an atomic pull.
@example Add the atomic pull.
person.add_atomic_pull(address)
@param [ Document
] document The embedded document to pull.
# File lib/mongoid/atomic.rb, line 34 def add_atomic_pull(document) document.flagged_for_destroy = true key = document.association_name.to_s delayed_atomic_pulls[key] ||= [] delayed_atomic_pulls[key] << document end
Add an atomic unset for the document.
@example Add an atomic unset.
document.add_atomic_unset(doc)
@param [ Document
] document The child document.
@return [ Array<Document> ] The children.
# File lib/mongoid/atomic.rb, line 49 def add_atomic_unset(document) document.flagged_for_destroy = true key = document.association_name.to_s delayed_atomic_unsets[key] ||= [] delayed_atomic_unsets[key] << document end
For array fields these are the unique adds that need to happen.
@example Get the array unique adds.
person.atomic_array_add_to_sets
@return [ Hash ] The array add_to_sets.
# File lib/mongoid/atomic.rb, line 92 def atomic_array_add_to_sets @atomic_array_add_to_sets ||= {} end
For array fields these are the pulls that need to happen.
@example Get the array pulls.
person.atomic_array_pulls
@return [ Hash ] The array pulls.
# File lib/mongoid/atomic.rb, line 82 def atomic_array_pulls @atomic_array_pulls ||= {} end
For array fields these are the pushes that need to happen.
@example Get the array pushes.
person.atomic_array_pushes
@return [ Hash ] The array pushes.
# File lib/mongoid/atomic.rb, line 72 def atomic_array_pushes @atomic_array_pushes ||= {} end
Returns path of the attribute for modification
@example Get path of the attribute
address.atomic_attribute_name(:city)
@return [ String ] The path to the document attribute in the database
# File lib/mongoid/atomic.rb, line 62 def atomic_attribute_name(name) embedded? ? "#{atomic_position}.#{name}" : name end
Get the removal modifier for the document. Will be nil on root documents, $unset on embeds_one, $set on embeds_many.
@example Get the removal operator.
name.atomic_delete_modifier
@return [ String ] The pull or unset operation.
# File lib/mongoid/atomic.rb, line 139 def atomic_delete_modifier atomic_paths.delete_modifier end
Get the insertion modifier for the document. Will be nil on root documents, $set on embeds_one, $push on embeds_many.
@example Get the insert operation.
name.atomic_insert_modifier
@return [ String ] The pull or set operator.
# File lib/mongoid/atomic.rb, line 150 def atomic_insert_modifier atomic_paths.insert_modifier end
Return the path to this Document
in JSON notation, used for atomic updates via $set in MongoDB.
@example Get the path to this document.
address.atomic_path
@return [ String ] The path to the document in the database.
# File lib/mongoid/atomic.rb, line 161 def atomic_path atomic_paths.path end
Get the atomic paths utility for this document.
@example Get the atomic paths.
document.atomic_paths
@return [ Object
] The associated path.
# File lib/mongoid/atomic.rb, line 181 def atomic_paths return @atomic_paths if @atomic_paths paths = if _association _association.path(self) else Atomic::Paths::Root.new(self) end paths.tap { @atomic_paths = paths unless new_record? } end
Returns the positional operator of this document for modification.
@example Get the positional operator.
address.atomic_position
@return [ String ] The positional operator with indexes.
# File lib/mongoid/atomic.rb, line 171 def atomic_position atomic_paths.position end
Get all the attributes that need to be pulled.
@example Get the pulls.
person.atomic_pulls
@return [ Array<Hash> ] The $pullAll operations.
# File lib/mongoid/atomic.rb, line 199 def atomic_pulls pulls = {} delayed_atomic_pulls.each_pair do |_, docs| path = nil ids = docs.map do |doc| path ||= doc.flag_as_destroyed doc._id end pulls[path] = { '_id' => { '$in' => ids } } and path = nil end pulls end
Get all the push attributes that need to occur.
@example Get the pushes.
person.atomic_pushes
@return [ Hash ] The $push and $each operations.
# File lib/mongoid/atomic.rb, line 218 def atomic_pushes pushable? ? { atomic_position => as_attributes } : {} end
Get all the attributes that need to be set.
@example Get the sets.
person.atomic_sets
@return [ Hash ] The $set operations.
# File lib/mongoid/atomic.rb, line 228 def atomic_sets if updateable? setters elsif settable? { atomic_path => as_attributes } else {} end end
Get all the attributes that need to be unset.
@example Get the unsets.
person.atomic_unsets
@return [ Array<Hash> ] The $unset operations.
# File lib/mongoid/atomic.rb, line 244 def atomic_unsets unsets = [] delayed_atomic_unsets.each_pair do |name, docs| path = nil docs.each do |doc| path ||= doc.flag_as_destroyed end unsets.push(path || name) end unsets end
Get all the atomic updates that need to happen for the current Document
. This includes all changes that need to happen in the entire hierarchy that exists below where the save call was made.
@note MongoDB does not allow “conflicting modifications” to be
performed in a single operation. Conflicting modifications are detected by the 'haveConflictingMod' function in MongoDB. Examination of the code suggests that two modifications (a $set and a $push with $each, for example) conflict if: (1) the key paths being modified are equal. (2) one key path is a prefix of the other. So a $set of 'addresses.0.street' will conflict with a $push and $each to 'addresses', and we will need to split our update into two pieces. We do not, however, attempt to match MongoDB's logic exactly. Instead, we assume that two updates conflict if the first component of the two key paths matches.
@example Get the updates that need to occur.
person.atomic_updates(children)
@return [ Hash ] The updates and their modifiers.
rubocop:disable Style/OptionalBooleanParameter
# File lib/mongoid/atomic.rb, line 119 def atomic_updates(_use_indexes = false) process_flagged_destroys mods = Modifiers.new generate_atomic_updates(mods, self) _descendants.each do |child| child.process_flagged_destroys generate_atomic_updates(mods, child) end mods end
Get a hash of atomic pulls that are pending.
@example Get the atomic pulls.
document.delayed_atomic_pulls
@return [ Hash ] name/document pairs.
# File lib/mongoid/atomic.rb, line 272 def delayed_atomic_pulls @delayed_atomic_pulls ||= {} end
Get all the atomic sets that have had their saves delayed.
@example Get the delayed atomic sets.
person.delayed_atomic_sets
@return [ Hash ] The delayed $sets.
# File lib/mongoid/atomic.rb, line 262 def delayed_atomic_sets @delayed_atomic_sets ||= {} end
Get the delayed atomic unsets.
@example Get the delayed atomic unsets.
document.delayed_atomic_unsets
@return [ Hash ] The atomic unsets
# File lib/mongoid/atomic.rb, line 282 def delayed_atomic_unsets @delayed_atomic_unsets ||= {} end
Flag the document as destroyed and return the atomic path.
@example Flag destroyed and return path.
document.flag_as_destroyed
@return [ String ] The atomic path.
# File lib/mongoid/atomic.rb, line 292 def flag_as_destroyed self.destroyed = true self.flagged_for_destroy = false atomic_path end
Get the flagged destroys.
@example Get the flagged destroy.
document.flagged_destroys
@return [ Array<Proc> ] The flagged destroys.
# File lib/mongoid/atomic.rb, line 304 def flagged_destroys @flagged_destroys ||= [] end
Process all the pending flagged destroys from nested attributes.
@example Process all the pending flagged destroys.
document.process_flagged_destroys
@return [ Array ] The cleared array.
# File lib/mongoid/atomic.rb, line 314 def process_flagged_destroys _assigning do flagged_destroys.each(&:call) end flagged_destroys.clear end
Private Instance Methods
Generates the atomic updates in the correct order.
@example Generate the updates.
model.generate_atomic_updates(mods, doc)
@param [ Modifiers
] mods The atomic modifications. @param [ Document
] doc The document to update for.
# File lib/mongoid/atomic.rb, line 337 def generate_atomic_updates(mods, doc) mods.unset(doc.atomic_unsets) mods.pull(doc.atomic_pulls) mods.set(doc.atomic_sets) mods.set(doc.delayed_atomic_sets) mods.push(doc.atomic_pushes) mods.push(doc.atomic_array_pushes) mods.add_to_set(doc.atomic_array_add_to_sets) mods.pull_all(doc.atomic_array_pulls) end
Clears all pending atomic updates.
# File lib/mongoid/atomic.rb, line 324 def reset_atomic_updates! Atomic::UPDATES.each do |update| send(update).clear end end