diff --git a/app/services/party_query_builder.rb b/app/services/party_query_builder.rb index f151ab8..600ed9c 100644 --- a/app/services/party_query_builder.rb +++ b/app/services/party_query_builder.rb @@ -46,15 +46,11 @@ class PartyQueryBuilder # Example edge case: If the request does not specify 'characters_count', # then the default (e.g. 3) will be used, with the upper bound coming from a constant. def apply_filters(query) - conditions = build_filters - query = query.where(conditions) - # If name_quality filtering is enabled via params, apply it. - query = query.where(name_quality) if @params[:name_quality].present? - query.where( - weapons_count: build_count(@params[:weapons_count], default_weapons_count)..max_weapons, - characters_count: build_count(@params[:characters_count], default_characters_count)..max_characters, - summons_count: build_count(@params[:summons_count], default_summons_count)..max_summons - ) + query = apply_base_filters(query) + query = apply_name_quality_filter(query) + query = apply_count_filters(query) + + query end # Example callback method: if no explicit status filter is provided, we may want @@ -148,6 +144,63 @@ class PartyQueryBuilder query end + # Applies base filtering conditions from build_filters to the query. + # @param query [ActiveRecord::QueryMethods::WhereChain] The current query. + # @return [ActiveRecord::Relation] The query with base filters applied. + def apply_base_filters(query) + query.where(build_filters) + end + + # Applies the name quality filter to the query if the parameter is present. + # @param query [ActiveRecord::QueryMethods::WhereChain] The current query. + # @return [ActiveRecord::Relation] The query with the name quality filter applied. + def apply_name_quality_filter(query) + @params[:name_quality].present? ? query.where(name_quality) : query + end + + # Applies count filters to the query based on provided parameters or default options. + # If apply_defaults is set in options, default ranges are applied. + # Otherwise, count ranges are built from provided parameters. + # @param query [ | ActiveRecord::QueryMethods::WhereChain] The current query. + # @return [ActiveRecord::Relation] The query with count filters applied. + def apply_count_filters(query) + if @options[:apply_defaults] + query.where( + weapons_count: default_weapons_count..max_weapons, + characters_count: default_characters_count..max_characters, + summons_count: default_summons_count..max_summons + ) + elsif count_filter_provided? + query.where(build_count_conditions) + else + query + end + end + + # Determines if any count filter parameters have been provided. + # @return [Boolean] True if any count filters are provided, false otherwise. + def count_filter_provided? + @params.key?(:weapons_count) || @params.key?(:characters_count) || @params.key?(:summons_count) + end + + # Builds a hash of count conditions based on the count filter parameters. + # @return [Hash] A hash with keys :weapons_count, :characters_count, and :summons_count. + def build_count_conditions + { + weapons_count: build_range(@params[:weapons_count], max_weapons), + characters_count: build_range(@params[:characters_count], max_characters), + summons_count: build_range(@params[:summons_count], max_summons) + } + end + + # Constructs a range for a given count parameter. + # @param param_value [String, nil] The count filter parameter value. + # @param max_value [Integer] The maximum allowed value for the count. + # @return [Range] A range from the provided count (or 0 if blank) to the max_value. + def build_range(param_value, max_value) + param_value.present? ? param_value.to_i..max_value : 0..max_value + end + # Maps an ID’s first character to the corresponding grid table and object table names. # # For example: @@ -170,27 +223,27 @@ class PartyQueryBuilder # Default values and maximum limits for counts. def default_weapons_count - @options[:default_weapons_count] || 5; + @options[:default_weapons_count] || 5 end def default_characters_count - @options[:default_characters_count] || 3; + @options[:default_characters_count] || 3 end def default_summons_count - @options[:default_summons_count] || 2; + @options[:default_summons_count] || 2 end def max_weapons - @options[:max_weapons] || 13; + @options[:max_weapons] || 13 end def max_characters - @options[:max_characters] || 5; + @options[:max_characters] || 5 end def max_summons - @options[:max_summons] || 8; + @options[:max_summons] || 8 end # Stub method for name quality filtering. diff --git a/spec/services/party_query_builder_spec.rb b/spec/services/party_query_builder_spec.rb index 983387f..17be922 100644 --- a/spec/services/party_query_builder_spec.rb +++ b/spec/services/party_query_builder_spec.rb @@ -34,7 +34,6 @@ RSpec.describe PartyQueryBuilder, type: :model do it 'returns an ActiveRecord::Relation with filters applied' do query = subject.build sql = query.to_sql - # Expect the element filter to be applied (converted to integer) expect(sql).to include('"parties"."element" = 3') # Expect the raid filter to be applied @@ -192,6 +191,61 @@ RSpec.describe PartyQueryBuilder, type: :model do expect(sql).not_to include('NOT EXISTS (') end end + + context 'when apply_defaults option is true' do + subject do + described_class.new( + base_query, + params: params, + current_user: current_user, + options: { apply_defaults: true } + ) + end + + it 'adds count filters to the query' do + query = subject.build + sql = query.to_sql + expect(sql).to include('"weapons_count" BETWEEN') + expect(sql).to include('"characters_count" BETWEEN') + expect(sql).to include('"summons_count" BETWEEN') + end + end + + context 'when apply_defaults option is false (or not provided)' do + let(:blanked_params) do + { + element: '3', + raid: '123e4567-e89b-12d3-a456-426614174000', + recency: '3600', + full_auto: '1', + auto_guard: '0', + charge_attack: '1', + weapons_count: '', # blank => should use default + characters_count: '', + summons_count: '', + includes: '300001,200002', + excludes: '100003', + name_quality: '1' # dummy flag for testing name_quality clause + } + end + + subject do + described_class.new( + base_query, + params: blanked_params, + current_user: current_user, + options: {} + ) + end + + it 'does not add count filters to the query' do + query = subject.build + sql = query.to_sql + expect(sql).not_to include('weapons_count BETWEEN') + expect(sql).not_to include('characters_count BETWEEN') + expect(sql).not_to include('summons_count BETWEEN') + end + end end end end