class RuboCop::Cop::Rails::PluckInWhere


Post.where(user_id: active_users.pluck(:id))
# bad
@example EnforcedStyle: aggressive
Post.where(user_id: active_users.pluck(:id))
# good
@example EnforcedStyle: conservative (default)
Post.where.not(user_id: active_users.select(:id))
Post.where(user_id: active_users.select(:id))
Post.where(user_id: User.active.select(:id))
# good
Post.where.not(user_id: User.active.pluck(:id))
Post.where(user_id: User.active.ids)
Post.where(user_id: User.active.pluck(:id))
# bad
@example
differently or using ‘pluck`.
cause significant performance issues compared to writing the query
subquery result sequentially, rather than using an index. This can
well. They need to scan all records of the outer table against the
databases like PostgreSQL and MySQL can’t optimize complex queries as
Additionally, when using a subquery with the SQL ‘IN` operator,
on an `Array` instance.
`pluck` on an `ActiveRecord::Relation` instance and calls to `pluck`
positives because the check cannot distinguish between calls to
within `where` are considered offenses. This might lead to false
When `EnforcedStyle` is set to `aggressive`, all calls to `pluck`
@safety
(e.g. a model class) within `where` are considered offenses.
to `conservative` (the default), only calls to `pluck` on a constant
This cop has two modes of enforcement. When the `EnforcedStyle` is set
a subquery.
using `select` helps to avoid additional database queries by running as
Since `pluck` is an eager method and hits the database immediately,
and can be replaced with `select`.
Identifies places where `pluck` is used in `where` query methods

def on_send(node)

def on_send(node)
  return unless in_where?(node)
  return if style == :conservative && !root_receiver(node)&.const_type?
  range = node.loc.selector
  if node.method?(:ids)
    replacement = 'select(:id)'
    message = MSG_IDS
  else
    replacement = 'select'
    message = MSG_SELECT
  end
  add_offense(range, message: message) do |corrector|
    corrector.replace(range, replacement)
  end
end

def root_receiver(node)

def root_receiver(node)
  receiver = node.receiver
  if receiver&.call_type?
    root_receiver(receiver)
  else
    receiver
  end
end