From c55f262b7fc72b1ca0660dd9e7371acb136a7ebf Mon Sep 17 00:00:00 2001 From: Sergio Marques Date: Thu, 9 Oct 2025 10:31:27 +0100 Subject: [PATCH] feat: add feature flag to control usage of query optimizations --- app/services/forest_liana/resources_getter.rb | 22 +++++---- lib/forest_liana.rb | 2 + .../forest_liana/resources_getter_spec.rb | 49 ++++++++++++++++++- 3 files changed, 62 insertions(+), 11 deletions(-) diff --git a/app/services/forest_liana/resources_getter.rb b/app/services/forest_liana/resources_getter.rb index 9ded9c6c..eb98f631 100644 --- a/app/services/forest_liana/resources_getter.rb +++ b/app/services/forest_liana/resources_getter.rb @@ -30,17 +30,19 @@ def self.get_ids_from_request(params, user) end def perform - polymorphic_association, preload_loads = analyze_associations(@resource) - includes = @includes.uniq - polymorphic_association - preload_loads - @optional_includes - has_smart_fields = Array(@params.dig(:fields, @collection_name)&.split(',')).any? do |field| - ForestLiana::SchemaHelper.is_smart_field?(@resource, field) - end + @records = optimize_record_loading(@resource, @records, false) - if includes.empty? || has_smart_fields - @records = optimize_record_loading(@resource, @records, false) - else - select = compute_select_fields - @records = optimize_record_loading(@resource, @records, false).references(includes).select(*select) + if ForestLiana.optimize_query_select_fields + polymorphic_association, preload_loads = analyze_associations(@resource) + includes = @includes.uniq - polymorphic_association - preload_loads - @optional_includes + has_smart_fields = Array(@params.dig(:fields, @collection_name)&.split(',')).any? do |field| + ForestLiana::SchemaHelper.is_smart_field?(@resource, field) + end + + if includes.any? && !has_smart_fields + select = compute_select_fields + @records = @records.references(includes).select(*select) + end end @records diff --git a/lib/forest_liana.rb b/lib/forest_liana.rb index 9d353b65..dc0715a7 100644 --- a/lib/forest_liana.rb +++ b/lib/forest_liana.rb @@ -29,6 +29,7 @@ module UserSpace mattr_accessor :meta mattr_accessor :logger mattr_accessor :reporter + mattr_accessor :optimize_query_select_fields # TODO: Remove once lianas prior to 2.0.0 are not supported anymore. mattr_accessor :names_old_overriden @@ -42,6 +43,7 @@ module UserSpace self.meta = {} self.logger = nil self.reporter = nil + self.optimize_query_select_fields = true @config_dir = 'lib/forest_liana/**/*.rb' diff --git a/spec/services/forest_liana/resources_getter_spec.rb b/spec/services/forest_liana/resources_getter_spec.rb index 44928a32..9591a892 100644 --- a/spec/services/forest_liana/resources_getter_spec.rb +++ b/spec/services/forest_liana/resources_getter_spec.rb @@ -82,6 +82,7 @@ def clean_database describe 'records eager loading' do let(:resource) { Product } let(:fields) { { resource.name => 'id,name,manufacturer', 'manufacturer' => 'name' } } + let(:expected_selected_resource_attributes) { %w[id name manufacturer_id] } shared_context 'resource current_database' do before do @@ -117,9 +118,31 @@ def connection.current_database end end + shared_examples 'filtered resource fields selection' do + it 'selects only the fields requested from the main resource' do + expected_non_selected_attributes = Product.attribute_names - expected_selected_resource_attributes + + full_sql = getter.perform.to_sql + + # Extract only the SELECT column list (between SELECT and the first FROM) + select_match = full_sql.match(/SELECT\s+(.*?)\s+FROM/i) + expect(select_match).not_to be_nil + select_columns_sql = select_match[1] + + expected_selected_resource_attributes.each do |attribute| + expect(select_columns_sql).to include(%Q{"products"."#{attribute}"}) + end + + expected_non_selected_attributes.each do |attribute| + expect(select_columns_sql).not_to include(%Q{"products"."#{attribute}"}) + end + end + end + context 'when the connections do not support current_database' do include_examples 'left outer join' include_examples 'records' + include_examples 'filtered resource fields selection' end context 'when the included association uses a different database connection' do @@ -151,6 +174,30 @@ def association_connection.current_database include_examples 'left outer join' include_examples 'records' + include_examples 'filtered resource fields selection' + end + + context 'when the configuration to optimize query select fields is disabled' do + let(:expected_selected_resource_attributes) { Product.attribute_names } + + before do + allow(ForestLiana).to receive(:optimize_query_select_fields).and_return(false) + end + + include_examples 'records' + + it 'selects all fields from the main resource' do + full_sql = getter.perform.to_sql + + # Extract only the SELECT column list (between SELECT and the first FROM) + select_match = full_sql.match(/SELECT\s+(.*?)\s+FROM/i) + expect(select_match).not_to be_nil + select_columns_sql = select_match[1] + + expected_selected_resource_attributes.each do |attribute| + expect(select_columns_sql).to include(%Q{"products"."#{attribute}"}) + end + end end end @@ -651,4 +698,4 @@ def association_connection.current_database end end end -end \ No newline at end of file +end