Skip to content

Commit

Permalink
Add Dragnet::DataGrid::QueryRelation
Browse files Browse the repository at this point in the history
  • Loading branch information
delonnewman committed Feb 24, 2024
1 parent 90caca5 commit 7e05b04
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 95 deletions.
101 changes: 6 additions & 95 deletions app/models/dragnet/data_grid/query.rb
Expand Up @@ -5,7 +5,6 @@ class DataGrid::Query
include Dragnet
include Memoizable

attr_reader :survey
attr_reader :params

delegate :to_sql, to: :records
Expand All @@ -27,10 +26,6 @@ def filter_by
params.fetch(:filter_by, EMPTY_HASH)
end

def sort_by_question?
uuid?(sort_by)
end

# @return [Array<String>]
def question_ids
filter_by.keys.select { uuid?(_1) }
Expand All @@ -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

Expand All @@ -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<Reply>]
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
end
114 changes: 114 additions & 0 deletions 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<Reply>]
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

0 comments on commit 7e05b04

Please sign in to comment.