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