Rails: Query with join greater and less than date values? - ruby-on-rails

I have 2 tables, a PlayingField table and Bookings table, and I want to write a rails query that takes in query parameters Start_date, End_date and Number_of_players
Scenario:
I pass in Start_date, End_date and Number_of_players to a method and I want to Get all PlayingFields that are available (not booked) between the dates provided and that the field can accommodate the number of players wanted.
PlayingField has_many bookings
Booking belongs_to PlayingField
pseudo code:
#Fields = PlayingFields.where(bookings.end_date > start_date AND bookings.start_date > end_date AND PlayingFields.capacity >= number_of_players)
Can someone please tell me what the best way to do this is on the rails side of things?
How can I query the database through rails to get all available PlayingFields between them dates and for the number of players required?
Thanks

The rails would have to includ the bookings table before the SQL call.
PlayingFields.joins(:bookings).where(bookings.end_date > start_date AND bookings.start_date > end_date AND bookings.capacity >= number_of_players)
I'm also curious how those two objects programmatically relate. How does a Booking model become associated with a PlayingField ?

Related

Query records which a have multiple conditions and records on join table

In a rails app which have a people table and an availabilities table:
Person.rb
has_many :availabilities
Availability.rb
belongs_to :person
People columns are irrelevant here. Availabilities have three particulars columns I use to find available people:
day: integer #0-6 0 is sunday
start_time: time
end_time: time
Actually there's a search engine that give the ability to do queries like this:
people = people.joins("INNER JOIN availabilities ON availabilities.person_id = people.id")
people = people.where("availabilities.day = ?", params[:day])
....
So, for instance, we can search for some people available on sunday from 8am to 11am.
I would like to implement a multi-day search. The search engine would display a multi select field and return an array of integer we can use:
params[:day] = ["1","2","3"]
A typical query would be: who are the people that have availabilities monday, friday and sunday from 8am to 12 pm. And we expect a list of people which have all those three day of availabilities,
My question is how to build the query? Is there an active record way or should I use plain SQL? The database is Postgres.
I can't use IN with HAVING/COUNT because a person can have multiple availabilities the same day.
My first guest is to try a generic union/intersect query builder but maybe I missing something easier.
I already search but maybe I missed something, any info is welcome!
ps: If you have a better idea for the title of my question, I struggle to find one.
You're already using a SQL fragment:
people = people.where("availabilities.day = ?", params[:day])
What you're looking for is the SQL IN operator, then you can pass in an Array:
people = people.where("availabilities.day IN ?", params[:day])
I'd put all of that in a scope on Availability with .includes to do the join:
def self.find_people(days)
# attach a list of people to each day
includes(:people).where("day IN ?", days)
end

Rails scope filter by date range

There are many questions relate to rails date range problem but mine is a little more complicated.
I have two models: house and booking. A House has_many bookings. A Booking has two attributes in date format: check_in and check_out.
What I want to achieve: Giving a valid date range, show all houses that are available during this range. In detail:
The start date of the range should not be in any booking.
The end date of the range should not be in any booking.
There should not be any booking between the start and the end.
Can this be done using the rails scope?
UPDATE:
I found the code below that can check scope date interval that overlaps.
named_scope :overlapping, lambda { |interval| {
:conditions => ["id <> ? AND (DATEDIFF(start_date, ?) * DATEDIFF(?, end_date)) >= 0", interval.id, interval.end_date, interval.start_date]
}}
How can I transfer this to my problem?
scope :overlapping, (lambda do |start_date, end_date|
House.includes(:bookings).where("bookings.check_in < ? AND bookings.check_out > ?",
start_date, end_date).references(:bookings).uniq
end)
I went ahead and deleted the >= and <= operators in favor of > and < to explicitly show these bookings being outside of the given range, but you can adjust them per your needs!
Update
Changed query to use #includes instead of #joins, since we're querying the attached table.
Yes it is possible to have this query through scope. Put this scope in house model.
scope :overlapping, -> (start_date, end_date) {
includes(:bookings).where('bookings.check_in < ? AND bookings.check_out > ?',
start_date.to_date, end_date.to_date)
}
And call as House.overlapping('2015-07-01', '2015-07-09')

Rails: will index help for range search?

In my Rails App, I did a alot of range search to group objects, like
scope :best_of_the_week, ->(time) do
start_time = time.beginning_of_week
end_time = time.end_of_week
where("created_at > ? AND created_at < ?", start_time, end_time).where('votes_count > ?', 300).order('votes_count DESC').first(8)
end
In this case, do I need to add index to created_at? and what about votes_count?
Addtionally, how can I elegantly combine the first two where searches? Or does combining them make any difference?
If you want max performance to this query, create an index for both. If you don't want to create too many indexes, you should index created_at, date seems do have a bigger range as the time goes (and size of database).
I like to use the find_by_sql and make SELECT retrieve just the essential data to improve performance, if you have too many var chars fields this will have a nice impact.
Just for sintax sugar
where("between ? and ?", start_time, end_time).(other stuff)

How to count "days with records" in Rails?

Rails adds and populates a created_at column for new records.
How can I use the to count the number of days that have records within a specified timeframe? (note: counting days, not counting records).
For example, say I have a Post model, how can I calculate how many days in the last year have a Post?
Since you asked for the ruby way, here it is:
Post.where('created_at >= ?', 1.year.ago).map { |p| p.created_at.beginning_of_day }.uniq.size
Update
You can put the following in your Post model
def self.number_of_days
where('created_at >= ?', 1.year.ago).map { |p| p.created_at.beginning_of_day }.uniq.size
end
Then in your controller you can do stuff like
#user.posts.number_of_days
Here's a more efficient way that delegates most of the work to the database (MySQL, not sure if it'll work on others):
Post.where('created_at >= ?', 1.year.ago).group('DATE(created_at)').length

Rails 3. How to perform a "where" query by a virtual attribute?

I have two models: ScheduledCourse and ScheduledSession.
scheduled_course has_many scheduled_sessions
scheduled_session belongs_to scheduled_course
ScheduledCourse has a virtual attribute...
def start_at
s = ScheduledSession.where("scheduled_course_id = ?", self.id).order("happening_at ASC").limit(1)
s[0].happening_at
end
... the start_at virtual attribute checks all the ScheduledSessions that belongs to the ScheduledCourse and it picks the earliest one. So start_at is the date when the first session happens.
Now I need to write in the controller so get only the records that start today and go into the future. Also I need to write another query that gets only past courses.
I can't do the following because start_at is a virtual attribute
#scheduled_courses = ScheduledCourse.where('start_at >= ?', Date.today).page(params[:page])
#scheduled_courses = ScheduledCourse.where('start_at <= ?', Date.today)
SQLite3::SQLException: no such column: start_at: SELECT "scheduled_courses".* FROM "scheduled_courses" WHERE (start_at >= '2012-03-13') LIMIT 25 OFFSET 0
You can't perform SQL queries on columns that aren't in the database. You should consider making this a real database column if you intend to do queries on it instead of a fake column; but if you want to select items from this collection, you can still do so. You just have to do it in Ruby.
ScheduledCourse.page(params).find_all {|s| s.start_at >= Date.today}
Veraticus is right; You cannot use virtual attributes in queries.
However, I think you could just do:
ScheduledCourse.joins(:scheduled_sessions).where('scheduled_courses.happening_at >= ?', Date.today)
It will join the tables together by matching ids, and then you can look at the 'happening_at' column, which is what your 'start_at' attribute really is.
Disclaimer: Untested, but should work.
I wonder if this would be solved by a subquery ( the subquery being to find the earliest date first). If so, perhaps the solution here might help point in a useful direction...

Resources