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
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.
3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 |
# File 'lib/sequel/model/associations.rb', line 3968 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] if v[:after_load] type_map[k] = if v.returns_array? true elsif (limit_and_offset = limit_map[k]) && !limit_and_offset.last.nil? :offset end end after_load_map.freeze alias_map.freeze type_map.freeze # 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 = @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 freezer = lambda do |h| h.freeze h.each_value(&freezer) end freezer.call(dependency_map) datasets = opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?} column_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.freeze @primary_keys = primary_keys.freeze @row_procs = row_procs.freeze # 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.each_key do |ta| ta_map[ta] = [row_procs[ta], alias_map[ta], type_map[ta], reciprocal_map[ta]].freeze end @ta_map = ta_map.freeze freeze end |
Instance Attribute Details
#after_load_map ⇒ Object (readonly)
Hash with table alias symbol keys and after_load hook values
3926 3927 3928 |
# File 'lib/sequel/model/associations.rb', line 3926 def after_load_map @after_load_map end |
#alias_map ⇒ Object (readonly)
Hash with table alias symbol keys and association name values
3929 3930 3931 |
# File 'lib/sequel/model/associations.rb', line 3929 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
3933 3934 3935 |
# File 'lib/sequel/model/associations.rb', line 3933 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.
3936 3937 3938 |
# File 'lib/sequel/model/associations.rb', line 3936 def dependency_map @dependency_map end |
#limit_map ⇒ Object (readonly)
Hash with table alias symbol keys and [limit, offset] values
3939 3940 3941 |
# File 'lib/sequel/model/associations.rb', line 3939 def limit_map @limit_map end |
#master ⇒ Object (readonly)
The table alias symbol for the primary model
3942 3943 3944 |
# File 'lib/sequel/model/associations.rb', line 3942 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)
3946 3947 3948 |
# File 'lib/sequel/model/associations.rb', line 3946 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.
3950 3951 3952 |
# File 'lib/sequel/model/associations.rb', line 3950 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.
3954 3955 3956 |
# File 'lib/sequel/model/associations.rb', line 3954 def records_map @records_map end |
#reflection_map ⇒ Object (readonly)
Hash with table alias symbol keys and AssociationReflection values
3957 3958 3959 |
# File 'lib/sequel/model/associations.rb', line 3957 def reflection_map @reflection_map end |
#row_procs ⇒ Object (readonly)
Hash with table alias symbol keys and callable values used to create model instances
3960 3961 3962 |
# File 'lib/sequel/model/associations.rb', line 3960 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).
3965 3966 3967 |
# File 'lib/sequel/model/associations.rb', line 3965 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).
4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 |
# File 'lib/sequel/model/associations.rb', line 4068 def load(hashes) # 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 = records_map = {} alias_map.keys.each{|ta| records_map[ta] = {}} master = master() # Assign to local variables for speed increase rp = row_procs[master] rm = records_map[master] = {} dm = dependency_map records_map.freeze # 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_map.each_value(&:freeze) freeze records end |