find records using Rails 3 where and 'not equal' - ruby-on-rails-3

I have the following find which does not work
self.participants.where(:role => "Celebrant", :created_at => year..Time.now, :board_id => !current_board.id)
What I want to to find the participants that satisfy the above and
:board_id not equal to current_board.id
How can I do this with rails 3 where?

You'd want something like this:
self.participants.
where(:role => "Celebrant", :created_at => year..Time.now).
where('board_id <> ?', current_board.id)
You have to drop down to string conditions and SQL snippets for "not equals" as Hash conditions are a bit limited:
Only equality, range and subset checking are possible with Hash conditions.

Related

SQL problems when migrating from MySQL to PostgreSQL

I have a Ruby on Rails 2.3.x application that I'm trying to migrate from my own VPS to Heroku, including porting from SQLite (development) and MySQL (production) to Postgres.
This is a typical Rails call I'm using:
spots = Spot.paginate(:all, :include => [:thing, :user, :store, {:thing => :tags}, {:thing => :brand}], :group => :thing_id, :order => order, :conditions => conditions, :page => page, :per_page => per_page)
Question 1: I get a lot of errors like PG::Error: ERROR: column "spots.id" must appear in the GROUP BY clause or be used in an aggregate function. SQLite/MySQL was evidently more forgiving here. Of course I can easily fix these by adding the specified fields to my :group parameter, but I feel I'm messing up my code. Is there a better way?
Question 2: If I throw in all the GROUP BY columns that Postgres is missing I end up with the following statement (only :group has changed):
spots = Spot.paginate(:all, :include => [:thing, :user, :store, {:thing => :tags}, {:thing => :brand}], :group => 'thing_id,things.id,users.id,spots.id', :order => order, :conditions => conditions, :page => page, :per_page => per_page)
This in turn produces the following SQL code:
SELECT * FROM (SELECT DISTINCT ON ("spots".id) "spots".id, spots.created_at AS alias_0 FROM "spots"
LEFT OUTER JOIN "things" ON "things".id = "spots".thing_id
WHERE (spots.recommended_to_user_id = 1 OR spots.user_id IN (1) OR things.is_featured = 't')
GROUP BY thing_id,things.id,users.id,spots.id) AS id_list
ORDER BY id_list.alias_0 DESC LIMIT 16 OFFSET 0;
...which produces the error PG::Error: ERROR: missing FROM-clause entry for table "users". How can I solve this?
Question 1:
...Is there a better way?
Yes. Since PostgreSQL 9.1 the primary key of a table logically covers all columns of a table in the GROUP BY clause. I quote the release notes for version 9.1:
Allow non-GROUP BY columns in the query target list when the primary
key is specified in the GROUP BY clause (Peter Eisentraut)
Question 2:
The following statement ... produces the error
PG::Error: ERROR: missing FROM-clause entry for table "users"
How can I solve this?
First (as always!), I formatted your query to make it easier to understand. The culprit has bold emphasis:
SELECT *
FROM (
SELECT DISTINCT ON (spots.id)
spots.id, spots.created_at AS alias_0
FROM spots
LEFT JOIN things ON things.id = spots.thing_id
WHERE (spots.recommended_to_user_id = 1 OR
spots.user_id IN (1) OR
things.is_featured = 't')
GROUP BY thing_id, things.id, users.id, spots.id
) id_list
ORDER BY id_list.alias_0 DESC
LIMIT 16
OFFSET 0;
It's all obvious now, right?
Well, not all of it. There is a lot more. DISTINCT ON and GROUP BY in the same query for one, which has its uses, but not here. Radically simplify to:
SELECT s.id, s.created_at AS alias_0
FROM spots s
WHERE s.recommended_to_user_id = 1 OR
s.user_id = 1 OR
EXISTS (
SELECT 1 FROM things t
WHERE t.id = s.thing_id
AND t.is_featured = 't')
ORDER BY s.created_at DESC
LIMIT 16;
The EXISTS semi-join avoids the later need to GROUP BY a priori. This should be much faster (besides being correct) - if my assumptions about the missing table definitions hold.
Going the "pure SQL" route opened up a can of worms for me, so I tried keeping the will_paginate gem and tweak the Spot.paginate parameters instead. The :joins parameter turned out to be very helpful.
This is currently working for me:
spots = Spot.paginate(:all, :include => [:thing, {:thing => :tags}, {:thing => :brand}], :joins => [:user, :store, :thing], :group => 'thing_id,things.id,users.id,spots.id', :order => order, :conditions => conditions, :page => page, :per_page => per_page)

RoR table join with "where" needed on both tables

I'm having trouble wrapping my head around joining tables. I have a one_to_many relationship between Locations and Listings. Locations have a longitude and latitude, as well as it's primary key: zipcode. Listings have a title and zipcode.
Listing.where("title LIKE ?", "%#{params[:search2]}%")
Location.where(:lat => #min_lat..#max_lat, :lon => #min_lon..#max_lon)
I'd basically Like to combine these two statements so that I can get all Listings within a given range of a zipcode, which is determnied by using a range of longitudes and latitudes. I can't figure out how to do this in Ruby on Rails in a fashion where I don't have to do separate SQL finds and loop through the data in my code to find the correct data.
Edit Updated code:
#results=Location.joins(:listings).where(:listings => ["title LIKE ?", "%#{params[:search2]}%"], :locations => {:zipcode => params[:searchZip]})
SQL output:SELECT "locations".* FROM "locations" INNER JOIN "listings" ON "listings"."location_id" = "locations"."zipcode" WHERE ("locations"."listings" IN ('title LIKE ?', '%fish%')) AND ("locations"."zipcode" = 44012)
I don't know why it is doing "locations"."listings" (which gives an error), or where the IN is coming from.
I suggest using ARel as opposed to writing your own SQL:
Listing.joins(:location).
where(:listings => {:lat => #min_lat..#max_lat, :lon => #min_lon..#max_lon},
:location => ["title LIKE ?", "%#{params[:search2]}%"])
If I am understanding correctly that Location has_many :listings, then this query will return all of the listings in the location range with matching titles:
Listing.joins(:location).where(
'listings.title LIKE ? AND
locations.lat BETWEEN ? AND ? AND
locations.lon BETWEEN ? AND ?',
"%#{params[:search2]}%", #min_lat, #max_lat, #min_lon, #max_lon)
Location.joins(:listings).where(:listings => {:lat => #min_lat..#max_lat, :lon => #min_lon..#max_lon} )

How to define new instantaneous variable row by row - RAILS3 BEGINNER

I was hoping somebody may be able to point me in the right direction...
I have a database called Info and use a find command to select the rows in this database which match a certain criteria
#matching = Info.find( :all, :conditions => ["product_name = ?", distinctproduct], :order => 'Price ASC')
I then pull out the cheapest of these items
#cheapest = #matching.first
Finally, I would like to create an instantaneous array which contains a list of #cheapest for a number of different search criteria. i.e. row 1 in #allcheapest is #cheapest for criteria 1, row 2 in #allcheapest is #cheapest for criteria 2, ...
Any help would be great, thanks in advance
Info.where(:product_name => distinct_product.to_s).order('Price ASC').first
to select the cheapest price for the product_name. Without more insight into how your database is structured, it is difficult to suggest how to obtain the latter, but you may try
Info.where(:product_name => distinct_product.to_s).order('Price ASC').group(:product_name)

rails group by multiple columns

i have budgets table with emptype_id and calendar_id actual_head, estimated_head
when i do Budgets.sum(:actual_head ,:group=>"emptype_id,calendar_id") i do not get the result grouped by the above two columns but only by the emptype_id
however when i check the log the sql query is right
SELECT sum(`budgets`.actual_head) AS sum_actual_head, emptype_id,calendar_id AS emptype_id_calendar_id FROM `budgets` GROUP BY emptype_id,calendar_id
has 103 rows
I wanted to iterate through each emptype_id and calendar_id to get a sum of actual_head
and do some calculations on it.
Grouping with multiple columns cannot be supported by rails. You have to use a regular find all:
budgets = Budgets.find(:all,
:select => "emptype_id, calendar_id, sum(budgets.actual_head) AS sum_actual_head",
:group => "emptype_id, calendar_id")
budgets.each { |budget| puts budget.sum_actual_head }
I cheat. Do :group => ["emptype_id,calendar_id"].
Not want you nor I want, but this works at least.
I'm not sure of this, buy try :group => [:emptype_id, :calendar_id]

Rails combined ('AND') searches on associated join tables

I cant get rails to return combined ('AND') searches on associated join tables of an Object.
E.g. I have Books that are in Categories. Lets say: Book 1: is in category 5 and 8
But I can't get 'AND' to filter results using the join table? E.g ::->
Class Books
has_and_belongs_to_many :categories, :join_table => "book_categories"
Book.find :all, :conditions => "book_categories.category_id = 5 AND book_categories.category_id = 8", :include => "categories"
... returns nil
(why does it not return all books that are in both 5 & 8 ??)
However: 'OR' does work:
Book.find :all, :conditions => "book_categories.category_id = 5 OR book_categories.category_id = 8"
... returns all books in category 5 and 8
I must be missing something?
The problem is at the SQL level. That condition runs on a link table row, and any individual link table row can never have a category_id of both 5 and 8. You really want separate link table rows to have these IDs.
Try looking into Rails' named_scope, specifically the part that allows filtering with a lambda (so you can take an argument). I've never tried it out myself, but if I had to implement what you're looking for, that's what I'd look in to.