diff --git a/app/models/dragnet/data_grid/query.rb b/app/models/dragnet/data_grid/query.rb index e37db22..705865d 100644 --- a/app/models/dragnet/data_grid/query.rb +++ b/app/models/dragnet/data_grid/query.rb @@ -5,7 +5,6 @@ class DataGrid::Query include Dragnet include Memoizable - attr_reader :survey attr_reader :params delegate :to_sql, to: :records @@ -27,10 +26,6 @@ def filter_by params.fetch(:filter_by, EMPTY_HASH) end - def sort_by_question? - uuid?(sort_by) - end - # @return [Array] def question_ids filter_by.keys.select { uuid?(_1) } @@ -47,9 +42,7 @@ def questions # @return [Hash{String => Question}] def questions_map - questions.each_with_object({}) do |question, map| - map[question.id] = question - end + questions.index_by(&:id) end memoize :questions_map @@ -63,98 +56,16 @@ def question?(question_id) questions_map.key?(question_id) end - attr_writer :join_alias_index - - def join_alias_index - @join_alias_index ||= 0 - end - - def next_join_alias - "answers_#{join_alias_index}".tap do - self.join_alias_index += 1 - end - end - - private :join_alias_index, :join_alias_index=, :next_join_alias - - def join_aliases - filter_by.each_with_object({}) do |(field, _), aliases| - aliases[field] = next_join_alias if uuid?(field) - end - end - memoize :join_aliases - - def join_alias(question_id) - join_aliases.fetch(question_id) do - raise "unknown join alias for `#{question_id}`" - end - end - - def no_joins? - join_aliases.count.zero? - end - # @return [ActiveRecord::Relation] def records - ordered_records(filtered_records(survey.replies)) + relation.build end private - def sorting_scope(scope, question) - if no_joins? - scope.joins(:answers).where(answers: { question_id: question.id }) - else - join_name = next_join_alias - join_aliases[:sorting] = join_name - scope - .joins(Arel.sql("inner join answers #{join_name} on replies.id = #{join_name}.reply_id")) - .where(join_name => { question_id: question.id }) - end - end - - def ordered_records(scope) - if !sort_by_question? - scope.order(sort_by => sort_direction) - else - question = Question.includes(:question_type).find(sort_by) - sorted_records(question, sorting_scope(scope, question)) - end - end - - def sorted_records(question, scope) - Perspectives::DataGridSortQuery - .get(question.question_type) - .sort(question, scope, sort_direction, join_aliases.fetch(:sorting, :answers)) - end - - SIMPLE_FILTER_ATTRIBUTES = %i[created_at user_id].to_set.freeze - private_constant :SIMPLE_FILTER_ATTRIBUTES - - def filtered_records(scope) - return scope if filter_by.empty? - - filter_by.reduce(scope) do |current_scope, (field, value)| - if SIMPLE_FILTER_ATTRIBUTES.include?(field) - current_scope.where(field => value) - elsif question?(field) - join_name = join_alias(field) - narrowed = narrowed_scope(current_scope, field, join_name) - filtered_values(narrowed, question(field), join_name, value) - else - current_scope - end - end - end - - def narrowed_scope(scope, field, join_name) - scope - .joins(Arel.sql("inner join answers #{join_name} on replies.id = #{join_name}.reply_id")) - .where(join_name => { question_id: field }) - end - - def filtered_values(scope, question, table, value) - Perspectives::DataGridFilterQuery.get(question.question_type).filter(question, scope, table, value) + def relation + QueryRelation.new(@survey.replies, sort_by:, sort_direction:, filter_by:) end + memoize :relation end -end \ No newline at end of file +end diff --git a/app/models/dragnet/data_grid/query_relation.rb b/app/models/dragnet/data_grid/query_relation.rb new file mode 100644 index 0000000..537adce --- /dev/null +++ b/app/models/dragnet/data_grid/query_relation.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +module Dragnet + module DataGrid + class QueryRelation + include Dragnet + prepend Memoizable + + attr_reader :sort_by, :sort_direction, :filter_by + + def initialize(base_relation, params) + @base_relation = base_relation + @sort_by, @sort_direction, @filter_by = + params.values_at(:sort_by, :sort_direction, :filter_by) + end + + # @return [ActiveRecord::Relation] + def build + ordered_records(filtered_records(@base_relation)) + end + + private + + attr_writer :join_alias_index + + def join_alias_index + @join_alias_index ||= 0 + end + + def next_join_alias + "answers_#{join_alias_index}".tap do + self.join_alias_index += 1 + end + end + + def join_aliases + filter_by.each_with_object({}) do |(field, _), aliases| + aliases[field] = next_join_alias if uuid?(field) + end + end + memoize :join_aliases + + def join_alias(question_id) + join_aliases.fetch(question_id) do + raise "unknown join alias for `#{question_id}`" + end + end + + def no_joins? + join_aliases.count.zero? + end + + def sort_by_question? + uuid?(sort_by) + end + + def ordered_records(scope) + if !sort_by_question? + scope.order(sort_by => sort_direction) + else + question = Question.includes(:question_type).find(sort_by) + sorted_records(question, sorting_scope(scope, question)) + end + end + + def sorting_scope(scope, question) + if no_joins? + scope.joins(:answers).where(answers: { question_id: question.id }) + else + join_name = next_join_alias + join_aliases[:sorting] = join_name + scope + .joins(Arel.sql("inner join answers #{join_name} on replies.id = #{join_name}.reply_id")) + .where(join_name => { question_id: question.id }) + end + end + + def sorted_records(question, scope) + Perspectives::DataGridSortQuery + .get(question.question_type) + .sort(question, scope, sort_direction, join_aliases.fetch(:sorting, :answers)) + end + + SIMPLE_FILTER_ATTRIBUTES = %i[created_at user_id].to_set.freeze + private_constant :SIMPLE_FILTER_ATTRIBUTES + + def filtered_records(scope) + return scope if filter_by.empty? + + filter_by.reduce(scope) do |current_scope, (field, value)| + if SIMPLE_FILTER_ATTRIBUTES.include?(field) + current_scope.where(field => value) + elsif question?(field) + join_name = join_alias(field) + narrowed = narrowed_scope(current_scope, field, join_name) + filtered_values(narrowed, question(field), join_name, value) + else + current_scope + end + end + end + + def narrowed_scope(scope, field, join_name) + scope + .joins(Arel.sql("inner join answers #{join_name} on replies.id = #{join_name}.reply_id")) + .where(join_name => { question_id: field }) + end + + def filtered_values(scope, question, table, value) + Perspectives::DataGridFilterQuery.get(question.question_type).filter(question, scope, table, value) + end + end + end +end