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