Sql is null in ActiveRecord query - ruby-on-rails

I have class method to get posts but now sure how to resolve it.
So, somebody can set flag on post, but in meantime post doesn't have any flag :
Post.find(3).flag #nil
This is self method for getting posts :
def self.for_review
joins(:flag).where('need_approval = ? and question = ? and DATE(posts.created_at) = ?', "true", "false", Time.now.to_date)
.where('flags.need_check = ? or flags IS NULL', 'false')
end
Problem is with second where condition
where('flags.need_check = ? or flag IS NULL', 'false')
Because flag is NULL doesn't work .
Is anybody knows what's problem ?
Thanks

joins uses an INNER JOIN by default; this will only return posts that do have at least one flag. You need a LEFT JOIN. Assuming a Post has_many flags:
joins('LEFT JOIN flags ON posts.id = flags.post_id').where(...).where('flags.need_check = ? OR flags.id IS NULL', false)
Also drop the quotes around true and false, unless those fields are indeed strings with the literal values "true" and "false".

THe problem is joins(:flag). It defaults to inner join, so all the posts without flags will be rejected. You need includes(:flag).references(:flag) instead.
Also flags IS NULL makes no sense as there is no column called flags. Instead do flags.id IS NULL

Related

Rewhere or unscope a query containing an array condition on Rails

I'm trying to rewhere or unscope a query, where the original condition cannot be written using hash condition:
Reservation.where('block_id IS NULL OR block_id != ?', 'something')
> SELECT `reservations`.* FROM `reservations` WHERE (block_id IS NULL OR block_id != 'something')
Trying to rewhere doesn't work:
Reservation.where('block_id IS NULL OR block_id != ?', 'something').rewhere(block_id: 'anything')
> SELECT `reservations`.* FROM `reservations` WHERE (block_id IS NULL OR block_id != 'something') AND `reservations`.`block_id` = 'anything'
But this example with hash condition would work:
Reservation.where.not(block_id: 'something').rewhere(block_id: 'anything')
> SELECT `reservations`.* FROM `reservations` WHERE `reservations`.`block_id` = 'anything'
I understand that this is probably because on the array condition rails doesn't know which column I'm invoking a where, and therefore rewhere won't find anything to replace.
Is there any way to explicitly tell which column I'm filtering in an array condition? or rewrite the first query (IS NULL OR != value) with hash condition?
Note: Please don't suggest unscoped, as I'm trying to unscope/rewhere only this specific condition, not the whole query.
Thanks!
Sorry it wasn't clear that you had other where clauses that you wanted to keep. You could access the array of where clauses using relations.values[:where] and manipulate it, something like:
Reservation.where('block_id IS NULL OR block_id != ?', 'something')
.tap do |relation|
# Depending on your version of Rails you can do
where_values = relation.where_values
# Or
where_values = relation.values[:where]
# With the first probably being better
where_values.delete_if { |where| ... }
end
.where(block_id: 'anything')
aka hacking

Rails 4: OR in where clause not working with querying nil

I am trying to query any Company in my database where the field visible is either NULL or true. Here is what I found on a Stackoverflow post:
#companies = Company.where('visible=? OR visible=?', nil, true).page(params[:page]).per(10)
Somehow though, this does not seem to work for querying nil. When I use this code, displaying all companies where visible is nil works very well though.
#companies = Company.where('visible' => nil).page(params[:page]).per(10)
I would very much appreciate any ideas here.
Thanks!
EDIT:
This still displays only companies where visible is nil:
#companies = Company.where('visible is ? OR visible=?', nil, true).page(params[:page]).per(10)
That's because
Company.where('visible=?', nil) makes query:
Company Load (0.3ms) SELECT companies.* FROM companies WHERE
(visible = NULL)
In SQL, to compare with NULL, = doesn't work. It requires IS instead.
Company.where('visible is ?', nil) should do the trick for you. Add or statement along with it.
Company Load (0.3ms) SELECT companies.* FROM companies WHERE
(visible is NULL)
OR, the perfect way:
Company.where(:visible => [true, nil])
The problem is that you have no NOT NULL constraint in your DB.
You can also use conditional expressions(CASE) or function COALESCE.

Confusing result when using where.not with Rails

I've got an orders model with a payment status string field. Ordinarily this field should be populated but sometimes it's nil. I have the following scope
scope :not_pending, -> { where.not(payment_status: 'pending') }
In one of my tests, the payment_status is nil for an order but I still get an empty result for the scope. If I change it to remove the .not, I get an empty result again so I'm a bit confused.
EDIT - Added SQL
"SELECT "orders".* FROM "orders" INNER JOIN "order_compilations" ON "orders"."id" = "order_compilations"."order_id" WHERE "order_compilations"."menu_group_id" = 3724 AND ("orders"."payment_status" != 'pending')"
Just add nil along, then it will check both
scope :not_pending, -> { where.not(payment_status: 'pending').or(payment_status: nil) }
which is equivalent to where("payment_status <> 'pending' OR payment_status IS NULL")
UPDATE
changed to include nil
This is not because of Rails but because it's how SQL actually works.
!= means something different, but still something. NULL is not something.
You can see this related issue.
In SQL, a NULL value in an expression will always evaluate the expression to false. The theory is that a NULL value is unknown, not just an empty value that you could evaluate to 0, or false.
So for example, 1 = NULL is false, but 1 != NULL is false too.
So in your case, I would probably write:
where("payment_status != 'pending' or payment_status is null")

Does not equal conditional

I want to have a where clause with an equal and does not equal condition:
#user = User.where(:user_id => current_user.id, :author_id != current_user.id).nil? ? (render :something) : (render :somethingelse)
The above does not work:
syntax error, unexpected ')',
expecting tASSOC ...d, :user_id !=
current_user.id).nil? ? (render
:index) : (re...
If I change the second condition from != to => it will work, however.
How do I have both conditions in one where clase? Thank you
Here's how you would use Arel to generate the query "select * from users where user_id = ? and author_id != ?":
users = User.arel_table
User.where(users[:user_id]. eq(current_user.id).and(
users[:author_id].not_eq(current_user.id)))
Using Arel isn't as concise as using Hash conditions for simple conditions, but it's a lot more powerful!
Here's a link to the full list of predications (eq, not_eq, gt, lt, etc.) available with Arel.
I believe, it should be:
#user = User.where(['user_id = ? AND author_id <> ?', current_user.id, current_user.id])
render(#user ? :something : :somethingelse)
Rails 4 has this all figured out
Model.where.not(:colname => nil)
#=> returns all records whose :colname values are not nil
The syntax error is due to you attempting to use != instead of =>. The where method does not support inequality with hashed arguments, so your not equal will need to be written using array arguments.
User.where(:user_id => current_user.id).where(['users.author_id <> ?', current_user.id])
http://guides.rubyonrails.org/active_record_querying.html#hash-conditions
Only equality, range and subset checking are possible with Hash conditions.
You'll need to either drop down to straight SQL or invert and arel query, see Is there a way to invert an ActiveRecord::Relation query?
Not sure if you're aware, the not equal condition typically does not match (author_id) NULL values. You'll have to do an OR author_id IS NULL if you want that.
#users = User.where("user_id = ? AND (author_id != ? OR author_id IS NULL)",
current_user.id, current_user.id)
render(#users.present? ? :something : :somethingelse)
Also note that I'm using #users.present? because where finder returns an ActiveRecord::Relation array.

What is the proper syntax for `!current_user`

I am writing this statement here :
where :commentable_id => comments.map(&:id), :user_id => !current_user
The problem is it's returning only a user with the id of 0. Assuming this is a non-user, or more specifically a !user.
In this case, I am just trying to say anybody but the current user.
How would you write that?
Using "not #{current_user.id} returns :
SELECT "comments".* FROM "comments" WHERE ("comments"."user_id" = 0)
Right, I've written this in the other answer I gave.
The problem is that hashes in conditions are only good for making == or IN comparisons in SQL. You cannot do bigger or smaller comparisons or not-equals. You have to write a snippet of SQL yourself.
where(:commentable_id => comments.map(&:id)).where("user_id != ?", current_user.id)
You approach doesn't work, because it will send the results of !current_user to the SQL generator (which is the ARel gem). According to Ruby logic, any object that isn't nil or false is considered to be true. When you place a "bang" (exclamation mark) before it, it will make if false. And ARel will try to convert false in to whatever database understands as false. Most databases don't understand booleans and they use different other methods for that. SQLite uses 't' and 'f', and MySQL uses 1 and 0 (I believe).
where :commentable_id => comments.map(&:id), :user_id => "<> #{current_user.id}"
This will do it.

Resources