Skip to content

Commit f352809

Browse files
authored
fix: correct column selection in cross-db associations and extended-search (#737)
1 parent 392c4ef commit f352809

File tree

4 files changed

+53
-18
lines changed

4 files changed

+53
-18
lines changed

app/services/forest_liana/base_getter.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ def analyze_associations(resource)
4747
end
4848

4949
def separate_database?(resource, association)
50+
return false if SchemaUtils.polymorphic?(association)
51+
5052
target_model_connection = association.klass.connection
5153
target_model_database = target_model_connection.current_database if target_model_connection.respond_to? :current_database
5254
resource_connection = resource.connection

app/services/forest_liana/resources_getter.rb

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def records
5858
records = @records.offset(offset).limit(limit).to_a
5959
polymorphic_association, preload_loads = analyze_associations(@resource)
6060

61-
if polymorphic_association && Rails::VERSION::MAJOR >= 7
61+
if polymorphic_association.any? && Rails::VERSION::MAJOR >= 7
6262
preloader = ActiveRecord::Associations::Preloader.new(records: records, associations: polymorphic_association)
6363
preloader.loaders
6464
preloader.branches.each do |branch|
@@ -85,7 +85,6 @@ def preload_cross_database_associations(records, preload_loads)
8585
next unless separate_database?(@resource, association)
8686

8787
columns = columns_for_cross_database_association(association_name)
88-
8988
if association.macro == :belongs_to
9089
foreign_key = association.foreign_key
9190
primary_key = association.klass.primary_key
@@ -125,27 +124,22 @@ def preload_cross_database_associations(records, preload_loads)
125124
end
126125

127126
def columns_for_cross_database_association(association_name)
128-
return [:id] unless @params[:fields].present?
129-
130-
fields = @params[:fields][association_name.to_s]
131-
return [:id] unless fields
132-
133-
base_fields = fields.split(',').map(&:strip).map(&:to_sym) | [:id]
134-
135127
association = @resource.reflect_on_association(association_name)
136-
extra_key = association.foreign_key
137128

138-
# Add the foreign key used for the association to ensure it's available in the preloaded records
139-
# This is necessary for has_one associations, without it calling record.public_send(foreign_key) would raise a "missing attribute" error
140-
base_fields << extra_key if association.macro == :has_one
129+
# Always include all columns of the associated model to avoid missing attribute errors
130+
columns = association.klass.column_names.map(&:to_sym)
131+
132+
# Ensure the foreign key is present for manual binding (especially for has_one)
133+
columns << association.foreign_key.to_sym if association.macro == :has_one
141134

142-
base_fields.uniq
135+
columns.uniq
143136
end
144137

145138
def compute_includes
146139
associations_has_one = ForestLiana::QueryHelper.get_one_associations(@resource)
140+
147141
@optional_includes = []
148-
if @field_names_requested
142+
if @field_names_requested && @params['searchExtended'].to_i != 1
149143
includes = associations_has_one.map do |association|
150144
association_name = association.name.to_s
151145

@@ -175,7 +169,10 @@ def compute_includes
175169

176170
@includes = (includes & @field_names_requested).concat(includes_for_smart_search)
177171
else
178-
@includes = associations_has_one.map(&:name)
172+
@includes = associations_has_one
173+
# Avoid eager loading has_one associations pointing to a different database as ORM can't join cross databases
174+
.reject { |association| separate_database?(@resource, association) }
175+
.map(&:name)
179176
end
180177
end
181178

app/services/forest_liana/schema_adapter.rb

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,13 @@ def add_associations
278278
field[:field] = association.name
279279
field[:inverse_of] = inverse_of(association)
280280
field[:relationship] = get_relationship_type(association)
281-
# NOTICE: Create the fields of hasOne, HasMany, … relationships.
281+
282+
ForestLiana::SchemaUtils.disable_filter_and_sort_if_cross_db!(
283+
field,
284+
association.name.to_s,
285+
ForestLiana.name_for(@model)
286+
)
287+
# NOTICE: Create the fields of hasOne, HasMany, … relationships.
282288
else
283289
collection.fields << get_schema_for_association(association)
284290
end
@@ -346,7 +352,7 @@ def get_schema_for_column(column)
346352
end
347353

348354
def get_schema_for_association(association)
349-
{
355+
opts ={
350356
field: association.name.to_s,
351357
type: get_type_for_association(association),
352358
relationship: get_relationship_type(association),
@@ -363,6 +369,14 @@ def get_schema_for_association(association)
363369
widget: nil,
364370
validations: []
365371
}
372+
373+
ForestLiana::SchemaUtils.disable_filter_and_sort_if_cross_db!(
374+
opts,
375+
association.name.to_s,
376+
ForestLiana.name_for(@model)
377+
)
378+
379+
opts
366380
end
367381

368382
def get_relationship_type(association)

app/services/forest_liana/schema_utils.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,5 +126,27 @@ def self.habtm?(model)
126126
def self.is_active_type? model
127127
Object.const_defined?('ActiveType::Object') && model < ActiveType::Object
128128
end
129+
130+
def self.disable_filter_and_sort_if_cross_db!(opts, name, collection_name)
131+
return unless opts[:reference]
132+
133+
assoc_name = opts[:reference].split('.').first&.underscore&.to_sym || name
134+
model = find_model_from_collection_name(collection_name)
135+
return unless model
136+
137+
association = model.reflect_on_association(assoc_name)
138+
return unless association
139+
return if polymorphic?(association)
140+
141+
model_db = model.connection_db_config.database
142+
assoc_db = association.klass.connection_db_config.database
143+
144+
if model_db != assoc_db
145+
opts[:is_filterable] = false
146+
opts[:is_sortable] = false
147+
end
148+
rescue => e
149+
FOREST_LOGGER.warn("Could not evaluate cross-db association for #{name}: #{e.message}")
150+
end
129151
end
130152
end

0 commit comments

Comments
 (0)