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.
3967 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 |
# File 'lib/sequel/model/associations.rb', line 3967 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
3925 3926 3927 |
# File 'lib/sequel/model/associations.rb', line 3925 def after_load_map @after_load_map end |
#alias_map ⇒ Object (readonly)
Hash with table alias symbol keys and association name values
3928 3929 3930 |
# File 'lib/sequel/model/associations.rb', line 3928 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
3932 3933 3934 |
# File 'lib/sequel/model/associations.rb', line 3932 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.
3935 3936 3937 |
# File 'lib/sequel/model/associations.rb', line 3935 def dependency_map @dependency_map end |
#limit_map ⇒ Object (readonly)
Hash with table alias symbol keys and [limit, offset] values
3938 3939 3940 |
# File 'lib/sequel/model/associations.rb', line 3938 def limit_map @limit_map end |
#master ⇒ Object (readonly)
The table alias symbol for the primary model
3941 3942 3943 |
# File 'lib/sequel/model/associations.rb', line 3941 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)
3945 3946 3947 |
# File 'lib/sequel/model/associations.rb', line 3945 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.
3949 3950 3951 |
# File 'lib/sequel/model/associations.rb', line 3949 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.
3953 3954 3955 |
# File 'lib/sequel/model/associations.rb', line 3953 def records_map @records_map end |
#reflection_map ⇒ Object (readonly)
Hash with table alias symbol keys and AssociationReflection values
3956 3957 3958 |
# File 'lib/sequel/model/associations.rb', line 3956 def reflection_map @reflection_map end |
#row_procs ⇒ Object (readonly)
Hash with table alias symbol keys and callable values used to create model instances
3959 3960 3961 |
# File 'lib/sequel/model/associations.rb', line 3959 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).
3964 3965 3966 |
# File 'lib/sequel/model/associations.rb', line 3964 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).
4067 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 |
# File 'lib/sequel/model/associations.rb', line 4067 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 |