Class: ActiveRecord::QueryMethods::WhereChain

Inherits:
Object
  • Object
show all
Includes:
ActiveModel::ForbiddenAttributesProtection
Defined in:
activerecord/lib/active_record/relation/query_methods.rb

Overview

WhereChain objects act as placeholder for queries in which #where does not have any parameter. In this case, #where must be chained with #not to return a new relation.

Instance Method Summary collapse

Constructor Details

#initialize(scope) ⇒ WhereChain

Returns a new instance of WhereChain.


20
21
22
# File 'activerecord/lib/active_record/relation/query_methods.rb', line 20

def initialize(scope)
  @scope = scope
end

Instance Method Details

#missing(*args) ⇒ Object

Returns a new relation with left outer joins and where clause to identify missing relations.

For example, posts that are missing a related author:

Post.where.missing(:author)
# SELECT "posts".* FROM "posts"
# LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# WHERE "authors"."id" IS NULL

Additionally, multiple relations can be combined. This will return posts that are missing both an author and any comments:

Post.where.missing(:author, :comments)
# SELECT "posts".* FROM "posts"
# LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id"
# WHERE "authors"."id" IS NULL AND "comments"."id" IS NULL

91
92
93
94
95
96
97
98
99
100
# File 'activerecord/lib/active_record/relation/query_methods.rb', line 91

def missing(*args)
  args.each do |arg|
    reflection = @scope.klass._reflect_on_association(arg)
    opts = { reflection.table_name => { reflection.association_primary_key => nil } }
    @scope.left_outer_joins!(arg)
    @scope.where!(opts)
  end

  @scope
end

#not(opts, *rest) ⇒ Object

Returns a new relation expressing WHERE + NOT condition according to the conditions in the arguments.

#not accepts conditions as a string, array, or hash. See QueryMethods#where for more details on each format.

User.where.not("name = 'Jon'")
# SELECT * FROM users WHERE NOT (name = 'Jon')

User.where.not(["name = ?", "Jon"])
# SELECT * FROM users WHERE NOT (name = 'Jon')

User.where.not(name: "Jon")
# SELECT * FROM users WHERE name != 'Jon'

User.where.not(name: nil)
# SELECT * FROM users WHERE name IS NOT NULL

User.where.not(name: %w(Ko1 Nobu))
# SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'activerecord/lib/active_record/relation/query_methods.rb', line 44

def not(opts, *rest)
  opts = sanitize_forbidden_attributes(opts)

  where_clause = @scope.send(:where_clause_factory).build(opts, rest)

  @scope.references!(PredicateBuilder.references(opts)) if Hash === opts

  if not_behaves_as_nor?(opts)
    ActiveSupport::Deprecation.warn(<<~MSG.squish)
      NOT conditions will no longer behave as NOR in Rails 6.1.
      To continue using NOR conditions, NOT each condition individually
      (`#{
        opts.flat_map { |key, value|
          if value.is_a?(Hash) && value.size > 1
            value.map { |k, v| ".where.not(#{key.inspect} => { #{k.inspect} => ... })" }
          else
            ".where.not(#{key.inspect} => ...)"
          end
        }.join
      }`).
    MSG
    @scope.where_clause += where_clause.invert(:nor)
  else
    @scope.where_clause += where_clause.invert
  end

  @scope
end