Rails 4: OR in where clause not working with querying nil - ruby-on-rails

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.

Related

Select from DB all records or by array if present

In RoR app I want to write a model method that will return some records.
It should select by ids if ids are present or all records if not
def viewing_duration(ids = nil)
if ids
tracks.where(video_id: ids).sum('age(updated_at, created_at)')
else
tracks.sum('age(updated_at, created_at)')
end
end
Question:
How I can write query in one line and pass the expression right to where method?
Something like this:
tracks.where(video_id: ids.nil? ? 'all' : ids).sum('age(updated_at, created_at)')
Keeping it as more lines probably makes it easier to understand. I suggest keeping it close to as it is while removing repetition:
def viewing_duration(ids = nil)
if ids
tracks.where(video_id: ids)
else
tracks
end.sum('age(updated_at, created_at)')
end
If you do want to pass something into where to find all records, you can use an sql statement that will always evaluate to true. For example:
tracks.where(ids.nil? ? '1=1' : { video_id: ids }).sum('age(updated_at, created_at)')
It is not one line, but as idea how to organize your code using chaining
def viewing_duration(ids = nil)
entities = tracks
entities = entities.where(video_id: ids) if ids
entities.sum('age(updated_at, created_at)')
end

Rails How do I find case insensitive values that are not already associated to the user?

Newbie Rails developer here so please bare with me.
I have a table called Ingredients where it contains a title field and an association to a User. A user can have many ingredients.
I want to query the database to get the ingredients that are not already available to a User.
I tried doing something like this with Rails:
#ingredients = current_user.ingredients
#community_ingredients = Ingredient.all.excluding(#ingredients).pluck(:title, :id)
But the problem is that this still returns values that are the same & only the case is different.
How can I achieve this outcome?
Try following queries.
#community_ingredients = Ingredient.includes(:user).where("users.user_id = ?", current_user.id).where(users: { id: nil } ).pluck(:title, :id)
OR
Ingredient.includes(:user).where("users.user_id = ?", current_user.id).where(ingredients: {user_id: nil } ).pluck(:title, :id)
OR
Ingredient.includes(:user).where("users.user_id = ?", current_user.id).where(users: { ingredient_id: nil } ).pluck(:title, :id)
Choose right query based on your association and feel free to suggest me so I can remove the extra one.
Most probably the first or second query will work, I strongly feel the third might not be the case.
Let's say this one is not working for you and you want to have solution based on your architecture.
#ingredients = current_user.ingredients.pluck(:title)
#community_ingredients = Ingredient.where.not("lower(title) IN (?)", #ingredients.map(&:downcase)).pluck(:title, :id)
So basically we need to convert both column value and the matching list in same case.
So we have converted to downcase.
here is how it looks in my local system, just make sure it's working that way.

Rails - Find with no associated records

I want to select one user and add to it associated records such as child for this example.
But only the child with a specific place_id.
This code works, but when the user doesn't have any child entry I got an error.
#user = User.includes(:child).find(params[:id],
:conditions => ["child.place_id = ?", #place_id])
Here is the error:
Couldn't find User with id=19 [WHERE (child.place_id = 0)]
Thanks !
Try where clause, since you are already using brute SQL. This will not produce error and will either fetch or set to nil:
#user = User.includes(:child).
where("users.id=? AND child.place_id = ?",
params[:id],#place_id).first
PS: Is it child.place_id or children.place_id? ActiveRecord tends to pluralize table names.
EDIT:
This only works if there are children. If you want it to work event without children,do this:
#user = User.joins('LEFT JOIN child on child.user_id = users.id').
where('child.place_id = ? AND users.id = ?', #place_id, params[:id]).
select('users.field1, child.field2 as field3')
If you want specific fields, add them in select method above, which is provided as an example.

rails how to find with no associated records

I know that this will be an easy one but I'm having real issues working it out.
I have users that can have_many results. I'm trying to work out how to return users that don't yet have any results to the #starters object(from the controller).
#users = #event.entires
#starters = #users.where("results = ?", 0)
could anyone explain how i would check if a user has no results?
Best solution (as MrYoshiji commented)
#starters = #users.includes(:results).where(results: { id: nil })
This will execute the same query as the one in my second solution.
Other SQL solution
You could use a LEFT OUTER JOIN. This way, you will always have all the results from the "left" table (users) but you will also have matching records for the "right" table (results) eventhough there are non, which will leave you with empty fields that you can check.
#starters = #users.joins("LEFT OUTER JOIN results ON results.user_id = users.id").where("results.user_id IS NULL")
In your case, replace users with the name of your "user" model.
Other Ruby solution
#starters = #users.select { |u| !!u.results }
Here !! will force conversion to a boolean, if there are no results, u.results will return [] (empty array). And !![] equals true.
Try left joining and finding which one is null
#users.joins("LEFT OUTER JOIN user ON user.id = result.user_id").where("result.id IS NULL")
If your #users is an Array, try:
#starters = #users.select { |u| u.results.empty? }
This should get all of the users that do not have any results:
#starters = ActiveRecord::Base.execute("select * from users where id not in (select user_id from results)")
Another way to do this would be the following:
User.where("id not in ?", "(select user_id from results)")

PostgreSQL, Rails + Heroku, Column must appear in "group by"

I'm getting this error when I deploy my app on Heroku:
Started GET "/collections/transect/search?utf8=%E2%9C%93&search%5Btagged_with%5D=village&commit=Search" for 98.201.59.6 at 2011-03-27 17:02:12 -0700
ActionView::Template::Error (PGError: ERROR: column "photos.custom_title" must appear in the GROUP BY clause or be used in an aggregate function
: SELECT "photos".* FROM "photos" INNER JOIN "taggings" ON "photos"."id" = "taggings"."photo_id" INNER JOIN "tags" ON "tags"."id" = "taggings"."tag_id" WHERE "tags"."name" IN ('village') AND ("photos".collection_id = 1) GROUP BY photos.id LIMIT 20 OFFSET 0):
17:
18: - #bodyclass = 'dark'
19: #search_view.photo_tiles
20: = render :partial => 'collections/photos/alt_tiles', :collection => #photos, :as => :photo
app/views/collections/search.html.haml:20:in `_app_views_collections_search_html_haml__2343730670144375006_16241280__2249843891577483539'
I saw these similar questions (1,2).
The problem is, nothing in this view is asking for the custom_title attribute, nor am I executing a query with a "group_by" clause.
Here's the partial that seems to trigger the error:
- ((photo_counter+1) % 5 == 0) ? #class = 'last' : #class = ''
.photo{ :class => #class }
.alt_tile
= link_to( image_tag(photo.file.url(:tile)), collection_photo_path(#collection,photo), :class => 'img_container' )
.location= photo.location(:min)
.tags= photo.tag_array.join(' | ')
Here's the collections#search action which is what raised the error:
def search
#curator_toolbar = true
#collection = Collection.find(params[:id])
#search = #collection.photos.search(params[:search])
#photos = #search.page(params[:page]).per(20)
end
So it looks like maybe this is a plugin issue? I'm using MetaSearch for search functionality and Kaminari for pagination. Does anyone have any ideas or suggestions as to what would cause this specifically and how I can possibly fix it?
--EDIT--
Ok, I seem to have found the real problem:
Using MetaSearch with my keyword tags model, I created a search method that looks like this:
def self.tagged_with( string )
array = string.split(',').map{ |s| s.lstrip }
joins(:tags).where('tags.name' => array ).group('photos.id')
end
Now, I was given a lot of help in creating this method -- as I mentioned before I'm a total SQL moron.
This method works on SQLite but not on PostgreSQL because whenever keywords are included in a search it triggers the "group_by" problem.
So, in this question it seems to indicate that I need to put every column that is part of my photo model in the "group" argument or Postgre will break.
That horrifies me for several reasons:
My photo model is pretty complex and has a ton of fields.
My app is still in development and the photo model changes more than any other.
I don't want to have my code breaking every time someone touches the photo model in the future if they forget to add the columns to the group statement on the tag searching argument.
So, can anyone help me understand how to rewrite this method so that it won't break PostgreSQL -- and ideally so that I won't have to include a list of all the fields that belong to this model in the solution, or at least not a manually maintained list?
So, it turns out I could solve this problem by replacing "group" with "select" in my tagged_with method.
def self.tagged_with( string )
array = string.split(',').map{ |s| s.lstrip }
select('distinct photos.*').joins(:tags).where('tags.name' => array )
end
Problem solved! See this article for a great explanation as to why this is a better idea anyway. (Sorry, web site was removed later on and I don't recall what it said.) Also, thanks to Mark Westling for his answer on a spinoff question that solved my problem.

Resources