Rails ActiveRecord Find with Date - sql

I'm having a hard time figuring this out but how do I tell my finder statement to ignore the time of the Datetime field in the db?
def trips_leaving_in_two_weeks
Trip.find(:all, :conditions => ["depart_date = ?", 2.weeks.from_now.to_date])
end
I want depart_date to come back as just a date but it keeps returning the time as well and causing this equality not to work. Is there someway to just compare against the dates? Thanks
Edit
Here's the code I'm using now that works:
Trip.find(:all, :conditions => ["DATE(depart_date) = ?", 2.weeks.from_now.to_date])

Not sure which DB you're using but does this work?
"depart_date = DATE(?)"

I would use this approach:
Rails 3.x
Trip.where(
:depart_date => 2.weeks.from_now.beginning_of_day..2.weeks.from_now.end_of_day
)
Rails 2.x
Trip.all(
:conditions => {
:depart_date => 2.weeks.from_now.beginning_of_day..2.weeks.from_now.end_of_day
})
If you index the depart_date column this solution will be efficient as the query uses the index. This solution is DB neutral.
When calculated fields are used in a where clause, the performance degrades(unless there is a special index).

Related

Ruby Rails Query In conditional

I have this query that works but I would like to expand it so that I can check for multiple ids such that I pass in a vector of ids. [1,2,3,5] etc... I have tried using SQL IN with no luck.
EventType.find(3).events.all(:include => {:sheet => :rink}, :conditions => ["rinks.id = ?", 2])
You were on the right track with IN. Here's syntax that will work in Rails 3+:
EventType.find(3).events.where("id IN (?)", [1,2,3]).include(:sheet => :rink)
Improvement from a comment removes SQL entirely:
EventType.find(3).events.where(:id => [1,2,3]).include(:sheet => :rink)

Self join in ruby on rails

I am having trouble with a self join statement in Ruby on Rails.
Well, it is kind of complicated and irritating but what I want to get is:
5 jobs out of the JobCompact table that has the same foreign key build_compact_id and either the language "ruby" or "rubinius"
My query looks as follows:
JobCompact.all(
:joins => "JOIN job_compacts AS jobs2 ON job_compacts.build_compact_id = jobs2.build_compact_id",
:conditions => ["job_compacts.language = ? AND jobs2.language=?", 'ruby', 'rubinius'],
:limit => 5)
In general that seems to be working but only return me objects out of the first table. The second table (jobs2) is not shown in the result set. I would like to get a collection with all job_compacts that match the given condition.
I hope I could explain my problem, otherwise do not hesitate to ask me. Thanks in advance!

Rails ActiveRecord query

My question is twofold... Primarily, I am trying to figure out how to ask > or < when filtering this query. You can see at the end I have .where(:created_at > 2.months.ago) and that is improper syntax, but I'm not sure the correct way to call something similar.
Secondly, this is a bit of a long string and is going to get longer as the are more conditions I have to factor in. Is there a cleaner way of building this, or is a long string of conditions like this pretty standard?
class PhotosController < ApplicationController
def showcase
#photos = Photo.order(params[:sort] || 'random()').search(params[:search]).paginate(:per_page => 12, :page => params[:page]).where(:created_at > 2.months.ago)
end
Thanks.
Unfortunately you've hit a sore point in the ActiveRecord querying api. There is no standard, out of the box way to do this. You can do date ranges very easily, but < and > have no easy path. However Arel, the underlying SQL engine, can do this very easily. You could write a simple scope to handle it thusly:
scope :created_after, lambda {|date| where arel_table[:created_at].gt(date) }
And you could refactor this easily to take a column, or gt versus lt, etc.
Other people have solved this problem already, however, and you could take advantage of their work. One example is MetaWhere, which adds a bunch of syntactic sugar to your queries. For example, using it you might write:
Article.where(:title.matches => 'Hello%', :created_at.gt => 3.days.ago)
On #2, scopes do tend to get long. You might look into the gem has_scope, which helps to alleviate this by defining scopes on the controller in an analogous way to how they are defined on the model. An example from the site:
# The model
# Note it's using old Rails 2 named_scope, but Rails 3 scope works just as well.
class Graduation < ActiveRecord::Base
named_scope :featured, :conditions => { :featured => true }
named_scope :by_degree, proc {|degree| { :conditions => { :degree => degree } } }
end
# The controller
class GraduationsController < ApplicationController
has_scope :featured, :type => :boolean
has_scope :by_degree
def index
#graduations = apply_scopes(Graduation).all
end
end
You can do where(["created_at > ?", 2.months.ago]) for your first question.
For your second question there are several solutions :
You can use scopes to embed the conditions in them and then combine them.
You can break the line in multiple lines.
You can keep it like this if you have a large screen and you don't work with any other people.

How best to work around PostgreSQL's stricter grouping

I am trying to convert from using MySQL to using PostgreSQL. I have this type of structure:
User(entity) -> Follow -> Business(entity) -> Story
The user needs to see all the news updates put out by the businesses they follow. The following query works great with MySQL because it simply shows all associated stories and groups by the story.id. Unfortunately, being that PostgreSQL is much more literal in the interpretation of the SQL standard, if I want to do the GROUP BY clause I need to ask for each field individually using the DISTINCT clause which.
Story.find(:all, :joins => { :entity => { :followers => :follower } }, :conditions => ['followers_follows.id = ?', 4],
:group => 'stories.id')
PostgreSQL spits out: "ERROR: column "stories.entity_id" must appear in the GROUP BY clause or be used in an aggregate function"
Having to specify each field individually seems inelegant. If anybody can give me a clean way to get the same result as MySQL without having to resort to getting duplicate fields (removing the group by) or having to specify each individual field with along with the DISTINCT clause, I'd appreciate it!
Thanks!
Well, I certainly wouldn't say that PostgreSQL's interpretation of the SQL standard is too strict. In fact, it's the other way around.
Here's a possible solution:
Story.all( :joins => { :entity => { :followers=> :follower } },
:conditions => ['followers_follows.id = ?', 4],
:group => Story.column_names.map { |c| "stories.#{c}" }.join(', ') )
But there are many alternative queries. I blogged about this a few weeks ago. Here's the post: http://awesomeful.net/posts/72-postgresql-s-group-by

My Rails queries are starting to get complicated, should I switch to raw SQL queries? What do you do?

My Rails app is starting to need complicated queries. Should I just start using raw SQL queries? What is the trend in the Rails community?
Update:
I do not have written queries right now, I wanted to ask this question before I start. But here is an example of what I want to do:
I have books which have categories. I want to say-
Give me all books that were:
-created_at (added to store) between date1 and date2
-updated_at before date3
-joined with books that exist in shopping carts right now
I haven't written the query yet but I think the rails version will be something like this:
books_to_consider = Book.find(:all,
:conditions => "created_at <= '#{date2}' AND created_at >= '#{date1}' AND updated_at <= '#{date3}'",
:joins => "as b inner join carts as c on c.book_id = b.id")
I am not saying ActiveRecord can't handle this query, but is it more accepted to go with raw SQL for readability (or maybe there are other limitations I don't know of yet)?
The general idea is to stick to ActiveRecord-generated queries as much as possible, and use SQL fragments only where necessary. SQL fragments are explicitly supported because the creators of ActiveRecord realised that SQL cannot be completely abstracted away.
Using the the find method without SQL fragments is generally rewarded with better maintainability. Given your example, try:
Book.find(:all,
:conditions => ["created_at >= ? AND created_at <= ? AND updated_at <= ?",
date1, date2, date3]
:include => :carts)
The :inlude => :carts will do the join if you added has_many :carts to your Book model. As you can see, there does not have to be much SQL involved. Even the quoting and escaping of input can be left to Rails, while still using SQL literals to handle the >= and <= operators.
Going a little bit further, you can make it even clearer:
class Book < AciveRecord::Base
# Somewhere in your Book model:
named_scope :created_between, lambda { |start_date, end_date|
{ :conditions => { :created_at => start_date..end_date } }
}
named_scope :updated_before, lambda { |date|
{ :conditions => ["updated_at <= ?", date] }
}
# ...
end
Book.created_between(date1, date2).updated_before(date3).find(:all,
:include => :carts)
Update: the point of the named_scopes is, of course, to reuse the conditions. It's up to you to decide whether or not it makes sense to put a set of conditions in a named scope or not.
Like molf is saying with :include, .find() has the advantage of eager loading of children.
Also, there are several plugins, like pagination, that will wrap the find function. You'll have to use .find() to use the plugins.
If you have a really complex sql query remember that .find() uses your exact parameter string. You can always inject your own sql code:
:conditions => ["id in union (select * from table...
And don't forget there are a lot of optional parameters for .find()
:conditions - An SQL fragment like "administrator = 1", [ "user_name = ?", username ], or ["user_name = :user_name", { :user_name => user_name }]. See conditions in the intro.
:order - An SQL fragment like "created_at DESC, name".
:group - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
:having - Combined with +:group+ this can be used to filter the records that a GROUP BY returns. Uses the HAVING SQL-clause.
:limit - An integer determining the limit on the number of rows that should be returned.
:offset - An integer determining the offset from where the rows should be fetched. So at 5, it would skip rows 0 through 4.
:joins - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed), named associations in the same form used for the :include option, which will perform an INNER JOIN on the associated table(s), or an array containing a mixture of both strings and named associations. If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table‘s columns. Pass :readonly => false to override.
:include - Names associations that should be loaded alongside. The symbols named refer to already defined associations. See eager loading under Associations.
:select - By default, this is "*" as in "SELECT * FROM", but can be changed if you, for example, want to do a join but not include the joined columns. Takes a string with the SELECT SQL fragment (e.g. "id, name").
:from - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name of a database view).
:readonly - Mark the returned records read-only so they cannot be saved or updated.
:lock - An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE". :lock => true gives connection‘s default exclusive lock, usually "FOR UPDATE".
src: http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M002553