Rails: will index help for range search? - ruby-on-rails

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)

Related

Rails 5: use .where with two attributes

I'm combining the two .where like this:
#questions = (FirstQuestion.where(user_id: current_user) + SecondQuestion.where(user_id: current_user)).sort_by(&:created_at).reverse
Both .where searches are with one attribute... the user_id. But now I want to search with two attributes, the user_id and this created_at >= ?", Date.today + 60.days. So basically I want to find the object with a user_id: current_user and the objects that where created less then or equal to 60 days.
Any idea on how to implement this?
Please see my comment as well... because it is kind of a code smell when you have models names FirstQuestion, SecondQuestion. There's really no reason for having separate models. You could probably easily model the logic via an attribute question_depth or something (I don't know what you are trying to achieve exactly).
With regard to your question: ActiveRecord is quite a nice class, that allows for very customizable queries. In your case, you could easily write both conditions each in a separate where, or create a single where. That's totally up to you:
Question.where(user: current_user).where('created_at <= ?', 60.days.from_now)
Or in a single where
Question.where('user_id = ? AND created_at <= ?', current_user.id, 60.days.from_now)
Also, consider using scopes on your Question model for readability and reusability:
class Question < AppModel
scope :by_user, -> (user) { where(user: user) }
scope :min_age, -> (date) { where('created_at <= ?', date) }
end
And use it like:
Question.by_user(current_user).min_age(60.days.from_now)

How to query a ActiveRecord Relation for created_by field

So i'm currently using the following command to join and query my tables - looking for an OrderItem amongst my Orders where the orderable_id = applicable_product_item_id the total_price = 0 and the buyer_id = current_user
Order.joins(:items)
.where(order_items: {id: OrderItem.where(orderable_id: applicable_product_item_id)})
.where(total_price: 0)
.where(buyer_id: current_user)
This all works fine, but now i want to query further and i want to know if the order that it has found has a created_at date > searchable_created_by_date
i've tried using another .where in the query as well as selecting the .first in the array and further querying that i.e. query = above_query.first
then
query.where("created_at > ?", searchable_created_by_date)
but i get
Undefined method where for #<Order:0x007fbc8d8edf90>
furman87's comment sounds right to me:
You'll have to specify the table in your where clause -- .where("orders.created_at > ?", searchable_created_by_date)
You might also try:
Order.
where(total_price: 0).
where(buyer_id: current_user).
where("created_at > ?", searchable_created_by_date).
joins(:order_items).
where(order_items: {id: OrderItem.where(orderable_id: applicable_product_item_id)})
I think putting the created_at statement before the joins statement will disambiguate the query - but I'm not 100% sure.
Also, I would have thought that you would have done joins(:order_items). But, I suppose that depends on how you have your associations set up. If joins(:items) works for you, then more power to you! (And ignore the comment.)

Rails: Query with join greater and less than date values?

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 ?

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