Class: Sequel::Model::Associations::EagerGraphLoader
- Defined in:
- lib/sequel/model/associations.rb
Overview
This class is the internal implementation of eager_graph. It is responsible for taking an array of plain hashes and returning an array of model objects with all eager_graphed associations already set in the association cache.
Instance Attribute Summary collapse
-
#after_load_map ⇒ Object
readonly
Hash with table alias symbol keys and after_load hook values.
-
#alias_map ⇒ Object
readonly
Hash with table alias symbol keys and association name values.
-
#column_maps ⇒ Object
readonly
Hash with table alias symbol keys and subhash values mapping column_alias symbols to the symbol of the real name of the column.
-
#dependency_map ⇒ Object
readonly
Recursive hash with table alias symbol keys mapping to hashes with dependent table alias symbol keys.
-
#limit_map ⇒ Object
readonly
Hash with table alias symbol keys and [limit, offset] values.
-
#master ⇒ Object
readonly
Hash with table alias symbol keys and callable values used to create model instances The table alias symbol for the primary model.
-
#primary_keys ⇒ Object
readonly
Hash with table alias symbol keys and primary key symbol values (or arrays of primary key symbols for composite key tables).
-
#reciprocal_map ⇒ Object
readonly
Hash with table alias symbol keys and reciprocal association symbol values, used for setting reciprocals for one_to_many associations.
-
#records_map ⇒ Object
readonly
Hash with table alias symbol keys and subhash values mapping primary key symbols (or array of symbols) to model instances.
-
#reflection_map ⇒ Object
readonly
Hash with table alias symbol keys and AssociationReflection values.
-
#row_procs ⇒ Object
readonly
Hash with table alias symbol keys and callable values used to create model instances.
-
#type_map ⇒ Object
readonly
Hash with table alias symbol keys and true/false values, where true means the association represented by the table alias uses an array of values instead of a single value (i.e. true => *_many, false => *_to_one).
Instance Method Summary collapse
-
#initialize(dataset) ⇒ EagerGraphLoader
constructor
Initialize all of the data structures used during loading.
-
#load(hashes) ⇒ Object
Return an array of primary model instances with the associations cache prepopulated for all model objects (both primary and associated).
Constructor Details
#initialize(dataset) ⇒ EagerGraphLoader
Initialize all of the data structures used during loading.
2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 |
# File 'lib/sequel/model/associations.rb', line 2252 def initialize(dataset) opts = dataset.opts eager_graph = opts[:eager_graph] @master = eager_graph[:master] requirements = eager_graph[:requirements] reflection_map = @reflection_map = eager_graph[:reflections] reciprocal_map = @reciprocal_map = eager_graph[:reciprocals] @unique = eager_graph[:cartesian_product_number] > 1 alias_map = @alias_map = {} type_map = @type_map = {} after_load_map = @after_load_map = {} limit_map = @limit_map = {} reflection_map.each do |k, v| alias_map[k] = v[:name] type_map[k] = v.returns_array? after_load_map[k] = v[:after_load] unless v[:after_load].empty? limit_map[k] = v.limit_and_offset if v[:limit] end # Make dependency map hash out of requirements array for each association. # This builds a tree of dependencies that will be used for recursion # to ensure that all parts of the object graph are loaded into the # appropriate subordinate association. @dependency_map = {} # Sort the associations by requirements length, so that # requirements are added to the dependency hash before their # dependencies. requirements.sort_by{|a| a[1].length}.each do |ta, deps| if deps.empty? dependency_map[ta] = {} else deps = deps.dup hash = dependency_map[deps.shift] deps.each do |dep| hash = hash[dep] end hash[ta] = {} end end # This mapping is used to make sure that duplicate entries in the # result set are mapped to a single record. For example, using a # single one_to_many association with 10 associated records, # the main object column values appear in the object graph 10 times. # We map by primary key, if available, or by the object's entire values, # if not. The mapping must be per table, so create sub maps for each table # alias. records_map = {@master=>{}} alias_map.keys.each{|ta| records_map[ta] = {}} @records_map = records_map datasets = opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?} column_aliases = opts[:graph_aliases] || opts[:graph][:column_aliases] primary_keys = {} column_maps = {} models = {} row_procs = {} datasets.each do |ta, ds| models[ta] = ds.model primary_keys[ta] = [] column_maps[ta] = {} row_procs[ta] = ds.row_proc end column_aliases.each do |col_alias, tc| ta, column = tc column_maps[ta][col_alias] = column end column_maps.each do |ta, h| pk = models[ta].primary_key if pk.is_a?(Array) primary_keys[ta] = [] h.select{|ca, c| primary_keys[ta] << ca if pk.include?(c)} else h.select{|ca, c| primary_keys[ta] = ca if pk == c} end end @column_maps = column_maps @primary_keys = primary_keys @row_procs = row_procs # For performance, create two special maps for the master table, # so you can skip a hash lookup. @master_column_map = column_maps[master] @master_primary_keys = primary_keys[master] # Add a special hash mapping table alias symbols to 5 element arrays that just # contain the data in other data structures for that table alias. This is # used for performance, to get all values in one hash lookup instead of # separate hash lookups for each data structure. ta_map = {} alias_map.keys.each do |ta| ta_map[ta] = [records_map[ta], row_procs[ta], alias_map[ta], type_map[ta], reciprocal_map[ta]] end @ta_map = ta_map end |
Instance Attribute Details
#after_load_map ⇒ Object (readonly)
Hash with table alias symbol keys and after_load hook values
2209 2210 2211 |
# File 'lib/sequel/model/associations.rb', line 2209 def after_load_map @after_load_map end |
#alias_map ⇒ Object (readonly)
Hash with table alias symbol keys and association name values
2212 2213 2214 |
# File 'lib/sequel/model/associations.rb', line 2212 def alias_map @alias_map end |
#column_maps ⇒ Object (readonly)
Hash with table alias symbol keys and subhash values mapping column_alias symbols to the symbol of the real name of the column
2216 2217 2218 |
# File 'lib/sequel/model/associations.rb', line 2216 def column_maps @column_maps end |
#dependency_map ⇒ Object (readonly)
Recursive hash with table alias symbol keys mapping to hashes with dependent table alias symbol keys.
2219 2220 2221 |
# File 'lib/sequel/model/associations.rb', line 2219 def dependency_map @dependency_map end |
#limit_map ⇒ Object (readonly)
Hash with table alias symbol keys and [limit, offset] values
2222 2223 2224 |
# File 'lib/sequel/model/associations.rb', line 2222 def limit_map @limit_map end |
#master ⇒ Object (readonly)
Hash with table alias symbol keys and callable values used to create model instances The table alias symbol for the primary model
2226 2227 2228 |
# File 'lib/sequel/model/associations.rb', line 2226 def master @master end |
#primary_keys ⇒ Object (readonly)
Hash with table alias symbol keys and primary key symbol values (or arrays of primary key symbols for composite key tables)
2230 2231 2232 |
# File 'lib/sequel/model/associations.rb', line 2230 def primary_keys @primary_keys end |
#reciprocal_map ⇒ Object (readonly)
Hash with table alias symbol keys and reciprocal association symbol values, used for setting reciprocals for one_to_many associations.
2234 2235 2236 |
# File 'lib/sequel/model/associations.rb', line 2234 def reciprocal_map @reciprocal_map end |
#records_map ⇒ Object (readonly)
Hash with table alias symbol keys and subhash values mapping primary key symbols (or array of symbols) to model instances. Used so that only a single model instance is created for each object.
2238 2239 2240 |
# File 'lib/sequel/model/associations.rb', line 2238 def records_map @records_map end |
#reflection_map ⇒ Object (readonly)
Hash with table alias symbol keys and AssociationReflection values
2241 2242 2243 |
# File 'lib/sequel/model/associations.rb', line 2241 def reflection_map @reflection_map end |
#row_procs ⇒ Object (readonly)
Hash with table alias symbol keys and callable values used to create model instances
2244 2245 2246 |
# File 'lib/sequel/model/associations.rb', line 2244 def row_procs @row_procs end |
#type_map ⇒ Object (readonly)
Hash with table alias symbol keys and true/false values, where true means the association represented by the table alias uses an array of values instead of a single value (i.e. true => *_many, false => *_to_one).
2249 2250 2251 |
# File 'lib/sequel/model/associations.rb', line 2249 def type_map @type_map end |
Instance Method Details
#load(hashes) ⇒ Object
Return an array of primary model instances with the associations cache prepopulated for all model objects (both primary and associated).
2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 |
# File 'lib/sequel/model/associations.rb', line 2351 def load(hashes) master = master() # Assign to local variables for speed increase rp = row_procs[master] rm = records_map[master] dm = dependency_map # This will hold the final record set that we will be replacing the object graph with. records = [] hashes.each do |h| unless key = master_pk(h) key = hkey(master_hfor(h)) end unless primary_record = rm[key] primary_record = rm[key] = rp.call(master_hfor(h)) # Only add it to the list of records to return if it is a new record records.push(primary_record) end # Build all associations for the current object and it's dependencies _load(dm, primary_record, h) end # Remove duplicate records from all associations if this graph could possibly be a cartesian product # Run after_load procs if there are any post_process(records, dm) if @unique || !after_load_map.empty? || !limit_map.empty? records end |