123456789_123456789_123456789_123456789_123456789_

Class: Mongoid::Atomic::Modifiers

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
Instance Chain:
Inherits: Hash
Defined in: lib/mongoid/atomic/modifiers.rb

Overview

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

Class Attribute Summary

::Mongoid::Extensions::Hash::ClassMethods - Extended

resizable?

Can the size of this object change?

Class Method Summary

::Mongoid::Extensions::Hash::ClassMethods - Extended

mongoize

Turn the object from the ruby type we deal with to a Mongo friendly type.

Instance Attribute Summary

::Mongoid::Extensions::Hash - Included

#resizable?

Can the size of this object change?

Instance Method Summary

::Mongoid::Extensions::Hash - Included

#__consolidate__

Consolidate the key/values in the hash under an atomic $set.

#__evolve_object_id__

Evolves each value in the hash to an object id if it is convertable.

#__mongoize_object_id__

Mongoizes each value in the hash to an object id if it is convertable.

#delete_id

Deletes an id value from the hash.

#extract_id

Get the id attribute from this hash, whether it's prefixed with an underscore or is a symbol.

#mongoize

Turn the object from the ruby type we deal with to a Mongo friendly type.

#to_criteria

Convert this hash to a criteria.

Instance Method Details

#add_each_operation(mods, field, value) (private)

Adds or appends an array operation with the $each specifier used in conjunction with $push.

Examples:

Add the operation.

modifications.add_operation(mods, field, value)

Parameters:

  • mods (Hash) —

    The modifications.

  • field (String) —

    The field.

  • value (Hash) —

    The atomic op.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 138

def add_each_operation(mods, field, value)
  if mods.has_key?(field)
    value.each do |val|
      mods[field]['$each'].push(val)
    end
  else
    mods[field] = { '$each' => value }
  end
end

#add_operation(mods, field, value) (private)

Add the operation to the modifications, either appending or creating a new one.

Examples:

Add the operation.

modifications.add_operation(mods, field, value)

Parameters:

  • mods (Hash) —

    The modifications.

  • field (String) —

    The field.

  • value (Hash) —

    The atomic op.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 115

def add_operation(mods, field, value)
  if mods.has_key?(field)
    if mods[field].is_a?(Array)
      value.each do |val|
        mods[field].push(val)
      end
    elsif mods[field]['$each']
      mods[field]['$each'].concat(value['$each'])
    end
  else
    mods[field] = value
  end
end

#add_to_set(modifications)

Add the atomic $addToSet modifiers to the hash.

Examples:

Add the $addToSet modifiers.

modifiers.add_to_set({ "preference_ids" => [ "one" ] })

Parameters:

  • modifications (Hash) —

    The add to set modifiers.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 14

def add_to_set(modifications)
  modifications.each_pair do |field, value|
    mods = add_to_set_conflict?(field) ? conflicting_add_to_sets : add_to_sets
    add_each_operation(mods, field, value)
    add_to_set_fields[field.split('.', 2)[0]] = field
  end
end

#add_to_set_conflict?(field) ⇒ true | false (private)

Is the operation going to be a conflict for an $addToSet?

As with $push, an $addToSet is only split out when the other operation does not shift array element positions. A conflicting $pull is left as-is so MongoDB rejects the update rather than the $addToSet possibly hitting a different element in a separate write.

Parameters:

  • field (String) —

    The field.

Returns:

  • (true | false) —

    If this field is a conflict.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 217

def add_to_set_conflict?(field)
  name = field.split('.', 2)[0]
  set_fields.has_key?(name) || push_fields.has_key?(name)
end

#add_to_set_fields ⇒ Array<String> (private)

Get the names of the fields that need to be added to sets.

Examples:

Get the add to set fields.

modifiers.add_to_set_fields

Returns:

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 359

def add_to_set_fields
  @add_to_set_fields ||= {}
end

#add_to_sets ⇒ Hash (private)

Get the $addToSet operations or initialize a new one.

Examples:

Get the $addToSet operations.

modifiers.add_to_sets

Returns:

  • (Hash) —

    The $addToSet operations.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 154

def add_to_sets
  self['$addToSet'] ||= {}
end

#conflicting_add_to_sets ⇒ Hash (private)

Get the conflicting $addToSet operations.

Examples:

Get the conflicting add to sets.

modifiers.conflicting_add_to_sets

Returns:

  • (Hash) —

    The conflicting add to set operations.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 262

def conflicting_add_to_sets
  conflicts['$addToSet'] ||= {}
end

#conflicting_pull_alls ⇒ Hash (private)

Get the conflicting pull all modifications.

Examples:

Get the conflicting pull alls.

modifiers.conflicting_pull_alls

Returns:

  • (Hash) —

    The conflicting pull all operations.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 282

def conflicting_pull_alls
  conflicts['$pullAll'] ||= {}
end

#conflicting_pulls ⇒ Hash (private)

Get the conflicting pull modifications.

Examples:

Get the conflicting pulls.

modifiers.conflicting_pulls

Returns:

  • (Hash) —

    The conflicting pull operations.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 272

def conflicting_pulls
  conflicts['$pull'] ||= {}
end

#conflicting_pushes ⇒ Hash (private)

Get the conflicting push modifications.

Examples:

Get the conflicting pushs.

modifiers.conflicting_pushs

Returns:

  • (Hash) —

    The conflicting push operations.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 292

def conflicting_pushes
  conflicts['$push'] ||= {}
end

#conflicting_sets ⇒ Hash (private)

Get the conflicting set modifications.

Examples:

Get the conflicting sets.

modifiers.conflicting_sets

Returns:

  • (Hash) —

    The conflicting set operations.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 302

def conflicting_sets
  conflicts['$set'] ||= {}
end

#conflicting_unsets ⇒ Hash (private)

Get the conflicting unset modifications.

Returns:

  • (Hash) —

    The conflicting unset operations.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 309

def conflicting_unsets
  conflicts['$unset'] ||= {}
end

#conflicts ⇒ Hash (private)

Get the push operations that would have conflicted with the sets.

Examples:

Get the conflicts.

modifiers.conflicts

Returns:

  • (Hash) —

    The conflicting modifications.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 319

def conflicts
  self[:conflicts] ||= {}
end

#pull(modifications)

Adds pull all modifiers to the modifiers hash.

Examples:

Add pull all operations.

modifiers.pull({ "addresses" => { "_id" => { "$in" => [ 1, 2, 3 ]}}})

Parameters:

  • modifications (Hash) —

    The pull all modifiers.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 42

def pull(modifications)
  modifications.each_pair do |field, value|
    mods = pull_conflict?(field) ? conflicting_pulls : pulls
    mods[field] = value
    pull_fields[field.split('.', 2)[0]] = field
  end
end

#pull_all(modifications)

Adds pull all modifiers to the modifiers hash.

Examples:

Add pull all operations.

modifiers.pull_all({ "addresses" => { "street" => "Bond" }})

Parameters:

  • modifications (Hash) —

    The pull all modifiers.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 28

def pull_all(modifications)
  modifications.each_pair do |field, value|
    mods = pull_all_conflict?(field) ? conflicting_pull_alls : pull_alls
    add_operation(mods, field, value)
    pull_fields[field.split('.', 2)[0]] = field
  end
end

#pull_all_conflict?(field) ⇒ true | false (private)

Is the operation going to be a conflict for a $pullAll?

Parameters:

  • field (String) —

    The field.

Returns:

  • (true | false) —

    If this field is a conflict.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 238

def pull_all_conflict?(field)
  name = field.split('.', 2)[0]
  set_fields.has_key?(name) || push_fields.has_key?(name) ||
    add_to_set_fields.has_key?(name) || pull_root?(name)
end

#pull_all_root?(name) ⇒ true | false (private)

Is there a $pullAll on the given root-level field? Does not initialize the $pullAll operations.

Parameters:

  • name (String) —

    The root-level field name.

Returns:

  • (true | false) —

    If a $pullAll touches this field.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 441

def pull_all_root?(name)
  (self['$pullAll'] || {}).keys.any? { |key| key.split('.', 2)[0] == name }
end

#pull_alls ⇒ Hash (private)

Get the $pullAll operations or initialize a new one.

Examples:

Get the $pullAll operations.

modifiers.pull_alls

Returns:

  • (Hash) —

    The $pullAll operations.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 379

def pull_alls
  self['$pullAll'] ||= {}
end

#pull_conflict?(field) ⇒ true | false (private)

Is the operation going to be a conflict for a $pull?

Parameters:

  • field (String) —

    The field.

Returns:

  • (true | false) —

    If this field is a conflict.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 227

def pull_conflict?(field)
  name = field.split('.', 2)[0]
  set_fields.has_key?(name) || push_fields.has_key?(name) ||
    add_to_set_fields.has_key?(name) || pull_all_root?(name)
end

#pull_fields ⇒ Array<String> (private)

Get the names of the fields that need to be pulled.

Examples:

Get the pull fields.

modifiers.pull_fields

Returns:

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 329

def pull_fields
  @pull_fields ||= {}
end

#pull_root?(name) ⇒ true | false (private)

Is there a $pull on the given root-level field? Does not initialize the $pull operations.

Parameters:

  • name (String) —

    The root-level field name.

Returns:

  • (true | false) —

    If a $pull touches this field.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 431

def pull_root?(name)
  (self['$pull'] || {}).keys.any? { |key| key.split('.', 2)[0] == name }
end

#pulls ⇒ Hash (private)

Get the $pull operations or initialize a new one.

Examples:

Get the $pull operations.

modifiers.pulls

Returns:

  • (Hash) —

    The $pull operations.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 389

def pulls
  self['$pull'] ||= {}
end

#push(modifications)

Adds push modifiers to the modifiers hash.

Examples:

Add push operations.

modifiers.push({ "addresses" => { "street" => "Bond" }})

Parameters:

  • modifications (Hash) —

    The push modifiers.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 56

def push(modifications)
  modifications.each_pair do |field, value|
    push_fields[field] = field
    mods = push_conflict?(field) ? conflicting_pushes : pushes
    add_operation(mods, field, { '$each' => Array.wrap(value) })
  end
end

#push_conflict?(field) ⇒ true | false (private)

Is the operation going to be a conflict for a $push?

Examples:

Is this a conflict for a push?

modifiers.push_conflict?(field)

Parameters:

  • field (String) —

    The field.

Returns:

  • (true | false) —

    If this field is a conflict.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 200

def push_conflict?(field)
  name = field.split('.', 2)[0]
  set_fields.has_key?(name) || pull_fields.has_key?(name) ||
    add_to_set_fields.has_key?(name) || unset_fields.has_key?(name) ||
    (push_fields.keys.count { |item| item.split('.', 2).first == name } > 1)
end

#push_fields ⇒ Array<String> (private)

Get the names of the fields that need to be pushed.

Examples:

Get the push fields.

modifiers.push_fields

Returns:

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 339

def push_fields
  @push_fields ||= {}
end

#pushes ⇒ Hash (private)

Get the $push/$each operations or initialize a new one.

Examples:

Get the $push/$each operations.

modifiers.pushes

Returns:

  • (Hash) —

    The $push/$each operations.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 399

def pushes
  self['$push'] ||= {}
end

#set(modifications)

Adds set operations to the modifiers hash.

Examples:

Add set operations.

modifiers.set({ "title" => "sir" })

Parameters:

  • modifications (Hash) —

    The set modifiers.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 70

def set(modifications)
  modifications.each_pair do |field, value|
    next if field == '_id'

    mods = set_conflict?(field) ? conflicting_sets : sets
    add_operation(mods, field, value)
    set_fields[field.split('.', 2)[0]] = field
  end
end

#set_conflict?(field) ⇒ true | false (private)

Is the operation going to be a conflict for a $set?

Examples:

Is this a conflict for a set?

modifiers.set_conflict?(field)

Parameters:

  • field (String) —

    The field.

Returns:

  • (true | false) —

    If this field is a conflict.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 166

def set_conflict?(field)
  name = field.split('.', 2)[0]
  pull_fields.has_key?(name) || push_fields.has_key?(name) ||
    add_to_set_fields.has_key?(name)
end

#set_fields ⇒ Array<String> (private)

Get the names of the fields that need to be set.

Examples:

Get the set fields.

modifiers.set_fields

Returns:

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 349

def set_fields
  @set_fields ||= {}
end

#sets(initialize: true) ⇒ Hash (private)

Get the $set operations or initialize a new one.

Examples:

Get the $set operations.

modifiers.sets

Returns:

  • (Hash) —

    The $set operations.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 409

def sets(initialize: true)
  return self['$set'] unless initialize

  self['$set'] ||= {}
end

#unset(modifications)

Adds unset operations to the modifiers hash.

Examples:

Add unset operations.

modifiers.unset([ "addresses" ])

Parameters:

  • modifications (Array<String>) —

    The unset association names.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 86

def unset(modifications)
  modifications.each do |field|
    field = field.to_s

    if unset_conflict?(field)
      # If the conflicting $set covers the entire parent field (not just a
      # sub-path), it writes the complete current state, which already
      # reflects this unset. Skip the $unset — it's redundant.
      next if unset_superseded_by_set?(field)

      conflicting_unsets.update(field => true)
    else
      unsets.update(field => true)
    end
    unset_fields[field.split('.', 2)[0]] = field
  end
end

#unset_conflict?(field) ⇒ true | false (private)

Is the operation going to be a conflict for an $unset?

A $push or $addToSet does not shift existing array element positions, so a conflicting $unset can be safely applied in a separate write afterwards. A $pull does shift positions, so the $unset is not split out in that case; the operations stay in the same update and MongoDB rejects the update (code 40) without changing stored data, rather than risking the $unset hitting a different element.

Parameters:

  • field (String) —

    The field.

Returns:

  • (true | false) —

    If this field is a conflict.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 184

def unset_conflict?(field)
  key = field.split('.', 2)[0]
  return true if set_fields.has_key?(key)
  return false if pull_fields.has_key?(key)

  push_fields.has_key?(key) || add_to_set_fields.has_key?(key)
end

#unset_fields ⇒ Array<String> (private)

Get the names of the fields that need to be unset.

Examples:

Get the unset fields.

modifiers.unset_fields

Returns:

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 369

def unset_fields
  @unset_fields ||= {}
end

#unset_superseded_by_set?(field) ⇒ Boolean (private)

Returns true if the $unset is made redundant by a $set that covers the entire root-level field. When $set "children" is issued, the current (live) state of the array already includes all pending changes (e.g., embedded association removed), so a $unset for any children.*.x field is unnecessary.

Parameters:

  • field (String) —

    the field

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 251

def unset_superseded_by_set?(field)
  key = field.split('.', 2).first
  sets(initialize: false)&.has_key?(key)
end

#unsets ⇒ Hash (private)

Get the $unset operations or initialize a new one.

Examples:

Get the $unset operations.

modifiers.unsets

Returns:

  • (Hash) —

    The $unset operations.

[ GitHub ]

  
# File 'lib/mongoid/atomic/modifiers.rb', line 421

def unsets
  self['$unset'] ||= {}
end