class Sequel::Model::Associations::AssociationReflection

AssociationReflection is a Hash subclass that keeps information on Sequel::Model associations. It provides methods to reduce internal code duplication. It should not be instantiated by the user.

Constants

ASSOCIATION_DATASET_PROC
FINALIZE_SETTINGS

Map of methods to cache keys used for finalizing associations.

Public Instance Methods

_add_method() click to toggle source

Name symbol for the _add internal association method

   # File lib/sequel/model/associations.rb
36 def _add_method
37   self[:_add_method]
38 end
_remove_all_method() click to toggle source

Name symbol for the _remove_all internal association method

   # File lib/sequel/model/associations.rb
41 def _remove_all_method
42   self[:_remove_all_method]
43 end
_remove_method() click to toggle source

Name symbol for the _remove internal association method

   # File lib/sequel/model/associations.rb
46 def _remove_method
47   self[:_remove_method]
48 end
_setter_method() click to toggle source

Name symbol for the _setter association method

   # File lib/sequel/model/associations.rb
51 def _setter_method
52   self[:_setter_method]
53 end
add_method() click to toggle source

Name symbol for the add association method

   # File lib/sequel/model/associations.rb
56 def add_method
57   self[:add_method]
58 end
apply_dataset_changes(ds) click to toggle source

Apply all non-instance specific changes to the given dataset and return it.

   # File lib/sequel/model/associations.rb
84 def apply_dataset_changes(ds)
85   ds = ds.with_extend(AssociationDatasetMethods).clone(:association_reflection => self)
86   if exts = self[:reverse_extend]
87     ds = ds.with_extend(*exts)
88   end
89   ds = ds.select(*select) if select
90   if c = self[:conditions]
91     ds = (c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.where(*c) : ds.where(c)
92   end
93   ds = ds.order(*self[:order]) if self[:order]
94   ds = ds.limit(*self[:limit]) if self[:limit]
95   ds = ds.limit(1).skip_limit_check if limit_to_single_row?
96   ds = ds.eager(self[:eager]) if self[:eager]
97   ds = ds.distinct if self[:distinct]
98   ds
99 end
apply_distinct_on_eager_limit_strategy(ds) click to toggle source

Use DISTINCT ON and ORDER BY clauses to limit the results to the first record with matching keys.

    # File lib/sequel/model/associations.rb
140 def apply_distinct_on_eager_limit_strategy(ds)
141   keys = predicate_key
142   ds.distinct(*keys).order_prepend(*keys)
143 end
apply_eager_dataset_changes(ds) click to toggle source

Apply all non-instance specific changes and the eager_block option to the given dataset and return it.

    # File lib/sequel/model/associations.rb
103 def apply_eager_dataset_changes(ds)
104   ds = apply_dataset_changes(ds)
105   if block = self[:eager_block]
106     ds = block.call(ds)
107   end
108   ds
109 end
apply_eager_graph_limit_strategy(strategy, ds) click to toggle source

Apply the eager graph limit strategy to the dataset to graph into the current dataset, or return the dataset unmodified if no SQL limit strategy is needed.

    # File lib/sequel/model/associations.rb
113 def apply_eager_graph_limit_strategy(strategy, ds)
114   case strategy
115   when :lateral_subquery
116     apply_lateral_subquery_eager_graph_limit_strategy(ds)
117   when :distinct_on
118     apply_distinct_on_eager_limit_strategy(ds.order_prepend(*self[:order]))
119   when :window_function
120     apply_window_function_eager_limit_strategy(ds.order_prepend(*self[:order])).select(*ds.columns)
121   else
122     ds
123   end
124 end
apply_eager_limit_strategy(ds, strategy=eager_limit_strategy, limit_and_offset=limit_and_offset()) click to toggle source

Apply an eager limit strategy to the dataset, or return the dataset unmodified if it doesn’t need an eager limit strategy.

    # File lib/sequel/model/associations.rb
128 def apply_eager_limit_strategy(ds, strategy=eager_limit_strategy, limit_and_offset=limit_and_offset())
129   case strategy
130   when :distinct_on
131     apply_distinct_on_eager_limit_strategy(ds)
132   when :window_function
133     apply_window_function_eager_limit_strategy(ds, limit_and_offset)
134   else
135     ds
136   end
137 end
apply_ruby_eager_limit_strategy(rows, limit_and_offset = limit_and_offset()) click to toggle source

If the ruby eager limit strategy is being used, slice the array using the slice range to return the object(s) at the correct offset/limit.

    # File lib/sequel/model/associations.rb
167 def apply_ruby_eager_limit_strategy(rows, limit_and_offset = limit_and_offset())
168   name = self[:name]
169   return unless range = slice_range(limit_and_offset)
170   if returns_array?
171     rows.each{|o| o.associations[name] = o.associations[name][range] || []}
172   else
173     offset = range.begin
174     rows.each{|o| o.associations[name] = o.associations[name][offset]}
175   end
176 end
apply_window_function_eager_limit_strategy(ds, limit_and_offset=limit_and_offset()) click to toggle source

Use a window function to limit the results of the eager loading dataset.

    # File lib/sequel/model/associations.rb
146 def apply_window_function_eager_limit_strategy(ds, limit_and_offset=limit_and_offset())
147   rn = ds.row_number_column 
148   limit, offset = limit_and_offset
149   ds = ds.unordered.select_append{|o| o.row_number.function.over(:partition=>predicate_key, :order=>ds.opts[:order]).as(rn)}.from_self
150   ds = ds.order(rn) if ds.db.database_type == :mysql
151   ds = if !returns_array?
152     ds.where(rn => offset ? offset+1 : 1)
153   elsif offset
154     offset += 1
155     if limit
156       ds.where(rn => (offset...(offset+limit))) 
157     else
158       ds.where{SQL::Identifier.new(rn) >= offset} 
159     end
160   else
161     ds.where{SQL::Identifier.new(rn) <= limit} 
162   end
163 end
assign_singular?() click to toggle source

Whether the associations cache should use an array when storing the associated records during eager loading.

    # File lib/sequel/model/associations.rb
180 def assign_singular?
181   !returns_array?
182 end
associated_class() click to toggle source

The class associated to the current model class via this association

   # File lib/sequel/model/associations.rb
66 def associated_class
67   cached_fetch(:class) do
68     begin
69       constantize(self[:class_name])
70     rescue NameError => e
71       raise NameError, "#{e.message} (this happened when attempting to find the associated class for #{inspect})", e.backtrace
72     end
73   end
74 end
associated_dataset() click to toggle source

The dataset associated via this association, with the non-instance specific changes already applied. This will be a joined dataset if the association requires joining tables.

   # File lib/sequel/model/associations.rb
79 def associated_dataset
80   cached_fetch(:_dataset){apply_dataset_changes(_associated_dataset)}
81 end
association_dataset_for(object) click to toggle source

Return an dataset that will load the appropriate associated objects for the given object using this association.

    # File lib/sequel/model/associations.rb
217 def association_dataset_for(object)
218   condition = if can_have_associated_objects?(object)
219     predicate_keys.zip(predicate_key_values(object))
220   else
221     false
222   end
223 
224   associated_dataset.where(condition)
225 end
association_dataset_proc() click to toggle source

Proc used to create the association dataset method.

    # File lib/sequel/model/associations.rb
229 def association_dataset_proc
230   ASSOCIATION_DATASET_PROC
231 end
association_method() click to toggle source

Name symbol for association method, the same as the name of the association.

   # File lib/sequel/model/associations.rb
61 def association_method
62   self[:name]
63 end
can_have_associated_objects?(obj) click to toggle source

Whether this association can have associated objects, given the current object. Should be false if obj cannot have associated objects because the necessary key columns are NULL.

    # File lib/sequel/model/associations.rb
187 def can_have_associated_objects?(obj)
188   true
189 end
cloneable?(ref) click to toggle source

Whether you are able to clone from the given association type to the current association type, true by default only if the types match.

    # File lib/sequel/model/associations.rb
193 def cloneable?(ref)
194   ref[:type] == self[:type]
195 end
dataset_method() click to toggle source

Name symbol for the dataset association method

    # File lib/sequel/model/associations.rb
198 def dataset_method
199   self[:dataset_method]
200 end
dataset_need_primary_key?() click to toggle source

Whether the dataset needs a primary key to function, true by default.

    # File lib/sequel/model/associations.rb
203 def dataset_need_primary_key?
204   true
205 end
delete_row_number_column(ds=associated_dataset) click to toggle source

Return the symbol used for the row number column if the window function eager limit strategy is being used, or nil otherwise.

    # File lib/sequel/model/associations.rb
209 def delete_row_number_column(ds=associated_dataset)
210   if eager_limit_strategy == :window_function
211     ds.row_number_column 
212   end
213 end
eager_graph_lazy_dataset?() click to toggle source

Whether to eagerly graph a lazy dataset, true by default. If this is false, the association won’t respect the :eager_graph option when loading the association for a single record.

    # File lib/sequel/model/associations.rb
361 def eager_graph_lazy_dataset?
362   true
363 end
eager_graph_limit_strategy(strategy) click to toggle source

The eager_graph limit strategy to use for this dataset

    # File lib/sequel/model/associations.rb
234 def eager_graph_limit_strategy(strategy)
235   if self[:limit] || !returns_array?
236     strategy = strategy[self[:name]] if strategy.is_a?(Hash)
237     case strategy
238     when true
239       true_eager_graph_limit_strategy
240     when Symbol
241       strategy
242     else
243       if returns_array? || offset
244         :ruby
245       end
246     end
247   end
248 end
eager_limit_strategy() click to toggle source

The eager limit strategy to use for this dataset.

    # File lib/sequel/model/associations.rb
251 def eager_limit_strategy
252   cached_fetch(:_eager_limit_strategy) do
253     if self[:limit] || !returns_array?
254       case s = cached_fetch(:eager_limit_strategy){default_eager_limit_strategy}
255       when true
256         true_eager_limit_strategy
257       else
258         s
259       end
260     end
261   end
262 end
eager_load_results(eo, &block) click to toggle source

Eager load the associated objects using the hash of eager options, yielding each row to the block.

    # File lib/sequel/model/associations.rb
266 def eager_load_results(eo, &block)
267   rows = eo[:rows]
268   unless eo[:initialize_rows] == false
269     Sequel.synchronize_with(eo[:mutex]){initialize_association_cache(rows)}
270   end
271   if eo[:id_map]
272     ids = eo[:id_map].keys
273     return ids if ids.empty?
274   end
275   strategy = eager_limit_strategy
276   cascade = eo[:associations]
277   eager_limit = nil
278 
279   if eo[:no_results]
280     no_results = true
281   elsif eo[:eager_block] || eo[:loader] == false || !use_placeholder_loader?
282     ds = eager_loading_dataset(eo)
283 
284     strategy = ds.opts[:eager_limit_strategy] || strategy
285 
286     eager_limit =
287       if el = ds.opts[:eager_limit]
288         raise Error, "The :eager_limit dataset option is not supported for associations returning a single record" unless returns_array?
289         strategy ||= true_eager_graph_limit_strategy
290         if el.is_a?(Array)
291           el
292         else
293           [el, nil]
294         end
295       else
296         limit_and_offset
297       end
298 
299     strategy = true_eager_graph_limit_strategy if strategy == :union
300     # Correlated subqueries are not supported for regular eager loading
301     strategy = :ruby if strategy == :correlated_subquery
302     strategy = nil if strategy == :ruby && assign_singular?
303 
304     objects = if strategy == :lateral_subquery
305       apply_lateral_subquery_eager_limit_strategy(ds, ids, eager_limit).all
306     else
307       apply_eager_limit_strategy(ds, strategy, eager_limit).all
308     end
309 
310     if strategy == :window_function
311       delete_rn = ds.row_number_column 
312       objects.each{|obj| obj.remove_key!(delete_rn)}
313     end
314   elsif strategy == :union
315     objects = []
316     ds = associated_dataset
317     loader = union_eager_loader
318     joiner = " UNION ALL "
319     ids.each_slice(subqueries_per_union).each do |slice|
320       sql = loader.send(:sql_origin)
321       join = false
322       slice.each do |k|
323         if join
324           sql << joiner
325         else
326           join = true
327         end
328         loader.append_sql(sql, *k)
329       end
330       objects.concat(ds.with_sql(sql.freeze).to_a)
331     end
332     ds = ds.eager(cascade) if cascade
333     ds.send(:post_load, objects)
334   else
335     loader = placeholder_eager_loader
336     loader = loader.with_dataset{|dataset| dataset.eager(cascade)} if cascade
337     objects = loader.all(ids)
338   end
339 
340   Sequel.synchronize_with(eo[:mutex]){objects.each(&block)} unless no_results
341 
342   if strategy == :ruby
343     apply_ruby_eager_limit_strategy(rows, eager_limit || limit_and_offset)
344   end
345 end
eager_loader_key() click to toggle source

The key to use for the key hash when eager loading

    # File lib/sequel/model/associations.rb
348 def eager_loader_key
349   self[:eager_loader_key]
350 end
eager_loading_use_associated_key?() click to toggle source

By default associations do not need to select a key in an associated table to eagerly load.

    # File lib/sequel/model/associations.rb
354 def eager_loading_use_associated_key?
355   false
356 end
filter_by_associations_add_conditions?() click to toggle source

Whether additional conditions should be added when using the filter by associations support.

    # File lib/sequel/model/associations.rb
367 def filter_by_associations_add_conditions?
368   self[:conditions] || self[:eager_block] || self[:limit]
369 end
filter_by_associations_conditions_expression(obj) click to toggle source

The expression to use for the additional conditions to be added for the filter by association support, when the association itself is filtered. Works by using a subquery to test that the objects passed also meet the association filter criteria.

    # File lib/sequel/model/associations.rb
375 def filter_by_associations_conditions_expression(obj)
376   ds = if filter_by_associations_limit_strategy == :lateral_subquery
377     apply_lateral_subquery_filter_limit_strategy(associated_eager_dataset, obj)
378   else
379     filter_by_associations_conditions_dataset.where(filter_by_associations_conditions_subquery_conditions(obj))
380   end
381 
382   {filter_by_associations_conditions_key=>ds}
383 end
finalize() click to toggle source

Finalize the association by first attempting to populate the thread-safe cache, and then transfering the thread-safe cache value to the association itself, so that a mutex is not needed to get the value.

    # File lib/sequel/model/associations.rb
388 def finalize
389   return unless cache = self[:cache]
390 
391   finalizer = proc do |meth, key|
392     next if has_key?(key)
393 
394     # Allow calling private methods to make sure caching is done appropriately
395     send(meth)
396     self[key] = cache.delete(key) if cache.has_key?(key)
397   end
398 
399   finalize_settings.each(&finalizer)
400 
401   unless self[:instance_specific]
402     finalizer.call(:associated_eager_dataset, :associated_eager_dataset)
403     finalizer.call(:filter_by_associations_conditions_dataset, :filter_by_associations_conditions_dataset)
404   end
405 
406   nil
407 end
finalize_settings() click to toggle source
    # File lib/sequel/model/associations.rb
419 def finalize_settings
420   FINALIZE_SETTINGS
421 end
handle_silent_modification_failure?() click to toggle source

Whether to handle silent modification failure when adding/removing associated records, false by default.

    # File lib/sequel/model/associations.rb
425 def handle_silent_modification_failure?
426   false
427 end
hash() click to toggle source

Hash value for the association reflection. This is precomputed to avoid concurrency issues at runtime.

    # File lib/sequel/model/associations.rb
431 def hash
432   self[:_hash]
433 end
initialize_association_cache(objects) click to toggle source

Initialize the associations cache for the current association for the given objects.

    # File lib/sequel/model/associations.rb
436 def initialize_association_cache(objects)
437   name = self[:name]
438   if assign_singular?
439     objects.each{|object| object.associations[name] = nil}
440   else
441     objects.each{|object| object.associations[name] = []}
442   end
443 end
inspect() click to toggle source

Show which type of reflection this is, and a guess at what code was used to create the association.

    # File lib/sequel/model/associations.rb
447 def inspect
448   o = self[:orig_opts].dup
449   o.delete(:class)
450   o.delete(:class_name)
451   o.delete(:block) unless o[:block]
452   o[:class] = self[:orig_class] if self[:orig_class]
453 
454   "#<#{self.class} #{self[:model]}.#{self[:type]} #{self[:name].inspect}#{", #{o.inspect[1...-1]}" unless o.empty?}>"
455 end
limit_and_offset() click to toggle source

The limit and offset for this association (returned as a two element array).

    # File lib/sequel/model/associations.rb
458 def limit_and_offset
459   if (v = self[:limit]).is_a?(Array)
460     v
461   else
462     [v, nil]
463   end
464 end
need_associated_primary_key?() click to toggle source

Whether the associated object needs a primary key to be added/removed, false by default.

    # File lib/sequel/model/associations.rb
468 def need_associated_primary_key?
469   false
470 end
placeholder_loader() click to toggle source

A placeholder literalizer that can be used to lazily load the association. If one can’t be used, returns nil.

    # File lib/sequel/model/associations.rb
474 def placeholder_loader
475   if use_placeholder_loader?
476     cached_fetch(:placeholder_loader) do
477       associated_dataset.placeholder_literalizer_loader do |pl, ds|
478         ds = ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
479         if self[:block]
480           ds = self[:block].call(ds)
481         end
482         ds
483       end
484     end
485   end
486 end
predicate_key_values(object) click to toggle source

The values that predicate_keys should match for objects to be associated.

    # File lib/sequel/model/associations.rb
494 def predicate_key_values(object)
495   predicate_key_methods.map{|k| object.get_column_value(k)}
496 end
predicate_keys() click to toggle source

The keys to use for loading of the regular dataset, as an array.

    # File lib/sequel/model/associations.rb
489 def predicate_keys
490   cached_fetch(:predicate_keys){Array(predicate_key)}
491 end
qualify(table, col) click to toggle source

Qualify col with the given table name.

    # File lib/sequel/model/associations.rb
499 def qualify(table, col)
500   transform(col) do |k|
501     case k
502     when Symbol, SQL::Identifier
503       SQL::QualifiedIdentifier.new(table, k)
504     else
505       Sequel::Qualifier.new(table).transform(k)
506     end
507   end
508 end
qualify_assoc(col) click to toggle source

Qualify col with the associated model’s table name.

    # File lib/sequel/model/associations.rb
511 def qualify_assoc(col)
512   qualify(associated_class.table_name, col)
513 end
qualify_cur(col) click to toggle source

Qualify col with the current model’s table name.

    # File lib/sequel/model/associations.rb
516 def qualify_cur(col)
517   qualify(self[:model].table_name, col)
518 end
reciprocal() click to toggle source

Returns the reciprocal association variable, if one exists. The reciprocal association is the association in the associated class that is the opposite of the current association. For example, Album.many_to_one :artist and Artist.one_to_many :albums are reciprocal associations. This information is to populate reciprocal associations. For example, when you do this_artist.add_album(album) it sets album.artist to this_artist.

    # File lib/sequel/model/associations.rb
526 def reciprocal
527   cached_fetch(:reciprocal) do
528     possible_recips = []
529 
530     associated_class.all_association_reflections.each do |assoc_reflect|
531       if reciprocal_association?(assoc_reflect)
532         possible_recips << assoc_reflect
533       end
534     end
535 
536     if possible_recips.length == 1
537       cached_set(:reciprocal_type, possible_recips.first[:type]) if ambiguous_reciprocal_type?
538       possible_recips.first[:name]
539     end
540   end
541 end
reciprocal_array?() click to toggle source

Whether the reciprocal of this association returns an array of objects instead of a single object, true by default.

    # File lib/sequel/model/associations.rb
545 def reciprocal_array?
546   true
547 end
remove_all_method() click to toggle source

Name symbol for the remove_all_ association method

    # File lib/sequel/model/associations.rb
550 def remove_all_method
551   self[:remove_all_method]
552 end
remove_before_destroy?() click to toggle source

Whether associated objects need to be removed from the association before being destroyed in order to preserve referential integrity.

    # File lib/sequel/model/associations.rb
556 def remove_before_destroy?
557   true
558 end
remove_method() click to toggle source

Name symbol for the remove_ association method

    # File lib/sequel/model/associations.rb
561 def remove_method
562   self[:remove_method]
563 end
remove_should_check_existing?() click to toggle source

Whether to check that an object to be disassociated is already associated to this object, false by default.

    # File lib/sequel/model/associations.rb
566 def remove_should_check_existing?
567   false
568 end
returns_array?() click to toggle source

Whether this association returns an array of objects instead of a single object, true by default.

    # File lib/sequel/model/associations.rb
572 def returns_array?
573   true
574 end
select() click to toggle source

The columns to select when loading the association.

    # File lib/sequel/model/associations.rb
577 def select
578   self[:select]
579 end
set_reciprocal_to_self?() click to toggle source

Whether to set the reciprocal association to self when loading associated records, false by default.

    # File lib/sequel/model/associations.rb
583 def set_reciprocal_to_self?
584   false
585 end
setter_method() click to toggle source

Name symbol for the setter association method

    # File lib/sequel/model/associations.rb
588 def setter_method
589   self[:setter_method]
590 end
slice_range(limit_and_offset = limit_and_offset()) click to toggle source

The range used for slicing when using the :ruby eager limit strategy.

    # File lib/sequel/model/associations.rb
593 def slice_range(limit_and_offset = limit_and_offset())
594   limit, offset = limit_and_offset
595   if limit || offset
596     (offset||0)..(limit ? (offset||0)+limit-1 : -1)
597   end
598 end

Private Instance Methods

_associated_dataset() click to toggle source

The base dataset used for the association, before any order/conditions options have been applied.

    # File lib/sequel/model/associations.rb
623 def _associated_dataset
624   associated_class.dataset
625 end
_lateral_subquery_filter_limit_strategy_conditions(obj, key, value_method, value_column) click to toggle source

Return an expression to filter the filter by associations dataset to only rows related to given objects.

    # File lib/sequel/model/associations.rb
789 def _lateral_subquery_filter_limit_strategy_conditions(obj, key, value_method, value_column)
790   value = case obj
791   when Array
792     if key.is_a?(Array)
793       key_methods = Array(value_method)
794       obj.map{|o| key_methods.map{|meth| o.send(meth)}}
795     else
796       obj.map{|o| o.send(value_method)}
797     end
798   when Sequel::Dataset
799     obj.select(*Array(qualify(associated_class.table_name, value_column)))
800   else
801     if key.is_a?(Array)
802       return Array(key).zip(Array(value_method).map{|meth| obj.send(meth)})
803     else
804       obj.send(value_method)
805     end
806   end
807 
808   {key => value}
809 end
ambiguous_reciprocal_type?() click to toggle source

Whether for the reciprocal type for the given association cannot be known in advantage, false by default.

    # File lib/sequel/model/associations.rb
629 def ambiguous_reciprocal_type?
630   false
631 end
apply_filter_by_associations_distinct_on_limit_strategy(ds) click to toggle source

Apply a distinct on eager limit strategy using IN with a subquery that uses DISTINCT ON to ensure only the first matching record for each key is included.

    # File lib/sequel/model/associations.rb
649 def apply_filter_by_associations_distinct_on_limit_strategy(ds)
650   k = filter_by_associations_limit_key 
651   ds.where(k=>apply_distinct_on_eager_limit_strategy(associated_eager_dataset.select(*k)))
652 end
apply_filter_by_associations_limit_strategy(ds) click to toggle source

Apply a limit strategy to the given dataset so that filter by associations works with a limited dataset.

    # File lib/sequel/model/associations.rb
635 def apply_filter_by_associations_limit_strategy(ds)
636   case filter_by_associations_limit_strategy
637   when :distinct_on
638     apply_filter_by_associations_distinct_on_limit_strategy(ds)
639   when :window_function
640     apply_filter_by_associations_window_function_limit_strategy(ds)
641   else
642     ds
643   end
644 end
apply_filter_by_associations_window_function_limit_strategy(ds) click to toggle source

Apply a distinct on eager limit strategy using IN with a subquery that uses a filter on the row_number window function to ensure that only rows inside the limit are returned.

    # File lib/sequel/model/associations.rb
657 def apply_filter_by_associations_window_function_limit_strategy(ds)
658   ds.where(filter_by_associations_limit_key=>apply_window_function_eager_limit_strategy(associated_eager_dataset.select(*filter_by_associations_limit_alias_key)).select(*filter_by_associations_limit_aliases))
659 end
apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset) click to toggle source
    # File lib/sequel/model/associations.rb
776 def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
777   table_name = self[:model].table_name
778   associated_table_name = associated_class.table_name
779 
780   associated_class.
781     from(table_name).
782     select_all(associated_table_name).
783     join(lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset).as(associated_table_name), true).
784     order(*self[:order])
785 end
apply_lateral_subquery_filter_limit_strategy(ds, obj) click to toggle source
    # File lib/sequel/model/associations.rb
818 def apply_lateral_subquery_filter_limit_strategy(ds, obj)
819   lateral_subquery_filter_limit_strategy_filter_dataset(self[:model].
820     select(*lateral_subquery_filter_limit_strategy_lateral_dataset_select).
821     join(lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj).as(associated_class.table_name), filter_by_associations_conditions_subquery_conditions(obj)), obj)
822 end
associated_eager_dataset() click to toggle source

The associated_dataset with the eager_block callback already applied.

    # File lib/sequel/model/associations.rb
662 def associated_eager_dataset
663   cached_fetch(:associated_eager_dataset) do
664     ds = associated_dataset.unlimited
665     if block = self[:eager_block]
666       ds = block.call(ds)
667     end
668     ds
669   end
670 end
cached_fetch(key) { || ... } click to toggle source

If the key exists in the reflection hash, return it. If the key doesn’t exist and association reflections are uncached, then yield to get the value. If the key doesn’t exist and association reflection are cached, check the cache and return the value if present, or yield to get the value, cache the value, and return it.

    # File lib/sequel/model/associations.rb
606 def cached_fetch(key)
607   fetch(key) do
608     return yield unless h = self[:cache]
609     Sequel.synchronize{return h[key] if h.has_key?(key)}
610     value = yield
611     Sequel.synchronize{h[key] = value}
612   end
613 end
cached_set(key, value) click to toggle source

Cache the value at the given key if caching.

    # File lib/sequel/model/associations.rb
616 def cached_set(key, value)
617   return unless h = self[:cache]
618   Sequel.synchronize{h[key] = value}
619 end
default_eager_limit_strategy() click to toggle source

The default eager limit strategy to use for this association

    # File lib/sequel/model/associations.rb
699 def default_eager_limit_strategy
700   self[:model].default_eager_limit_strategy || :ruby
701 end
eager_loading_dataset(eo=OPTS) click to toggle source

The dataset to use for eager loading associated objects for multiple current objects, given the hash passed to the eager loader.

    # File lib/sequel/model/associations.rb
674 def eager_loading_dataset(eo=OPTS)
675   ds = eo[:dataset] || associated_eager_dataset
676   ds = eager_loading_set_predicate_condition(ds, eo)
677   if associations = eo[:associations]
678     ds = ds.eager(associations)
679   end
680   if block = eo[:eager_block]
681     orig_ds = ds
682     ds = block.call(ds)
683   end
684   if eager_loading_use_associated_key?
685     ds = if ds.opts[:eager_graph] && !orig_ds.opts[:eager_graph]
686       block.call(orig_ds.select_append(*associated_key_array))
687     else
688       ds.select_append(*associated_key_array)
689     end
690   end
691   if self[:eager_graph]
692     raise(Error, "cannot eagerly load a #{self[:type]} association that uses :eager_graph") if eager_loading_use_associated_key?
693     ds = ds.eager_graph(self[:eager_graph])
694   end
695   ds
696 end
eager_loading_predicate_condition(keys) click to toggle source

The predicate condition to use for the eager_loader.

    # File lib/sequel/model/associations.rb
713 def eager_loading_predicate_condition(keys)
714   if transform = self[:eager_loading_predicate_transform]
715     keys = transform.call(keys, self)
716   end
717   {predicate_key=>keys}
718 end
eager_loading_set_predicate_condition(ds, eo) click to toggle source

Set the predicate condition for the eager loading dataset based on the id map in the eager loading options.

    # File lib/sequel/model/associations.rb
705 def eager_loading_set_predicate_condition(ds, eo)
706   if id_map = eo[:id_map]
707     ds = ds.where(eager_loading_predicate_condition(id_map.keys))
708   end
709   ds
710 end
filter_by_associations_add_conditions_dataset_filter(ds) click to toggle source

Add conditions to the dataset to not include NULL values for the associated keys, and select those keys.

    # File lib/sequel/model/associations.rb
722 def filter_by_associations_add_conditions_dataset_filter(ds)
723   k = filter_by_associations_conditions_associated_keys
724   ds.select(*k).where(Sequel.negate(k.zip([])))
725 end
filter_by_associations_conditions_dataset() click to toggle source

The base dataset to use for the filter by associations conditions subquery, regardless of the objects that are passed in as filter values.

    # File lib/sequel/model/associations.rb
745 def filter_by_associations_conditions_dataset
746   cached_fetch(:filter_by_associations_conditions_dataset) do
747     ds = associated_eager_dataset.unordered
748     ds = filter_by_associations_add_conditions_dataset_filter(ds)
749     ds = apply_filter_by_associations_limit_strategy(ds)
750     ds
751   end
752 end
filter_by_associations_conditions_subquery_conditions(obj) click to toggle source

The conditions to add to the filter by associations conditions subquery to restrict it to to the object(s) that was used as the filter value.

    # File lib/sequel/model/associations.rb
730 def filter_by_associations_conditions_subquery_conditions(obj)
731   key = qualify(associated_class.table_name, associated_class.primary_key)
732   case obj
733   when Array
734     {key=>obj.map(&:pk)}
735   when Sequel::Dataset
736     {key=>obj.select(*Array(qualify(associated_class.table_name, associated_class.primary_key)))}
737   else
738     Array(key).zip(Array(obj.pk))
739   end
740 end
filter_by_associations_limit_strategy() click to toggle source

The strategy to use to filter by a limited association

    # File lib/sequel/model/associations.rb
755 def filter_by_associations_limit_strategy
756   v = fetch(:filter_limit_strategy, self[:eager_limit_strategy])
757   if v || self[:limit] || !returns_array?
758     case v ||= self[:model].default_eager_limit_strategy
759     when true, :union, :ruby
760       # Can't use a union or ruby-based strategy for filtering by associations, switch to default eager graph limit
761       # strategy.
762       true_eager_graph_limit_strategy
763     when Symbol
764       v
765     end
766   end
767 end
lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset) click to toggle source
    # File lib/sequel/model/associations.rb
769 def lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset)
770   ds.
771     where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys))).
772     limit(*limit_and_offset).
773     lateral
774 end
lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj) click to toggle source
    # File lib/sequel/model/associations.rb
811 def lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj)
812   lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds.
813     select(*qualify(associated_class.table_name, associated_class.primary_key)).
814     limit(*limit_and_offset).
815     lateral)
816 end
limit_to_single_row?() click to toggle source

Whether to limit the associated dataset to a single row.

    # File lib/sequel/model/associations.rb
825 def limit_to_single_row?
826   !returns_array?
827 end
offset() click to toggle source

Any offset to use for this association (or nil if there is no offset).

    # File lib/sequel/model/associations.rb
830 def offset
831   limit_and_offset.last
832 end
placeholder_eager_loader() click to toggle source

A placeholder literalizer used to speed up eager loading.

    # File lib/sequel/model/associations.rb
835 def placeholder_eager_loader
836   cached_fetch(:placeholder_eager_loader) do
837     eager_loading_dataset.placeholder_literalizer_loader do |pl, ds|
838       arg = pl.arg
839 
840       if transform = self[:eager_loading_predicate_transform]
841         arg = arg.transform do |v|
842           transform.call(v, self)
843         end
844       end
845 
846       strategy = eager_limit_strategy
847       if strategy == :lateral_subquery
848         apply_lateral_subquery_eager_limit_strategy(ds, arg, limit_and_offset)
849       else
850         apply_eager_limit_strategy(ds.where(predicate_key=>arg), strategy)
851       end
852     end
853   end
854 end
possible_reciprocal_types() click to toggle source

The reciprocal type as an array, should be overridden in reflection subclasses that have ambiguous reciprocal types.

    # File lib/sequel/model/associations.rb
858 def possible_reciprocal_types
859   [reciprocal_type]
860 end
reciprocal_association?(assoc_reflect) click to toggle source

Whether the given association reflection is possible reciprocal association for the current association reflection.

    # File lib/sequel/model/associations.rb
864 def reciprocal_association?(assoc_reflect)
865   possible_reciprocal_types.include?(assoc_reflect[:type]) &&
866     (begin; assoc_reflect.associated_class; rescue NameError; end) == self[:model] &&
867     assoc_reflect[:conditions].nil? &&
868     assoc_reflect[:block].nil?
869 end
subqueries_per_union() click to toggle source

The number of subqueries to use in each union query, used to eagerly load limited associations. Defaults to 40, the optimal number depends on the latency between the database and the application.

    # File lib/sequel/model/associations.rb
874 def subqueries_per_union
875   self[:subqueries_per_union] || 40
876 end
transform(s) { |s)| ... } click to toggle source

If s is an array, map s over the block. Otherwise, just call the block with s.

    # File lib/sequel/model/associations.rb
880 def transform(s, &block)
881   s.is_a?(Array) ? s.map(&block) : (yield s)
882 end
true_eager_graph_limit_strategy() click to toggle source

The eager_graph limit strategy used when true is given as the value, choosing the best strategy based on what the database supports.

    # File lib/sequel/model/associations.rb
898 def true_eager_graph_limit_strategy
899   if associated_class.dataset.supports_window_functions?
900     :window_function
901   else
902     :ruby
903   end
904 end
true_eager_limit_strategy() click to toggle source

What eager limit strategy should be used when true is given as the value, defaults to UNION as that is the fastest strategy if the appropriate keys are indexed.

    # File lib/sequel/model/associations.rb
886 def true_eager_limit_strategy
887   if self[:eager_graph] || (offset && !associated_dataset.supports_offsets_in_correlated_subqueries?)
888     # An SQL-based approach won't work if you are also eager graphing,
889     # so use a ruby based approach in that case.
890     :ruby
891   else
892     :union 
893   end
894 end
union_eager_loader() click to toggle source

A placeholder literalizer used to speed up the creation of union queries when eager loading a limited association.

    # File lib/sequel/model/associations.rb
908 def union_eager_loader
909   cached_fetch(:union_eager_loader) do
910     associated_dataset.placeholder_literalizer_loader do |pl, ds|
911       ds = self[:eager_block].call(ds) if self[:eager_block]
912       keys = predicate_keys
913       ds = ds.where(keys.map{pl.arg}.zip(keys))
914       if eager_loading_use_associated_key?
915         ds = ds.select_append(*associated_key_array)
916       end
917       ds.from_self
918     end
919   end
920 end
use_placeholder_loader?() click to toggle source

Whether the placeholder loader can be used to load the association.

    # File lib/sequel/model/associations.rb
923 def use_placeholder_loader?
924   self[:use_placeholder_loader] && _associated_dataset.supports_placeholder_literalizer?
925 end