From 45661589ce1d8e47311d7daee2e4201ffb6f3ad3 Mon Sep 17 00:00:00 2001 From: Duccio Giovannelli Date: Mon, 18 Nov 2024 10:58:31 +0100 Subject: [PATCH] fix: #1411 --- lib/ransack/nodes/condition.rb | 13 ++++++- .../adapters/active_record/base_spec.rb | 35 +++++++++++++++++++ spec/ransack/search_spec.rb | 2 +- spec/support/schema.rb | 7 ++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/lib/ransack/nodes/condition.rb b/lib/ransack/nodes/condition.rb index af69c037..a6e17069 100644 --- a/lib/ransack/nodes/condition.rb +++ b/lib/ransack/nodes/condition.rb @@ -286,7 +286,14 @@ def negative? def arel_predicate predicate = attributes.map { |attribute| association = attribute.parent - if negative? && attribute.associated_collection? + parent_table = association.table + + if parent_table.class == Arel::Nodes::TableAlias + left = parent_table.left.name + parent_table.right = left if !ActiveRecord::Base.connection.table_exists?(left) + end + + if negative? && attribute.associated_collection? && not_nested_condition(attribute, parent_table) query = context.build_correlated_subquery(association) context.remove_association(association) if self.predicate_name == 'not_null' && self.value @@ -313,6 +320,10 @@ def arel_predicate predicate end + def not_nested_condition(attribute, parent_table) + parent_table.class != Arel::Nodes::TableAlias && attribute.name.starts_with?(parent_table.name) + end + private def combinator_method diff --git a/spec/ransack/adapters/active_record/base_spec.rb b/spec/ransack/adapters/active_record/base_spec.rb index d41cd6d0..59a7390d 100644 --- a/spec/ransack/adapters/active_record/base_spec.rb +++ b/spec/ransack/adapters/active_record/base_spec.rb @@ -191,6 +191,41 @@ module ActiveRecord end end + context 'negative conditions on related object with HABTM associations' do + let(:medieval) { Tag.create!(name: 'Medieval') } + let(:fantasy) { Tag.create!(name: 'Fantasy') } + let(:arthur) { Article.create!(title: 'King Arthur') } + let(:marco) { Article.create!(title: 'Marco Polo') } + let(:comment_arthur) { marco.comments.create!(body: 'King Arthur comment') } + let(:comment_marco) { arthur.comments.create!(body: 'Marco Polo comment') } + + before do + comment_arthur.tags << medieval + comment_marco.tags << fantasy + end + + it 'removes redundant joins from top query' do + s = Article.ransack(comments_tags_name_not_eq: "Fantasy") + sql = s.result.to_sql + expect(sql).to include('LEFT OUTER JOIN') + end + + it 'handles != for single values' do + s = Article.ransack(comments_tags_name_not_eq: "Fantasy") + articles = s.result.to_a + expect(articles).to include marco + expect(articles).to_not include arthur + end + + it 'handles NOT IN for multiple attributes' do + s = Article.ransack(comments_tags_name_not_in: ["Fantasy", "Scifi"]) + articles = s.result.to_a + + expect(articles).to include marco + expect(articles).to_not include arthur + end + end + context 'negative conditions on self-referenced associations' do let(:pop) { Person.create!(name: 'Grandpa') } let(:dad) { Person.create!(name: 'Father') } diff --git a/spec/ransack/search_spec.rb b/spec/ransack/search_spec.rb index 6426a20a..3c22a688 100644 --- a/spec/ransack/search_spec.rb +++ b/spec/ransack/search_spec.rb @@ -178,7 +178,7 @@ module Ransack # AND "articles"."title" = 'Test' AND "articles"."published" = 't' AND ('default_scope' = 'default_scope') # ) ORDER BY "people"."id" DESC - pending("spec should pass, but I do not know how/where to fix lib code") + #pending("spec should pass, but I do not know how/where to fix lib code") s = Search.new(Person, published_articles_title_not_eq: 'Test') expect(s.result.to_sql).to include 'default_scope' expect(s.result.to_sql).to include 'published' diff --git a/spec/support/schema.rb b/spec/support/schema.rb index 555299c4..cf8bf454 100644 --- a/spec/support/schema.rb +++ b/spec/support/schema.rb @@ -220,12 +220,14 @@ class Article < ::Article class Comment < ApplicationRecord belongs_to :article belongs_to :person + has_and_belongs_to_many :tags default_scope { where(disabled: false) } end class Tag < ApplicationRecord has_and_belongs_to_many :articles + has_and_belongs_to_many :comments end class Note < ApplicationRecord @@ -298,6 +300,11 @@ def self.create t.integer :tag_id end + create_table :comments_tags, force: true, id: false do |t| + t.integer :comment_id + t.integer :tag_id + end + create_table :notes, force: true do |t| t.integer :notable_id t.string :notable_type