Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 16 additions & 17 deletions lib/active_record/filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,30 +47,29 @@ def self.filter_joins(klass, filters)
custom = []
[build_filter_joins(klass, filters, [], custom), custom]
end

def self.materialize_joins(filters, js, custom, relations)
return if js.nil?

case js
when Array
js.map { |j| materialize_joins(filters, j, custom, relations) }
when Proc
materialize_joins(filters, js.call(filters), custom, relations)
when String
custom << js
else
relations << js
end
end

def self.build_filter_joins(klass, filters, relations=[], custom=[])
if filters.is_a?(Array)
filters.each { |f| build_filter_joins(klass, f, relations, custom) }.compact
elsif filters.is_a?(Hash)
filters.each do |key, value|
if klass.filters.has_key?(key.to_sym)
js = klass.filters.dig(key.to_sym, :joins)

if js.is_a?(Array)
js.each do |j|
if j.is_a?(String)
custom << j
else
relations << j
end
end
elsif js
if js.is_a?(String)
custom << js
else
relations << js
end
end
materialize_joins(filters[key], klass.filters.dig(key.to_sym, :joins), custom, relations)
elsif reflection = klass._reflections[key.to_s]
if value.is_a?(Hash)
relations << if reflection.polymorphic?
Expand Down
69 changes: 69 additions & 0 deletions test/filter_relationship_test/has_many_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,64 @@ class HasManyFilterTest < ActiveSupport::TestCase

class Account < ActiveRecord::Base
has_many :photos

js = -> (filters) do
x = if filters.is_a?(Array) && filters.size > 1
reflection = self.reflect_on_association(:photos)
filters.reduce([]) do |sum, f|
if !f.is_a?(String)
right_table = reflection.klass.arel_table.alias("photos_#{sum.size}")
left_table = reflection.active_record.arel_table
on = right_table[reflection.foreign_key].eq(left_table[reflection.klass.primary_key])
sum + left_table.join(right_table, Arel::Nodes::OuterJoin).on(on).join_sources
else
sum
end
end
else
:photos
end
end

filter_on :joinalias, js do |klass, table, key, value, relation_trail, alias_tracker|
if value.is_a?(Array) && value.size > 1
reflection = klass.reflect_on_association(:photos)

builder = ActiveRecord::PredicateBuilder.new(ActiveRecord::TableMetadata.new(reflection.klass, reflection.klass.arel_table.alias("photos_0"), reflection))
node = builder.build_from_filter_hash(value.shift, relation_trail + [reflection.name], alias_tracker)
n = value.shift(2)
t = 1
while !n.empty?
builder = ActiveRecord::PredicateBuilder.new(ActiveRecord::TableMetadata.new(reflection.klass, reflection.klass.arel_table.alias("photos_#{t}"), reflection))
t += 1
n[1] = builder.build_from_filter_hash(n[1], relation_trail + [reflection.name], alias_tracker)
if n[0] == 'AND'
if node.is_a?(Arel::Nodes::And)
node.children.push(n[1])
else
node = node.and(n[1])
end
elsif n[0] == 'OR'
node = Arel::Nodes::Grouping.new(node).or(Arel::Nodes::Grouping.new(n[1]))
elsif !n[0].is_a?(String)
builder = ActiveRecord::PredicateBuilder.new(ActiveRecord::TableMetadata.new(reflection.klass, reflection.klass.arel_table.alias("photos_#{t}"), reflection))
t += 1
n[0] = builder.build_from_filter_hash(n[0], relation_trail + [reflection.name], alias_tracker)
if node.is_a?(Arel::Nodes::And)
node.children.push(n[0])
else
node = node.and(n[0])
end
else
raise 'lll'
end
n = value.shift(2)
end
node
else
expand_filter_for_relationship(relation, value, relation_trail, alias_tracker)
end
end
end

class Photo < ActiveRecord::Base
Expand Down Expand Up @@ -165,4 +223,15 @@ class Property < ActiveRecord::Base
# assert_equal [a2], Property.filter(:listings => { :type => 'sublease'} )
# end

test "::filter custom!" do
query = Account.filter(joinalias: [{id: 1}, 'AND', {id: 2}])

assert_equal(<<-SQL.strip.gsub(/\s+/, ' '), query.to_sql.strip.gsub('"', ''))
SELECT accounts.* FROM accounts
LEFT OUTER JOIN photos photos_0 ON photos_0.account_id = accounts.id
LEFT OUTER JOIN photos photos_1 ON photos_1.account_id = accounts.id
WHERE photos_0.id = 1
AND photos_1.id = 2
SQL
end
end