Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ransack generates redundant left join statements #1433

Open
itsalongstory opened this issue Jul 28, 2023 · 3 comments
Open

Ransack generates redundant left join statements #1433

itsalongstory opened this issue Jul 28, 2023 · 3 comments

Comments

@itsalongstory
Copy link
Contributor

test_ransack.rb

require 'bundler/inline'

gemfile(true) do
  source 'https://rubygems.org'
  gem 'activerecord', '~> 7.0', '>= 7.0.6', require: "active_record"
  gem 'sqlite3', '~> 1.6', '>= 1.6.3'
  gem 'ransack', '~> 4.0'
  gem 'minitest', '~> 5.19', require: "minitest/autorun"
end

ActiveRecord::Base.establish_connection(
  adapter:  "sqlite3",
  database: "./test_ransack_search"
)

ActiveRecord::Schema.define do
  drop_table(:authors, if_exists: true)
  drop_table(:articles, if_exists: true)
  drop_table(:comments, if_exists: true)

  create_table :authors do |t|
    t.string :name
  end

  create_table :articles do |t|
    t.belongs_to :author
    t.string :title
  end

  create_table :comments do |t|
    t.belongs_to :article
    t.string :content
  end
end

class ApplicationRecord < ActiveRecord::Base
  primary_abstract_class

  def self.ransackable_attributes(auth_object = nil)
    authorizable_ransackable_attributes
  end

  def self.ransackable_associations(auth_object = nil)
    authorizable_ransackable_associations
  end
end

class Author < ApplicationRecord
  has_many :articles
end

class Article < ApplicationRecord
  has_many :comments
  belongs_to :author
end

class Comment < ApplicationRecord
  belongs_to :article
end

class MyTest < Minitest::Test
  # success
  def test_1
    query_params = { article_title_eq: 'abc' }
    assert_equal "SELECT \"comments\".* FROM \"comments\" INNER JOIN \"articles\" ON \"articles\".\"id\" = \"comments\".\"article_id\" WHERE \"articles\".\"title\" = 'abc'", Comment.joins(:article).ransack(query_params).result.to_sql
  end

  # failure
  def test_2
    query_params = { article_author_name_eq: 'abc' }
    assert_equal "SELECT \"comments\".* FROM \"comments\" INNER JOIN \"articles\" ON \"articles\".\"id\" = \"comments\".\"article_id\" INNER JOIN \"authors\" ON \"authors\".\"id\" = \"articles\".\"author_id\" WHERE \"authors\".\"name\" = 'abc'", Comment.joins(article: :author).ransack(query_params).result.to_sql
  end
end
deploy@fms-test:~$ ruby test_ransack.rb 
Fetching gem metadata from https://gems.ruby-china.com/........
Resolving dependencies...
Using concurrent-ruby 1.2.2
Using minitest 5.19.0
Using i18n 1.14.1
Using bundler 2.4.16
Using tzinfo 2.0.6
Using sqlite3 1.6.3 (x86_64-linux)
Using activesupport 7.0.6
Using activemodel 7.0.6
Using activerecord 7.0.6
Using ransack 4.0.0
-- drop_table(:authors, {:if_exists=>true})
   -> 0.0503s
-- drop_table(:articles, {:if_exists=>true})
   -> 0.0358s
-- drop_table(:comments, {:if_exists=>true})
   -> 0.0358s
-- create_table(:authors)
   -> 0.0359s
-- create_table(:articles)
   -> 0.0719s
-- create_table(:comments)
   -> 0.0717s
Run options: --seed 62497

# Running:

.F

Finished in 0.060084s, 33.2867 runs/s, 33.2867 assertions/s.

  1) Failure:
MyTest#test_2 [test_ransack.rb:71]:
--- expected
+++ actual
@@ -1 +1 @@
-"SELECT \"comments\".* FROM \"comments\" INNER JOIN \"articles\" ON \"articles\".\"id\" = \"comments\".\"article_id\" INNER JOIN \"authors\" ON \"authors\".\"id\" = \"articles\".\"author_id\" WHERE \"authors\".\"name\" = 'abc'"
+"SELECT \"comments\".* FROM \"comments\" INNER JOIN \"articles\" ON \"articles\".\"id\" = \"comments\".\"article_id\" INNER JOIN \"authors\" ON \"authors\".\"id\" = \"articles\".\"author_id\" LEFT OUTER JOIN \"authors\" \"authors_articles\" ON \"authors_articles\".\"id\" = \"articles\".\"author_id\" WHERE \"authors\".\"name\" = 'abc'"


2 runs, 2 assertions, 1 failures, 0 errors, 0 skips
@itsalongstory
Copy link
Contributor Author

itsalongstory commented Jul 28, 2023

In test_2, ransack generate sql like this:

SELECT
    "comments".*
FROM
    "comments"
    INNER JOIN "articles" ON "articles"."id" = "comments"."article_id"
    INNER JOIN "authors" ON "authors"."id" = "articles"."author_id"
    LEFT OUTER JOIN "authors" "authors_articles" ON "authors_articles"."id" = "articles"."author_id" -- ?????
WHERE
    "authors"."name" = 'abc'

The LEFT OUTER JOIN ... is redundant, isn't it?

What we need is just like below:

SELECT
    "comments".*
FROM
    "comments"
    INNER JOIN "articles" ON "articles"."id" = "comments"."article_id"
    INNER JOIN "authors" ON "authors"."id" = "articles"."author_id"
WHERE
    "authors"."name" = 'abc'

@hcyildirim
Copy link

I've noticed the same thing in my application, it brokes some of my SQL calculation. Did you find any solution to this? @itsalongstory

@itsalongstory
Copy link
Contributor Author

I've noticed the same thing in my application, it brokes some of my SQL calculation. Did you find any solution to this? @itsalongstory

Not yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants