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

Add new rule to prevent using any? where user can use exists? #472

Closed
loqimean opened this issue Sep 25, 2024 · 1 comment
Closed

Add new rule to prevent using any? where user can use exists? #472

loqimean opened this issue Sep 25, 2024 · 1 comment

Comments

@loqimean
Copy link

Is your feature request related to a problem? Please describe.

I'm always frustrated when my teammates uses method any? on an ActiveRecord collections that causes perfomance problem.

Describe the solution you'd like

Would be fine to get the warning when user uses the any? method, where it can be used the exists? method 'cause perfomance purpose:
Why Prefer exists? Over any?
any? loads all records into memory and checks for the presence of at least one.
exists? translates into a more efficient SQL query (like SELECT 1 FROM ... LIMIT 1), which checks for the existence of a record at the database level without loading the data.
When exists? should be used:

When you're working with ActiveRecord collections or queries, use exists? for performance efficiency, especially when dealing with large datasets.
When any? can still be used:

any? can be used safely for arrays or other in-memory collections, as it directly checks for elements without triggering database queries.

Describe alternatives you've considered

I don't guess that exists.

Additional context

no one.

@Earlopain
Copy link
Contributor

This is not possible for RuboCop to do. It has to distinguish variables between arrays and AR-Relations which it just can't do.

Regardless, I don't believe your suggestion is correct. Rails has optimizations in place where unnecessary queries will not be made:

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  gem "rails"
  # If you want to test against edge Rails replace the previous line with this:
  # gem "rails", github: "rails/rails", branch: "main"

  gem "sqlite3"
end

require "active_record"
require "active_record/testing/query_assertions"
require "minitest/autorun"
require "logger"

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :posts, force: true do |t|
  end
end

class Post < ActiveRecord::Base
end

Post.create!

class BugTest < ActiveSupport::TestCase
  include ActiveRecord::Assertions::QueryAssertions

  def test_any
    relation = Post.all

    assert_queries_count(1) do
      assert_queries_match(/LIMIT /) do
        relation.all.any?
      end
    end
  end

  def test_any_loaded
    relation = Post.all.load
    assert_queries_count(0) do
      relation.any?
    end
  end

  def test_exists
    relation = Post.all
    assert_queries_count(1) do
      assert_queries_match(/LIMIT /) do
        relation.exists?
      end
    end
  end

  def test_exists_loaded
    relation = Post.all.load
    assert_queries_count(1) do
      assert_queries_match(/LIMIT /) do
        relation.exists?
      end
    end
  end
end

The same also applies to associations. exists? will always make a query, while any? will make a query like exists? if not loaded and skip the query if already loaded. It's nicely explained here: rubocop/rails-style-guide#232 (comment)

Your suggestion probably applies to present? but unfortunatly this is also not possible for the same reasons, anything responds to present? with rails.

@koic koic closed this as not planned Won't fix, can't repro, duplicate, stale Sep 27, 2024
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

3 participants