module Sequel::Plugins::DatasetAssociations::DatasetMethods

Public Instance Methods

associated(name) click to toggle source

For the association given by name, return a dataset of associated objects such that it would return the union of calling the association method on all objects returned by the current dataset.

This supports most options that are supported when eager loading. However, it will only work for limited associations or *_one associations with orders if the database supports window functions.

    # File lib/sequel/plugins/dataset_associations.rb
 80 def associated(name)
 81   raise Error, "unrecognized association name: #{name.inspect}" unless r = model.association_reflection(name)
 82   ds = r.associated_class.dataset
 83   sds = opts[:limit] ? self : unordered
 84   ds = case r[:type]
 85   when :many_to_one
 86     ds.where(r.qualified_primary_key=>sds.select(*Array(r[:qualified_key])))
 87   when :one_to_one, :one_to_many
 88     r.send(:apply_filter_by_associations_limit_strategy, ds.where(r.qualified_key=>sds.select(*Array(r.qualified_primary_key))))
 89   when :many_to_many, :one_through_one
 90     mds = r.associated_class.dataset.
 91       join(r[:join_table], r[:right_keys].zip(r.right_primary_keys)).
 92       select(*Array(r.qualified_right_key)).
 93       where(r.qualify(r.join_table_alias, r[:left_keys])=>sds.select(*r.qualify(model.table_name, r[:left_primary_key_columns])))
 94     ds.where(r.qualified_right_primary_key=>r.send(:apply_filter_by_associations_limit_strategy, mds))
 95   when :many_through_many, :one_through_many
 96     if r.reverse_edges.empty?
 97       mds = r.associated_dataset
 98       fe = r.edges.first
 99       selection = Array(r.qualify(fe[:table], r.final_edge[:left]))
100       predicate_key = r.qualify(fe[:table], fe[:right])
101     else
102       mds = model.dataset
103       iq = model.table_name
104       edges = r.edges.map(&:dup)
105       edges << r.final_edge.dup
106       edges.each do |e|
107         alias_expr = e[:table]
108         aliaz = mds.unused_table_alias(e[:table])
109         unless aliaz == alias_expr
110           alias_expr = Sequel.as(e[:table], aliaz)
111         end
112         e[:alias] = aliaz
113         mds = mds.join(alias_expr, Array(e[:right]).zip(Array(e[:left])), :implicit_qualifier=>iq)
114         iq = nil
115       end
116       fe, f1e, f2e = edges.values_at(0, -1, -2)
117       selection = Array(r.qualify(f2e[:alias], f1e[:left]))
118       predicate_key = r.qualify(fe[:alias], fe[:right])
119     end
120 
121     mds = mds.
122       select(*selection).
123       where(predicate_key=>sds.select(*r.qualify(model.table_name, r[:left_primary_key_columns])))
124     ds.where(r.qualified_right_primary_key=>r.send(:apply_filter_by_associations_limit_strategy, mds))
125   when :pg_array_to_many
126     ds.where(Sequel[r.primary_key=>sds.select{Sequel.pg_array_op(r.qualify(r[:model].table_name, r[:key])).unnest}])
127   when :many_to_pg_array
128     ds.where(Sequel.function(:coalesce, Sequel.pg_array_op(r[:key]).overlaps(sds.select{array_agg(r.qualify(r[:model].table_name, r.primary_key))}), false))
129   else
130     raise Error, "unrecognized association type for association #{name.inspect}: #{r[:type].inspect}"
131   end
132 
133   ds = r.apply_eager_dataset_changes(ds).unlimited
134 
135   if r[:dataset_associations_join]
136     case r[:type]
137     when :many_to_many, :one_through_one
138       ds = ds.join(r[:join_table], r[:right_keys].zip(r.right_primary_keys))
139     when :many_through_many, :one_through_many
140       (r.reverse_edges + [r.final_reverse_edge]).each{|e| ds = ds.join(e[:table], e.fetch(:only_conditions, (Array(e[:left]).zip(Array(e[:right])) + Array(e[:conditions]))), :table_alias=>ds.unused_table_alias(e[:table]), :qualify=>:deep, &e[:block])}
141     end
142   end
143 
144   ds
145 end