ChatGPT解决这个技术问题 Extra ChatGPT

Rails where condition using NOT NIL

Using the rails 3 style how would I write the opposite of:

Foo.includes(:bar).where(:bars=>{:id=>nil})

I want to find where id is NOT nil. I tried:

Foo.includes(:bar).where(:bars=>{:id=>!nil}).to_sql

But that returns:

=> "SELECT     \"foos\".* FROM       \"foos\"  WHERE  (\"bars\".\"id\" = 1)"

That's definitely not what I need, and almost seems like a bug in ARel.

!nil evaluates to true in Ruby, and ARel translates true to 1 in a SQL query. So the generated query is in fact what you asked for - this was not an ARel bug.

n
notapatch

Rails 4+

ActiveRecord 4.0 and above adds where.not so you can do this:

Foo.includes(:bar).where.not('bars.id' => nil)
Foo.includes(:bar).where.not(bars: { id: nil })

When working with scopes between tables, I prefer to leverage merge so that I can use existing scopes more easily.

Foo.includes(:bar).merge(Bar.where.not(id: nil))

Also, since includes does not always choose a join strategy, you should use references here as well, otherwise you may end up with invalid SQL.

Foo.includes(:bar)
   .references(:bar)
   .merge(Bar.where.not(id: nil))

Rails 3

The canonical way to do this with Rails 3:

Foo.includes(:bar).where("bars.id IS NOT NULL")

Last one here isn't working for me, do we need an extra gem or plugin for this? I get: rails undefined method 'not_eq' for :confirmed_at:Symbol..
@Tim Yes, the MetaWhere gem I linked above.
I like the solution that doesn't require other gems :) even if it is a bit ugly
@oreoshake MetaWhere/Squeel are well worth having, this is just a tiny facet. But of course a general case is good to know.
@BKSpurgeon Chaining where conditions is simply building an AST, it doesn't hit the database until you hit a terminal method like each or to_a. Building the query isn't a performance concern; what you're requesting from the database is.
R
Ryan Bigg

It's not a bug in ARel, it's a bug in your logic.

What you want here is:

Foo.includes(:bar).where(Bar.arel_table[:id].not_eq(nil))

I'm curious what the logic is then for turning !nil into '1'
At a guess, !nil returns true, which is a boolean. :id => true gets you id = 1 in SQLese.
This is a good way to avoid writing raw sql fragments. The syntax isn't as concise as Squeel though.
I have not managed to do this with sqlite3. sqlite3 wants to see field_name != 'NULL'.
@zetetic Unless you're using postgres, in which case you get id = 't' :)
R
Raed Tulefat

Not sure of this is helpful but this what worked for me in Rails 4

Foo.where.not(bar: nil)

This is the best answer here. -- 2017 robots.thoughtbot.com/activerecords-wherenot
M
Matt Rogish

For Rails4:

So, what you're wanting is an inner join, so you really should just use the joins predicate:

  Foo.joins(:bar)

  Select * from Foo Inner Join Bars ...

But, for the record, if you want a "NOT NULL" condition simply use the not predicate:

Foo.includes(:bar).where.not(bars: {id: nil})

Select * from Foo Left Outer Join Bars on .. WHERE bars.id IS NOT NULL

Note that this syntax reports a deprecation (it talks about a string SQL snippet, but I guess the hash condition is changed to string in the parser?), so be sure to add the references to the end:

Foo.includes(:bar).where.not(bars: {id: nil}).references(:bar)

DEPRECATION WARNING: It looks like you are eager loading table(s) (one of: ....) that are referenced in a string SQL snippet. For example: Post.includes(:comments).where("comments.title = 'foo'") Currently, Active Record recognizes the table in the string, and knows to JOIN the comments table to the query, rather than loading comments in a separate query. However, doing this without writing a full-blown SQL parser is inherently flawed. Since we don't want to write an SQL parser, we are removing this functionality. From now on, you must explicitly tell Active Record when you are referencing a table from a string: Post.includes(:comments).where("comments.title = 'foo'").references(:comments)


The references call helped me!
T
Tilo

With Rails 4 it's easy:

 Foo.includes(:bar).where.not(bars: {id: nil})

See also: http://guides.rubyonrails.org/active_record_querying.html#not-conditions