Rails: Query with association? - ruby-on-rails

I want to gather all posts where created_at <= Time.now, status = 0 and dislikes < 3. created_at and status is columns in the Post table, but Dislike is its own table. How can I access the amount of dislikes within the query (Post and Dislike is "connected" through has_many)?
I have this now, and it works:
#posts = Post.where("created_at <= ? AND status = ?", Time.now, 0).order("created_at DESC")
How can I include the amount of dislikes in the query?
(The Dislike table consist of :post_id and :user_id and I can get the amount of dislikes on a post by writing #post.dislikes.count)

There is at least two approaches to reach the goal:
Using having in your query
#posts = Post.joins(:dislikes).
where("posts.created_at <= ? AND posts.status = ?", Time.now, 0).
group("posts.id").
order("posts.created_at DESC").
having("COUNT(dislikes.id) < 3")
Use counter cache column (described in RailsCasts episode)
#posts = Post.
where("created_at <= ? AND status = ? AND dislikes_count < ?", Time.now, 0, 3).
order("created_at DESC")

Related

How do you query a Model by parent ID & created at date

I'm trying to find all the records that have a specific channel_id, and were made less than 1 day ago. The query finds the records with the given channel_id but does not respect the date restriction. Instead, it returns records created at any date.
#discussions = Discussion.where('channel_id = ? and created_at > ?', current_user.subscription.pluck(:channel_id), 1.days.ago )
I expected to only get the Discussions that had relevant subscriptions channel_id, and were made less than 1 day ago. But instead, this query ignores the created at restriction but returns the Discussions that had the corresponding subscriptions channel ID.
#discussions = Discussion.where("channel_id IN (?) AND DATE(created_at) >= ?", current_user.subscription.pluck(:channel_id), (Date.today - 1.day))
OR
#discussions = Discussion.where("channel_id IN (?) AND created_at >= ?", current_user.subscription.pluck(:channel_id), (Date.today - 1.day).end_of_day)
Or
Discussion.where("DATE(created_at) >= ?", (Date.today - 1.day))
.where("channel_id IN (?)", current_user.subscription.pluck(:channel_id))
#discussions = Discussion.where("channel_id = :channel AND created_at > :date", channel: current_user.subscription.pluck(:channel_id), date: Date.yesterday )

Query nested count

I have two models, a User and an Exercise where a user has_many :exercises. There is a cache_counter on the User model, exercise_count
I want to make a leaderboard based on a user's exercise count in the current month. I want to display the 2 users with the highest exercise counts of the month along with the count. If there are other users with the same exercise count as the 2nd place user I want to display them too. (Display 2+ users)
The current query I have,
# User model
scope :exercises_this_month, -> { includes(:exercises).references(:exercises)
.where("exercise_count > ? AND exercises.exercise_time > ? AND exercises.exercise_time < ?",
0 , Time.now.beginning_of_month, Time.now.end_of_month)
}
def User.leaders
limit = User.exercises_this_month.where("exercise_count > ?", 0).order(exercise_count: :desc).second.exercise_count
User.exercises_this_month.where("exercise_count > ? AND exercise_count >= ?", 0, limit)
end
will return the top 2+ user object for all time. I want to limit this to the current month.
You can't use the counter_cache here as it stores the number from all time. Another issue you have is that if you have multiple players having the same highest number of exercises, you'll miss the ones with the second highest number.
# Exercise.rb
scope :exercises_this_month, -> {
where("exercises.exercise_time > ? AND exercises.exercise_time <= ?",
Time.now.beginning_of_month, Time.now.end_of_month)
}
#User.rb
players_this_month = User.joins(:exercises).group(:user_id).merge(Exercise.exercises_this_month).count ##
sorted_counts = players_this_month.values.uniq.sort
if sorted_counts.present?
second_place_counts = sorted_counts[-2] || sorted_counts[-1] #in case all have the same number
top_two_ids = players_this_month.map { |k,v| k if v >= second_place_counts } ##user ids with top two numbers
end
gem 'groupdate'
model
def self.by_month(month_num)
self.where("cast(strftime('%m', created_at) as int) = #
{Date.today.strftime("%m")}")
end
controller
def report
#posts =Post.by_month(Date.today.strftime("%m"))
end
view
some think like this
= #posts.group_by_month(:created_at).count
I am not sure what you want but this will be big help hopefully enjoy :)

Get impresions count from today, yesterday and this month, Impresionist gem

I am using gem called impressionist to log page views on show action.
Everythink works just great.I can get number of all pageviews with:
#advertisement.impression_count
But now I want to be able filter pageviews per today, yesterday and this month.
So far I came up with this solution.
#today = Impression.where( :conditions => { :created_at => Date.today...Date.today+1 }, :impresionable_id =>#advertisement.id)
There is no errors.
Then In view:
<%= "#{#today} views so far!" %>
gives me #<Impression::ActiveRecord_Relation:0x000000068d46f8>
then I tried to add like : <%= "#{#today.impression_count} views so far!" %> gives me this :
undefined method `impression_count'
then I tried just :<%= "#{#today.count} views so far!" %> and still error:
Mysql2::Error: Unknown column 'conditions.created_at' in 'where clause': SELECT COUNT(*) FROM `impressions` WHERE (`conditions`.`created_at` >= '2014-12-18' AND `conditions`.`created_at` < '2014-12-19') AND `impressions`.`impresionable_id` = 127
Any ideas ?
Thanks in advance!
#today = Impression.where( :conditions => { :created_at => Date.today...Date.today+1 }, :impresionable_id =>#advertisement.id)
returns a #<Impression::ActiveRecord_Relation:0x000000068d46f8>.
Try this:
#today = Impression.where(created_at: Date.today...Date.today+1, impresionable_id: #advertisement.id).count
Add scopes in impression.rb
scope :today, -> {where("created_at >= ? AND created_at < ?", Time.now.beginning_of_day, Time.now.end_of_day)}
scope :yesterday, -> {where("created_at >= ? AND created_at < ?", 1.day.ago.beginning_of_day, 1.day.ago.end_of_day)}
scope :this_month, -> {where("created_at >= ? AND created_at < ?", Time.now.beginning_of_month, Time.now.end_of_month)}
in Controller:
#today = Impression.today.where(impresionable_id: #advertisement.id)
#yesterday = Impression.yesterday.where(impresionable_id: #advertisement.id)
#this_month = Impression.this_month.where(impresionable_id: #advertisement.id)
And you can use these scopes anywhere you need to filter Impressions by date today, yesterday or this month. It's better compared to writing the where clause everywhere.
There's no need for the conditions hash.
today = Date.today
range = today..today.next_day
#imp = Impression.where(created_at: range, impressionable_id: #advertisement.id)
And if an #advertisement can have impressions, then the following would be better:
#imp = #advertisement.impressions.where(created_at: range)
Then to get the count, you must:
#today = #imp.count
Also, just FYI, you might need to use DateTime.now instead of Date.today because you're comparing with a datetime field i.e. created_at.
It was easier than I though
In advertisement.rb
has_many :impressions, :as=>:impressionable
def view_count_yesterday
impressions.where("created_at >= ? AND created_at < ?", 1.day.ago.beginning_of_day, 1.day.ago.end_of_day).size
end
def view_count_today
impressions.where("created_at >= ? AND created_at < ?", Time.now.beginning_of_day, Time.now.end_of_day).size
# impressionist_count(:start_date => 1.day.ago)
end

Rank posts by comments added today

I have two models: posts and comments. I want to rank the posts by the # of comments added today. So far I only have it ranked by the number of comments and am not sure how to add the today part.
Here is the posts controller:
#posts = Post.order('posts.comments_count desc')
I have tried
#posts = Post.order('posts.comments_count desc').where("created_at >= ?", Time.zone.now.beginning_of_day)
but that returned nothing.
Your second query is giving you the number of posts that have been created today, rather than the number of comments that have been created today.
This should be what you're looking for:
#posts = Post.select('posts.*, count(comments.id) as count_comments')
.joins(:comments)
.where('comments.created_at >= ?', Time.zone.now.beginning_of_day)
.group('posts.id')
.order('count_comments desc')
If you'd like to include all posts, including ones that don't have any comments today, then you'll need a left join:
#posts = Post.select('posts.*, count(comments.id) as count_comments')
.joins("left join comments on comments.post_id = posts.id and comments.created_at >= '#{Time.zone.now.beginning_of_day}'")
.group('posts.id')
.order('count_comments desc')

Rails 3 - How do I join on a table and filter for last X days?

I have Clients that has_many Transactions. I want to select all Clients who haven't had any transactions in the last 30 days (Transaction has a transaction_date) and display the client's name and the last transaction date.
How can I do this query in rails?
In your transaction model:
scope :trans_order => (:order => "transaction date ASC")
In your controller
#inactive_clients =
Client(:include => :transactions).where
("max(transaction_date) < ? AND transaction_date IS NOT NULL",
Time.now - 30.days)
Then in your view:
#inactive_clients .each |client|
= client.name
= client.transactions.trans_order.last.transaction_date
end
#client_transactions = Transaction.includes(:client).where('transaction_date < ?', 30.days.ago).group(:client_id)
#client_transactions.each do |client_transaction|
puts "client is #{client_transaction.client.name}"
end
I would use a not exists clause like
#clients = Client.where("not exists (select 'x' from
transactions where transactions.client_id = clients.id
and transaction_date > ?)",
30.days.ago)

Resources