Find Total Votes a User has on their Posts - ruby-on-rails

I'm using the thumbs_up gem to get votes(likes) on Posts.
I have a page for each user's statistics and one of the statistics I'm trying to find shows how many people have voted (liked) the current_user's Posts. Here is what I have so far, I just need to include something that shows only the count that was on the current_users posts.
#vote_count = Vote.where("voteable_type = ?", "Update").count
# This shows all of the votes on all of the updates instead of ONLY the vote count of the current_user's updates
The Votes table has these columns
voteable_id
voteable_type
voter_id
voter_type
...
...
I think I have to associate the voteable_id to the current_user's update_id but I can't figure it out.
Vote Model
scope :for_voter, lambda { |*args| where(["voter_id = ? AND voter_type = ?", args.first.id, args.first.class.base_class.name]) }
scope :for_voteable, lambda { |*args| where(["voteable_id = ? AND voteable_type = ?", args.first.id, args.first.class.base_class.name]) }
scope :recent, lambda { |*args| where(["created_at > ?", (args.first || 2.weeks.ago)]) }
scope :descending, lambda { order("created_at DESC") }
belongs_to :voteable, :polymorphic => true
belongs_to :voter, :polymorphic => true
attr_accessible :vote, :voter, :voteable if ActiveRecord::VERSION::MAJOR < 4
# Comment out the line below to allow multiple votes per user.
validates_uniqueness_of :voteable_id, :scope => [:voteable_type, :voter_type, :voter_id]
Edit
# user.rb
has_many :updates
# update.rb
belongs_to :user

Try with:
user.updates.joins(:votes).where(votes: { vote: true }).count

Related

How to resolve 'Remove from your finder: :includes => [:company]'

When I try to write nested relation, I got bellow warning and can't get company data.
Unused Eager Loading detected
User => [:company]
Remove from your finder: :includes => [:company]
I write bellow code.
When I check about sql. it looks like to make relation with user and company.
But this controller result can't get company data.
Do you know how to resolve this issue?
controller/product.rb
def products
Product
.sale_date_between(params[:from], params[:to])
.organization(current_user.company_id)
.order('sale_date DESC')
end
models/product.rb
belongs_to :user
scope :with_user, -> { includes(:user, {user: :company}) }
scope :sale_date_between, ->(from, to) { where('sale_date_date >= ?', from).where('sale_date_date <= ?', to) }
scope :company, ->(id) { with_user.where(users: { company_id: id }) }
models/user.rb
has_many :products
models/company.rb
belongs_to :user, foreign_key: 'company_id'

select specific attributes from array rails

I have post model
class Post < ActiveRecord::Base
acts_as_voteable
end
and Vote model
class Vote < ActiveRecord::Base
scope :for_voter, lambda { |*args| where(["voter_id = ? AND voter_type = ?", args.first.id, args.first.class.name]) }
scope :for_voteable, lambda { |*args| where(["voteable_id = ? AND voteable_type = ?", args.first.id, args.first.class.name]) }
scope :recent, lambda { |*args| where(["created_at > ?", (args.first || 2.weeks.ago)]) }
scope :descending, order("created_at DESC")
belongs_to :voteable, :counter_cache=>true,:polymorphic => true,:touch=>true
belongs_to :voter, :polymorphic => true
attr_accessible :vote, :voter, :voteable
# Comment out the line below to allow multiple votes per user.
validates_uniqueness_of :voteable_id, :scope => [:voteable_type, :voter_type, :voter_id]
end
when I get the post voters with these method
<% #post.voters_who_voted.each do |voter|%>
<%= voter.name %>
<% end %>
I load my database
how can I select only the user name and user id from these array?
update I changed my code I am using thumbs_up gem I pasted less code first to simplify the question
What do you mean by "load database"? If you want to select only id and name columns, then use #post.users.select([:id, :name]).each ...
Or is it about this problem (according to code that you provided)?
UPD.
voters_who_voted loads all voters and returns array https://github.com/bouchard/thumbs_up/blob/master/lib/acts_as_voteable.rb#L113. You have to add own association to Post model:
has_many :voters, :through => :votes, :source => :voter, :source_type => 'User'
It's just example, perhaps voters will clash with already existing method, if any.
Then use it here instead of voters_who_voted
did you try collect method ??
names = #post.users.collect(&:name)
ids = #post.user.collect(&:id)
If you want it to be related you can make a HASH with it. Id's mapped to the names.

how to get unvoted post count in Ruby on Rails

I am using Thumbs_up gem for creating vote function. I have 3 tables - Post, User and Vote where Post is acts_as_voteable and User is acts_as_voter.
Model Post.rb
class Post < ActiveRecord::Base
attr_accessible :title, :content, :user_type
acts_as_voteable
validates_presence_of :title,:content
default_scope order: 'posts.created_at DESC'
end
Model Vote.rb
class Vote < ActiveRecord::Base
scope :for_voter, lambda { |*args| where(["voter_id = ? AND voter_type = ?", args.first.id, args.first.class.base_class.name]) }
scope :for_voteable, lambda { |*args| where(["voteable_id = ? AND voteable_type = ?", args.first.id, args.first.class.base_class.name]) }
scope :recent, lambda { |*args| where(["created_at > ?", (args.first || 2.weeks.ago)]) }
scope :descending, order("created_at DESC")
belongs_to :voteable, :polymorphic => true
belongs_to :voter, :polymorphic => true
attr_accessible :vote, :voter, :voteable
end
Model User.rb
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
acts_as_voter
has_many :posts, dependent: :destroy
end
Now I want to count posts number which are NOT VOTED. I am trying to do like this..
<%= Post.joins(:votes).where("dont_know_how_to_write_condition").count %>
Any help?? Thanks in advance
I am not sure if this will work in sqlite, it should on postgresql though.
def self.not_voted
joins("left outer join votes on votes.voteable_id = posts.id").
where("votes.id is null")
end
Then:
Post.not_voted.count
There are ways to do that condition in SQL with NOT EXISTS but they are very heavy queries. If this si going to be an action facing the public or performed multiple times, it might be too heavy to do it like that. I would recommend you to denormalize the tables and include a count of votes on the post. Just use an observer on vote creation and deletion to update the vote count for the post. The query then would be like
Post.where('votecount = 0')
This query is much lighter and scalable than the previous one

How can I optimize and combine these inefficient database queries?

Here's are my DB queries:
User has_many UserFollow (UserFollow is a relationship between User models).
User has_many Photos.
Photo has_many PhotoFollow relationships (PhotoFollow is a relationship between User and Photo models).
#user_list = Array.new
user_followers = UserFollow.where("user_1_id = ?", current_user.id).includes(:follower)
user_followers.each do |f|
#user_list << f.user
end
photos = Photo.where("user_id = ?", current_user.id).includes(:follow_relationships => [:photo])
photos.each do |p|
p.follow_relationships.each do |f|
#user_list << f.user if !#user_list.include? f.user
end
end
if #user_list.size < 150
users = User.where("verified = ? and first_name IS NOT NULL and last_name IS NOT NULL", true).includes(:solutions).limit(150 - #user_list.size)
users.each do |u|
#user_list << u if !#user_list.include? u
end
end
All of this takes a ridiculous amount of time obviously. Using includes helps, but I am wondering if there is some way to more efficiently do this set of operations.
Thanks,
Ringo
Append your associations first.
class User < ActiveRecord::Base
has_many :follows_to_user, :class_name => 'UserFollow', :foreign_key => 'follower_id'
has_many :follows_to_photo, :class_name => 'PhotoFollow', :foreign_key => 'user_id' # if you don't have it now
end
Now, first two queries you can make much more elegant within just one SQL query, returning AR::Relation scope.
#user_list = User.includes(:follows_to_user => {}, :follows_to_photo => {:photo => {}}).where(["user_follows.user_1_id = :user_id OR photos.user_id = :user_id", :user_id => current_user.id])
About 150... [updated]
Of course you'd better to implement that logic appending the previous SQL statement with conditions and UNION statement (using SQL syntax only), that should return AR::Relation and will be a little faster. But you can stay lazy and leave it in ruby, although it will return Array:
if (count = #user_list.count) && count < 150 # run COUNT just once and store value into local variable
#user_list |= User.where("verified = ? and first_name IS NOT NULL and last_name IS NOT NULL", true).includes(:solutions).limit(150 - count)
end
Looking at your code, you're planning on getting a list of users to your #user_list. You can build up the list of user ids first so we don't create unnecessary AR objects
First code
#user_list = Array.new
user_followers = UserFollow.where("user_1_id = ?", current_user.id).includes(:follower)
user_followers.each do |f|
#user_list << f.user
end
can be changed to
# assuming you have a user_id column on user_follows table
user_ids = User.joins(:user_follows).where(user_follows: { user_1_id: current_user.id })
.uniq.pluck('user_follows.user_id')
Second code
photos = Photo.where("user_id = ?", current_user.id).includes(:follow_relationships =>[:photo])
photos.each do |p|
p.follow_relationships.each do |f|
#user_list << f.user if !#user_list.include? f.user
end
end
can be changed to
user_ids += Photo.where(user_id: current_user.id).joins(follow_relationships: :photo)
.uniq.pluck('follow_relationships.user_id')
Third code
if #user_list.size < 150
users = User.where("verified = ? and first_name IS NOT NULL and last_name IS NOT NULL", true).includes(:solutions).limit(150 - #user_list.size)
users.each do |u|
#user_list << u if !#user_list.include? u
end
end
can be changed to
user_ids += users = User.where(verified: true)
.where('first_name IS NOT NULL AND last_name IS NOT NULL')
.where('id NOT IN (?)', user_ids)
.limit(150 - user_ids.size).pluck(:id)
Then you can just fetch all the users using user_ids
#user_list = User.where(id: user_ids)
There has to be a better way then my answer, but why don't you include :user since you're loading them when iterating over your queries?
#user_list = Array.new
user_followers = UserFollow.includes(:user).where("user_1_id = ?", current_user.id)
# why did you include followers?
user_followers.each do |f|
#user_list << f.user
end
photos = Photo.includes(follow_relationships: { photo: :user }).where("user_id = ?", current_user.id)
photos.each do |p|
p.follow_relationships.each do |f|
#user_list << f.user unless #user_list.include? f.user
end
end
if #user_list.size < 150
users = User.where("verified = ? and first_name IS NOT NULL and last_name IS NOT NULL", true).limit(150 - #user_list.size)
# why did you include solutions?
users.each do |u|
#user_list << u unless #user_list.include? u
end
end
Maybe this is faster, I'm not sure:
#follower_ids = UserFollow.where("user_1_id = ?", current_user.id).pluck(:user_1_id).uniq
#photo_ids = Photo.joins(follow_relationships: :photo)
#photo_ids = #photo_ids.where("user_id = ? and user_id not in (?)", current_user.id, #follower_ids)
#photo_ids = #photo_ids.pluck(:user_id).uniq
#followers = User.where("id in (?)", #follower_ids)
#photo_users = User.where("id in (?) and not in (?)", #photo_ids, #follower_ids)
#array_size = (#follower_ids + #photo_ids).size
if #array_size < 150
#users = User.where("verified = ? and first_name is not null and last_name is not null", true)
#users = #users.where("id not in (?)", #photo_ids + #follower_ids).limit(150 - #array_size)
else
#users = []
end
#final_array = #followers + #photo_users + #users
I haven't tested if this works, or if it's faster. It has more database queries but less iterations.
Update
What if you added another column to the users model that gets updated with a value from 1 to 3, depending on whether they have followers, photos, or nothing.
Then all you would need to do is:
# in User model
def self.valid_users
where("verified = ? and first_name is not null and last_name is not null", true)
end
#users = User.valid_users.order("sortable ASC").limit(150)
This is just a guess.
#user_list = current_user.followers.where("verified = ? and first_name IS NOT NULL and last_name IS NOT NULL", true).includes(:solutions, :photos => {:follow_relationships}).limit(150)
Over all I think its all wrong due to the complexity alone. If you cant read what its doing clearly then you should start over again.
Have you set up table indexes for your tables?
If you haven't yet, set them up for all of your foreign keys and for columns that you need to include in your conditions. In your DB migration script (with your correct table and column names, of course). They will speed up your queries, especially if you have a large dataset:
add_index :user_follows, :follower_id
add_index :user_follows, :followed_id
add_index :photos, :user_id
add_index :photo_follow_relationships, :photo_id
add_index :photo_follow_relationships, :follower_id
add_index :users, :verified
add_index :users, :first_name
add_index :users, :last_name
Also, some comments and recommendations:
# ... [SNIP] Query and add to user list.
user_followers = [] # You are not actually using the UserFollow records. Unload
# them from memory. Otherwise, they will be stored until we
# leave the block.
# There is no need to fetch other Photo data here, and there is no need to load
# :photo for FollowRelationship. But there is a need to load :user.
photos = Photo.where(:user_id => current_user.id).select('photos.id').
includes(:follow_relationships => [:user])
# ... [SNIP] Add to user list.
photos = [] # Unload photos.
# ... [SNIP] Your condition.
# There is no need to load :solutions for the users here.
users = User.where("verified = ? and first_name IS NOT NULL and last_name IS NOT NULL", true).limit(150 - #user_list.size)
# ... [SNIP] Add to user list.
It would, of course, be nicer if you refactor the code too, as in some of mind blank's recommendations. You can also manke use of has_many :through associations too to clean up your controller.
Add the following relationships to your models
class User
has_many :user_follows
has_many :inverse_user_follows, :class_name=>'UserFollow', :foreign_key=>:follower_id
# followers for user
has_many :followers, :through => :user_follows
# users followed by user
has_many :followed_users, :through => :inverse_user_follows, :source => :user
# photos created by user
has_many :photos
has_many :photo_user_follows, :through => :photos, :source => :user_follows
# followers for user's photo
has_many :photo_followers, :through => :photo_user_follows, :source => :follower
has_many :photo_follows
# photos followed by user
has_many :followed_photos, :source => :photo, :through => :photo_follows
end
class UserFollow
# index user_id and follower_id columns
belongs_to :user
belongs_to :follower, :class_name => "User"
end
Photo related models
class Photo
# index user_id column
belongs_to :user
has_many :photo_follows
has_many :followers, :through => :photo_follows
end
class PhotoFollow
# index photo_id and follower_id columns
belongs_to :photo
belongs_to :follower, :class_name => "User"
end
Now you can get the users who are current users followers OR current user's photo followers OR active users..
user_ids = current_user.follower_ids | current_user.photo_follower_ids
User.where("ids IN (?) OR
( verified = ? and first_name IS NOT NULL and last_name IS NOT NULL )",
user_ids, true).limit(150)

ActiveRecord associations and scopes oh my

I'm trying to figure out how to pull data from multiple tables and making sense of it.
I have events, occurrences and venues tables.
Here are the models/associations so far:
models/event.rb:
class Event < ActiveRecord::Base
has_many :occurences, :dependent => :destroy
belongs_to :price
belongs_to :venue
validates_presence_of :venue
end
models/venue.rb:
class Venue < ActiveRecord::Base
has_many :events, :dependent => :destroy
end
models/occurence.rb:
class Occurence < ActiveRecord::Base
belongs_to :event
scope :today, lambda { where("occurence.datetime_start <= ? AND datetime_end >= ?", Time.now.end_of_day, Time.now) }
scope :this_week, lambda { where("occurence.datetime_start <= ? AND datetime_end <= ?", Time.now.end_of_day, Time.now.at_end_of_week) }
scope :next_week, lambda { where("occurence.datetime_start > ? AND datetime_end < ?", Time.now.at_end_of_week, Time.now.at_end_of_week + 1.weeks) }
scope :this_month, lambda { where("occurence.datetime_start <= ? AND datetime_end >= ?", Time.now.end_of_day, Time.now.at_end_of_month) }
scope :next_month, lambda { where("occurence.datetime_start >= ? AND datetime_end <= ?", Time.now.at_beginning_of_month + 1.months, Time.now.at_end_of_month + 1.months) }
end
What I'd like to show in my view/events/index.html.erb is a table of all events happening today and I like to do something like:
<% #events.today.each do |event| %>
and display all the events that match the .today scope.
I tried moving the scope to the event model to no avail. Should I move the scopes to the event model? So far my searches have lead me to
"Using scope to return results within multiple DateTime ranges in ActiveRecord"
and
"Multiple scope in rails 3.0" but I can't really make anything out of it.
Can anyone point me in the right direction? Should I create a new function in my event model?
class Event < ActiveRecord::Base
has_many :occurences, :dependent => :destroy
belongs_to :price
belongs_to :venue
validates_presence_of :venue
scope :today, lambda {
joins(:occurences).where("datetime_start <= ? AND datetime_end >= ?", Time.now.end_of_day, Time.now)
}
end
If you want your syntax to work like:
#events.today
Then the scope would have to be in the Event model.
If you're OK with:
#event.occurences.today
then you're OK now, i.e.:
#events = Event.all
#events.each do |ev|
ev.occurences.today.each do |oc|
end
end

Resources