Skip to content

Commit a256135

Browse files
feat: improve performance by avoiding unnecessary jointure on belongsTo relations (#735)
1 parent 32aee70 commit a256135

File tree

4 files changed

+55
-22
lines changed

4 files changed

+55
-22
lines changed

app/serializers/forest_liana/serializer_factory.rb

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,6 @@ def relationship_related_link(attribute_name)
118118

119119
if ret[:href].blank?
120120
begin
121-
if @options[:include].try(:include?, attribute_name.to_s) &&
122-
!SchemaHelper.is_smart_field?(object.class, attribute_name.to_s)
123-
124-
object.send(attribute_name)
125-
end
126-
127121
SchemaUtils.many_associations(object.class).each do |a|
128122
if a.name == attribute_name
129123
ret[:href] = "/forest/#{ForestLiana.name_for(object.class)}/#{object.id}/relationships/#{attribute_name}"
@@ -137,6 +131,30 @@ def relationship_related_link(attribute_name)
137131
ret
138132
end
139133

134+
def has_one_relationships
135+
return {} if self.class.to_one_associations.nil?
136+
data = {}
137+
self.class.to_one_associations.each do |attribute_name, attr_data|
138+
relation = object.class.reflect_on_all_associations.find { |a| a.name == attribute_name }
139+
140+
next if !should_include_attr?(attribute_name, attr_data)
141+
142+
unless relation.polymorphic?
143+
relation_class_name = ForestLiana.name_for(relation.klass).demodulize
144+
145+
if object.send(relation.foreign_key.to_sym) &&
146+
@options[:fields][relation_class_name]&.size == 1 &&
147+
@options[:fields][relation_class_name]&.include?(relation.klass.primary_key.to_sym)
148+
149+
attr_data[:attr_or_block] = proc { relation.klass.new(relation.klass.primary_key => object.send(relation.foreign_key.to_sym)) }
150+
end
151+
end
152+
153+
data[attribute_name] = attr_data
154+
end
155+
data
156+
end
157+
140158
private
141159

142160
def intercom_integration?

app/services/forest_liana/base_getter.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ def includes_for_serialization
1919

2020
def compute_includes
2121
@includes = ForestLiana::QueryHelper.get_one_association_names_symbol(@resource)
22+
@optional_includes = []
2223
end
2324

2425
def optimize_record_loading(resource, records, force_preload = true)
2526
polymorphic, preload_loads = analyze_associations(resource)
26-
result = records.eager_load(@includes.uniq - preload_loads - polymorphic)
27+
result = records.eager_load(@includes.uniq - preload_loads - polymorphic - @optional_includes)
2728

2829
result = result.preload(preload_loads) if Rails::VERSION::MAJOR >= 7 && force_preload
2930

app/services/forest_liana/has_many_getter.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ def records
3737
private
3838

3939
def compute_includes
40+
@optional_includes = []
41+
4042
@includes = @association.klass
4143
.reflect_on_all_associations
4244
.select do |association|

app/services/forest_liana/resources_getter.rb

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def self.get_ids_from_request(params, user)
3131

3232
def perform
3333
polymorphic_association, preload_loads = analyze_associations(@resource)
34-
includes = @includes.uniq - polymorphic_association - preload_loads
34+
includes = @includes.uniq - polymorphic_association - preload_loads - @optional_includes
3535
has_smart_fields = @params[:fields][@collection_name].split(',').any? do |field|
3636
ForestLiana::SchemaHelper.is_smart_field?(@resource, field)
3737
end
@@ -132,26 +132,38 @@ def columns_for_cross_database_association(association_name)
132132

133133
def compute_includes
134134
associations_has_one = ForestLiana::QueryHelper.get_one_associations(@resource)
135+
@optional_includes = []
136+
if @field_names_requested
137+
includes = associations_has_one.map do |association|
138+
association_name = association.name.to_s
135139

136-
includes = associations_has_one.map(&:name)
137-
includes_for_smart_search = []
140+
if @params[:fields].key?(association_name) &&
141+
@params[:fields][association_name].split(',').size == 1 &&
142+
@params[:fields][association_name].split(',').include?(association.klass.primary_key)
138143

139-
if @collection && @collection.search_fields
140-
includes_for_smart_search = @collection.search_fields
141-
.select { |field| field.include? '.' }
142-
.map { |field| field.split('.').first.to_sym }
144+
@field_names_requested << association.foreign_key
145+
@optional_includes << association.name
146+
end
143147

144-
includes_has_many = SchemaUtils.many_associations(@resource)
145-
.select { |association| SchemaUtils.model_included?(association.klass) }
146-
.map(&:name)
148+
association.name
149+
end
147150

148-
includes_for_smart_search = includes_for_smart_search & includes_has_many
149-
end
151+
includes_for_smart_search = []
152+
if @collection && @collection.search_fields
153+
includes_for_smart_search = @collection.search_fields
154+
.select { |field| field.include? '.' }
155+
.map { |field| field.split('.').first.to_sym }
156+
157+
includes_has_many = SchemaUtils.many_associations(@resource)
158+
.select { |association| SchemaUtils.model_included?(association.klass) }
159+
.map(&:name)
160+
161+
includes_for_smart_search = includes_for_smart_search & includes_has_many
162+
end
150163

151-
if @field_names_requested
152164
@includes = (includes & @field_names_requested).concat(includes_for_smart_search)
153165
else
154-
@includes = includes
166+
@includes = associations_has_one.map(&:name)
155167
end
156168
end
157169

@@ -298,7 +310,7 @@ def pagination?
298310

299311
def compute_select_fields
300312
select = ['_forest_admin_eager_load']
301-
@params[:fields][@collection_name].split(',').each do |path|
313+
@field_names_requested.each do |path|
302314
association = get_one_association(path)
303315
if association
304316
while association.options[:through]

0 commit comments

Comments
 (0)