123456789_123456789_123456789_123456789_123456789_

Class: Mongoid::Association::EagerLoad::DiscriminatedInclusion Private

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Inclusion
Instance Chain:
self, Inclusion
Inherits: Mongoid::Association::EagerLoad::Inclusion
Defined in: lib/mongoid/association/eager_load/discriminated_inclusion.rb

Overview

Loads an inclusion that more than one subclass defines under the same name but pointing at different targets. A single $lookup can't serve them: they would all write to the same field and overwrite one another. So each subclass's inclusion is contributed into its own temporary field, carrying its own nested children, and a $set then routes every document to the field for its own type, by the discriminator.

For Machine.eager_load(:widgets), where Lathe#widgets => Cog and Press#widgets => Belt, it emits:

{ '$lookup' => { 'from' => 'cogs',  ..., 'as' => '__eager_load_widgets_Lathe' } },
{ '$lookup' => { 'from' => 'belts', ..., 'as' => '__eager_load_widgets_Press' } },
{ '$set' => {
'widgets' => { '$switch' => { 'branches' => [   # route each document to its
  { 'case' => { '$eq' => [ '$_type', 'Lathe' ] }, 'then' => '$__eager_load_widgets_Lathe' },  # own type's matches
  { 'case' => { '$eq' => [ '$_type', 'Press' ] }, 'then' => '$__eager_load_widgets_Press' }
], 'default' => [] } }
} },
{ '$unset' => [ '__eager_load_widgets_Lathe', '__eager_load_widgets_Press' ] }

Class Method Summary

Instance Method Summary

Inclusion - Inherited

#contribute

Add this inclusion's stages to the destination.

Constructor Details

.new(nodes) ⇒ DiscriminatedInclusion

[ GitHub ]

  
# File 'lib/mongoid/association/eager_load/discriminated_inclusion.rb', line 30

def initialize(nodes)
  super()
  @nodes = nodes
end

Instance Method Details

#contribute(destination, _chain)

Append each subclass's lookup (into its own temporary field), the routing $set, and the cleanup $unset.

Parameters:

  • destination (Array<Hash>)

    The pipeline the stages are appended to.

[ GitHub ]

  
# File 'lib/mongoid/association/eager_load/discriminated_inclusion.rb', line 39

def contribute(destination, _chain)
  fields = @nodes.map { |node| [ node, contribute_into_temporary(destination, node) ] }
  destination << route_by_type(fields)
  destination << { '$unset' => fields.map { |_node, field| field } }
end

#contribute_into_temporary(destination, node) (private)

Let the node build its own $lookup (with its nested children) and redirect it to write into a temporary field instead of the shared association name.

[ GitHub ]

  
# File 'lib/mongoid/association/eager_load/discriminated_inclusion.rb', line 49

def contribute_into_temporary(destination, node)
  field = temporary_field(node)
  captured = []
  node.contribute(captured, [])
  captured.first['$lookup']['as'] = field
  destination.concat(captured)
  field
end

#discriminator_key (private)

[ GitHub ]

  
# File 'lib/mongoid/association/eager_load/discriminated_inclusion.rb', line 82

def discriminator_key
  owner(@nodes.first).discriminator_key
end

#name (private)

[ GitHub ]

  
# File 'lib/mongoid/association/eager_load/discriminated_inclusion.rb', line 78

def name
  @nodes.first.association.name.to_s
end

#owner(node) (private)

[ GitHub ]

  
# File 'lib/mongoid/association/eager_load/discriminated_inclusion.rb', line 74

def owner(node)
  node.association.inverse_class
end

#route_by_type(fields) (private)

The $set that fills the association on each document from the temporary field matching its own type.

[ GitHub ]

  
# File 'lib/mongoid/association/eager_load/discriminated_inclusion.rb', line 64

def route_by_type(fields)
  branches = fields.map do |node, field|
    {
      'case' => { '$eq' => [ "$#{discriminator_key}", owner(node).discriminator_value ] },
      'then' => "$#{field}"
    }
  end
  { '$set' => { name => { '$switch' => { 'branches' => branches, 'default' => [] } } } }
end

#temporary_field(node) (private)

[ GitHub ]

  
# File 'lib/mongoid/association/eager_load/discriminated_inclusion.rb', line 58

def temporary_field(node)
  "__eager_load_#{node.association.name}_#{owner(node).discriminator_value}"
end