123456789_123456789_123456789_123456789_123456789_

Module: Mongoid::Association::Embedded::Batchable

Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Included In:
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
Defined in: lib/mongoid/association/embedded/batchable.rb

Overview

Contains behavior for executing operations in batch on embedded documents.

Instance Attribute Summary

Instance Method Summary

::Mongoid::Positional - Included

#positionally

Takes the provided selector and atomic operations and replaces the indexes of the embedded documents with the positional operator when needed.

#process_operations, #process_updates, #replace_index

Instance Attribute Details

#insertable?true | false (readonly, private)

This method is for internal use only.

Are we in a state to be able to batch insert?

Examples:

Can inserts be performed?

batchable.insertable?

Returns:

  • (true | false)

    If inserts can be performed.

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 199

def insertable?
  persistable? && !_assigning? && inserts_valid
end

#inserts_validtrue | false (rw, private)

This method is for internal use only.

Are the inserts currently valid?

Examples:

Are the inserts currently valid.

batchable.inserts_valid

Returns:

  • (true | false)

    If inserts are currently valid.

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 211

def inserts_valid
  @inserts_valid
end

#inserts_valid=(value) ⇒ true | false (rw, private)

This method is for internal use only.

::Set the inserts valid flag.

Examples:

::Set the flag.

batchable.inserts_valid = true

Parameters:

  • value (true | false)

    The flag.

Returns:

  • (true | false)

    The flag.

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 225

def inserts_valid=(value)
  @inserts_valid = value
end

#pathString (rw, private)

This method is for internal use only.

Get the atomic path.

Examples:

Get the atomic path.

batchable.path

Returns:

  • (String)

    The atomic path.

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 260

def path
  @path ||= if _unscoped.empty?
    Mongoid::Atomic::Paths::Embedded::Many.position_without_document(_base, _association)
  else
    _unscoped.first.atomic_path
  end
end

#path=(value) ⇒ String (rw, private)

This method is for internal use only.

::Set the atomic path.

Examples:

::Set the atomic path.

batchable.path = "addresses"

Parameters:

  • value (String)

    The path.

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 289

def path=(value)
  @path = value
end

Instance Method Details

#add_atomic_sets(sets) (private)

This method is for internal use only.

Add the atomic sets to the base document.

Examples:

Add the atomic sets.

batchable.add_atomic_sets([{ field: value }])

Parameters:

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 135

def add_atomic_sets(sets)
  if _assigning?
    _base.delayed_atomic_sets[path].try(:clear)
    _base.collect_children.each do |child|
      child.delayed_atomic_sets.clear
    end
    _base.delayed_atomic_sets[path] = sets
  end
end

#batch_clear(docs) ⇒ Array

Clear all of the docs out of the association in a single swipe.

Examples:

Clear all docs.

batchable.batch_clear(docs)

Parameters:

Returns:

  • (Array)

    The empty array.

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 35

def batch_clear(docs)
  pre_process_batch_remove(docs, :delete)
  unless docs.empty?
    collection.find(selector).update_one(
      positionally(selector, "$unset" => { path => true }),
      session: _session
    )
    # This solves the case in which a user sets, clears and resets an
    # embedded document. Previously, since the embedded document was
    # already marked not a "new_record", it wouldn't be persisted to
    # the second time. This change fixes that and allows it to be persisted.
    docs.each { |doc| doc.new_record = true }
    post_process_batch_remove(docs, :delete)
  end
  _unscoped.clear
end

#batch_insert(docs) ⇒ Array<Hash>

Insert new documents as a batch push ($push with $each). This ensures that all callbacks are run at the appropriate time and only 1 request is made to the database.

Examples:

Execute the batch push.

batchable.batch_insert([ doc_one, doc_two ])

Parameters:

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 23

def batch_insert(docs)
  execute_batch_push(docs)
end

#batch_remove(docs, method = :delete)

Batch remove the provided documents as a $pullAll or $pull.

Examples:

Batch remove the documents.

batchable.batch_remove([ doc_one, doc_two ])

Parameters:

  • docs (Array<Document>)

    The docs to remove.

  • method (Symbol) (defaults to: :delete)

    Delete or destroy.

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 59

def batch_remove(docs, method = :delete)
  # If the _id is nil, we cannot use $pull and delete by searching for
  # the id. Therefore we have to use pullAll with the documents'
  # attributes.
  removals = pre_process_batch_remove(docs, method)
  pulls, pull_alls = removals.partition { |o| !o["_id"].nil? }

  if !_base.persisted?
    post_process_batch_remove(docs, method) unless docs.empty?
    return reindex
  end

  if !docs.empty?
    if !pulls.empty?
      collection.find(selector).update_one(
        positionally(selector, "$pull" => { path => { "_id" => { "$in" => pulls.pluck("_id") } } }),
        session: _session
      )
    end
    if !pull_alls.empty?
      collection.find(selector).update_one(
        positionally(selector, "$pullAll" => { path => pull_alls }),
        session: _session
      )
    end
    post_process_batch_remove(docs, method)
  else
    collection.find(selector).update_one(
      positionally(selector, "$set" => { path => [] }),
      session: _session
    )
  end
  reindex
end

#batch_replace(docs) ⇒ Array<Hash>

Batch replace the provided documents as a $set.

Examples:

Batch replace the documents.

batchable.batch_replace([ doc_one, doc_two ])

Parameters:

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 102

def batch_replace(docs)
  if docs.blank?
    if _assigning? && !empty?
      _base.delayed_atomic_sets.delete(path)
      clear_atomic_path_cache
      _base.add_atomic_unset(first)
      target_duplicate = _target.dup
      pre_process_batch_remove(target_duplicate, :delete)
      post_process_batch_remove(target_duplicate, :delete)
    else
      batch_remove(_target.dup)
    end
  elsif _target != docs
    _base.delayed_atomic_sets.delete(path) unless _assigning?
    docs = normalize_docs(docs).compact
    _target.clear and _unscoped.clear
    _base.delayed_atomic_unsets.delete(path)
    clear_atomic_path_cache
    inserts = execute_batch_set(docs)
    add_atomic_sets(inserts)
  end
end

#clear_atomic_path_cache (private)

This method is for internal use only.

Clear the cache for path and atomic_paths. This method is used when the path method is used, and the association has not been set on the document yet, which can cause path and atomic_paths to be calculated incorrectly later.

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 274

def clear_atomic_path_cache
  self.path = nil
  _base.instance_variable_set("@atomic_paths", nil)
end

#execute_batch_push(docs) ⇒ Array<Hash> (private)

This method is for internal use only.

Perform a batch persist of the provided documents with $push and $each.

Examples:

Perform a batch push.

batchable.execute_batch_push(docs)

Parameters:

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 178

def execute_batch_push(docs)
  self.inserts_valid = true
  pushes = pre_process_batch_insert(docs)
  if insertable?
    collection.find(selector).update_one(
        positionally(selector, '$push' => { path => { '$each' => pushes } }),
        session: _session
    )
    post_process_batch_insert(docs)
  end
  pushes
end

#execute_batch_set(docs) ⇒ Array<Hash> (private)

This method is for internal use only.

Perform a batch persist of the provided documents with a $set.

Examples:

Perform a batch $set.

batchable.execute_batch_set(docs)

Parameters:

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 155

def execute_batch_set(docs)
  self.inserts_valid = true
  inserts = pre_process_batch_insert(docs)
  if insertable?
    collection.find(selector).update_one(
        positionally(selector, '$set' => { path => inserts }),
        session: _session
    )
    post_process_batch_insert(docs)
  end
  inserts
end

#normalize_docs(docs) ⇒ Array<Document> (private)

This method is for internal use only.

Normalize the documents, in case they were provided as an array of hashes.

Examples:

Normalize the docs.

batchable.normalize_docs(docs)

Parameters:

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 240

def normalize_docs(docs)
  if docs.first.is_a?(::Hash)
    docs.map do |doc|
      attributes = { _association: _association, _parent: _base }
      attributes.merge!(doc)
      Factory.build(klass, attributes)
    end
  else
    docs
  end
end

#post_process_batch_insert(docs) ⇒ Enumerable (private)

This method is for internal use only.

Post process the documents after batch insert.

Examples:

Post process the documents.

batchable.post_process_batch_insert(docs)

Parameters:

  • docs (Array<Documents>)

    The inserted docs.

Returns:

  • (Enumerable)

    The document enum.

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 368

def post_process_batch_insert(docs)
  docs.each do |doc|
    doc.new_record = false
    doc.run_after_callbacks(:create, :save)
    doc.post_persist
  end
end

#post_process_batch_remove(docs, method) ⇒ Array<Document> (private)

This method is for internal use only.

Post process the batch removal.

Examples:

Post process the documents.

batchable.post_process_batch_remove(docs, :delete)

Parameters:

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 387

def post_process_batch_remove(docs, method)
  docs.each do |doc|
    doc.run_after_callbacks(:destroy) if method == :destroy
    doc.freeze
    doc.destroyed = true
  end
end

#pre_process_batch_insert(docs) ⇒ Array<Hash> (private)

This method is for internal use only.

Pre processes the batch insert for the provided documents.

Examples:

Pre process the documents.

batchable.pre_process_batch_insert(docs)

Parameters:

Returns:

  • (Array<Hash>)

    The documents as an array of hashes.

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 315

def pre_process_batch_insert(docs)
  docs.map do |doc|
    next unless doc
    append(doc)
    if persistable? && !_assigning?
      self.path = doc.atomic_path unless path
      if doc.valid?(:create)
        doc.run_before_callbacks(:save, :create)
      else
        self.inserts_valid = false
      end
    end
    doc.send(:as_attributes)
  end
end

#pre_process_batch_remove(docs, method) ⇒ Array<Hash> (private)

This method is for internal use only.

Pre process the batch removal.

Examples:

Pre process the documents.

batchable.pre_process_batch_remove(docs, :delete)

Parameters:

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 342

def pre_process_batch_remove(docs, method)
  docs.map do |doc|
    self.path = doc.atomic_path unless path
    execute_callback :before_remove, doc
    unless _assigning?
      doc.apply_destroy_dependencies!
      doc.run_before_callbacks(:destroy) if method == :destroy
    end
    _target.delete_one(doc)
    _unscoped.delete_one(doc)
    unbind_one(doc)
    execute_callback :after_remove, doc
    doc.send(:as_attributes)
  end
end

#selectorHash (private)

This method is for internal use only.

Get the selector for executing atomic operations on the collection.

Examples:

Get the selector.

batchable.selector

Returns:

  • (Hash)

    The atomic selector.

[ GitHub ]

  
# File 'lib/mongoid/association/embedded/batchable.rb', line 301

def selector
  @selector ||= _base.atomic_selector
end