Module: Mongoid::Atomicity

Extended by:
ActiveSupport::Concern
Included in:
Components
Defined in:
lib/mongoid/atomicity.rb

Overview

TODO:

Durran: Refactor class out into separate objects for each type of update.

This module contains the logic for supporting atomic operations against the database.

Instance Method Summary collapse

Instance Method Details

#_updatesHash

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 $pushAll, 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 $pushAll 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.

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.

Examples:

Get the updates that need to occur.

person._updates

Returns:

  • (Hash)

    The updates and their modifiers.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/mongoid/atomicity.rb', line 34

def _updates
  processed = {}

  _children.inject({ "$set" => _sets, "$pushAll" => {}, :other => {} }) do |updates, child|
    changes = child._sets
    updates["$set"].update(changes)
    unless changes.empty?
      processed[child._conflicting_modification_key] = true
    end

    if processed.has_key?(child._conflicting_modification_key)
      target = :other
    else
      target = "$pushAll"
    end

    child._pushes.each do |attr, val|
      if updates[target].has_key?(attr)
        updates[target][attr] << val
      else
        updates[target].update({attr => [val]})
      end
    end
    updates
  end.delete_if do |key, value|
    value.empty?
  end
end