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.
3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 |
# File 'lib/sequel/model/associations.rb', line 3042 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] limit_map = @limit_map = eager_graph[:limits] @unique = eager_graph[:cartesian_product_number] > 1 alias_map = @alias_map = {} type_map = @type_map = {} after_load_map = @after_load_map = {} reflection_map.each do |k, v| alias_map[k] = v[:name] after_load_map[k] = v[:after_load] unless v[:after_load].empty? type_map[k] = if v.returns_array? true elsif (limit_and_offset = limit_map[k]) && !limit_and_offset.last.nil? :offset end 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
2999 3000 3001 |
# File 'lib/sequel/model/associations.rb', line 2999 def after_load_map @after_load_map end |
#alias_map ⇒ Object (readonly)
Hash with table alias symbol keys and association name values
3002 3003 3004 |
# File 'lib/sequel/model/associations.rb', line 3002 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
3006 3007 3008 |
# File 'lib/sequel/model/associations.rb', line 3006 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.
3009 3010 3011 |
# File 'lib/sequel/model/associations.rb', line 3009 def dependency_map @dependency_map end |
#limit_map ⇒ Object (readonly)
Hash with table alias symbol keys and [limit, offset] values
3012 3013 3014 |
# File 'lib/sequel/model/associations.rb', line 3012 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
3016 3017 3018 |
# File 'lib/sequel/model/associations.rb', line 3016 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)
3020 3021 3022 |
# File 'lib/sequel/model/associations.rb', line 3020 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.
3024 3025 3026 |
# File 'lib/sequel/model/associations.rb', line 3024 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.
3028 3029 3030 |
# File 'lib/sequel/model/associations.rb', line 3028 def records_map @records_map end |
#reflection_map ⇒ Object (readonly)
Hash with table alias symbol keys and AssociationReflection values
3031 3032 3033 |
# File 'lib/sequel/model/associations.rb', line 3031 def reflection_map @reflection_map end |
#row_procs ⇒ Object (readonly)
Hash with table alias symbol keys and callable values used to create model instances
3034 3035 3036 |
# File 'lib/sequel/model/associations.rb', line 3034 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).
3039 3040 3041 |
# File 'lib/sequel/model/associations.rb', line 3039 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).
3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 |
# File 'lib/sequel/model/associations.rb', line 3144 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 |