Finding the most in a has_many relationship with an extra condition - ruby-on-rails

I have a Users model that has many Polls. The Polls can be the same amongst users. The Poll model has many votes. The User Model also has many votes. The Vote model has User ID, Poll ID, and option chosen. I want to, for a given user, find the poll with the most votes.
I was looking counter_cache, but I'm not sure how to apply it to this problem. Is there a better way to solve this than iterating through each poll a user has and seeing the largest count where used_id = user_Id?

Yes, you could do this with the counter_cache. On the polls table you'd have a column votes_count. Your migration could look like this:
add_column :polls, :votes_count, :integer, default: 0
and the approach for finding the record would be:
class User
has_many :polls
has_many :votes
end
class Poll
has_many :votes
end
class Vote
belongs_to :user
belongs_to :poll, counter_cache :true
##attr :option_chosen
end
If you had some records before adding the table you need to reset the counters.
Now, to fetch the poll with the most votes, you could easily do:
User.first.polls.order(votes_count: :desc).first
Hope that helps.

Related

How do I count within nested resourced and display the results?

I am building a job board in rails based on PostgreSQL. I want to count and display the amount of job offers per employer on the index of the employer page. What is the code for this kind of count?
I created a nested resource, and associated my employer and offer model, by:
class Employer < ActiveRecord::Base
has_many :offers, dependent: :delete_all
end
You should use counter_cache i.e. adding an extra column(offer_count) in employer table & update the counter while making entry in offer table. For more details , check counter_cache in http://guides.rubyonrails.org/association_basics.html
Your migration should like
def change
add_column :employers, :offers_count, :integer, default: 0
Employer.reset_column_information
Employer.all.each do |p|
Employer.update_counters p.id, :offers_count => p.offers.length
end
end

Create if record does not exist

I have 3 models in my rails app
class Contact < ActiveRecord::Base
belongs_to :survey, counter_cache: :contact_count
belongs_to :voter
has_many :contact_attempts
end
class Survey < ActiveRecord::Base
has_many :questions
has_many :contacts
end
class Voter < ActiveRecord::Base
has_many :contacts
end
the Contact consists of the voter_id and a survey_id. The Logic of my app is that a there can only be one contact for a voter in any given survey.
right now I am using the following code to enforce this logic. I query the contacts table for records matching the given voter_id and survey_id. if does not exist then it is created. otherwise it does nothing.
if !Contact.exists?(:survey_id => survey, :voter_id => voter)
c = Contact.new
c.survey_id = survey
c.voter_id = voter
c.save
end
Obviously this requires a select and a insert query to create 1 potential contact. When I am adding potentially thousands of contacts at once.
Right now I'm using Resque to allow this run in the background and away from the ui thread. What can I do to speed this up, and make it more efficient?
You can do the following:
Contact.where(survey_id: survey,voter_id: voter).first_or_create
You should add first a database index to force this condition at the lowest level as possible:
add_index :contacts, [:voter_id, :survey_id], unique: true
Then you should add an uniqueness validation at an ActiveRecord level:
validates_uniqueness_of :voter_id, scope: [:survey_id]
Then contact.save will return false if a contact exists for a specified voter and survey.
UPDATE: If you create the index, then the uniqueness validation will run pretty fast.
See if those links can help you.
Those links are for rails 4.0.2, but you can change in the api docks
From the apidock: first_or_create, find_or_create_by
From the Rails Guide: find-or-create-by
It would be better if you let MySQL to handle it.
Create a migration and add a composite unique key to survey_id, voter_id
add_index :contact, [:survey_id, :voter_id], :unique=> true
Now
Contact.create(:survey_id=>survey, :voter_id=>voter_id)
Will create new record only if there is no duplicates.

Structure for recurring unlimited voting contests

I am in the planning stages of building an app where all users (registered or not) can vote every minute or so. The voting window should last a set period of time (for ex. 1 month). At which point a winning entity is defined and the voting period resets and starts all over. Visitors can then leave comments about the winner for that period. My question is what do you think is the best way to set something like this up?
Here are my current thoughts, but doesn't seem ideal:
1) Vote model: entity_id, contest_id, user_id (optional), created_at, ip_address
search for ip in db on new vote and see if time diff greater than allowed vote time limit between user votes
use CAPTCHA every variable number of votes to ensure human
calculate current vote count by counting all the entries for an entity for a contest
2) Contest model: start and end datetime
have a weekly or monthly cron job create the newest instance
votes find current contest if current date in between these 2 dates
individual model allows to create attributes to the contest(for example, special kinds of contests)
3) Winner model: contest_id, entity_id
allows for users to comments on past contest winners
Without knowing more details, I would go with something along the lines of:
class User
has_many :votes
has_many :comments
has_many :contests, :through => :votes
class Vote
belongs_to :user
belongs_to :contest
class Contest
has_many :votes
has_many :users, :through => :votes
class Comment
belongs_to :user
This way, you can have #user.votes, #contest.votes, #contest.users, etc.
I don't see the need for a Winners model, since that can just be a boolean in Users. If you needed to, you could always have a Winnings model that belonged to both Users and Contests to link the two.
Hope that helps.

How to create a Many-to-One relationship with existing records in Rails

I'm trying to implement something similar to that below. Basic setup is a Schedule model
class Schedule < ActiveRecord::Base
has_many :events
end
The Event model will then belong_to a Schedule.
Events are comprised of a name, datetime, running length and a Room
A Room is chosen from a select list when the event is created, all Rooms are created beforehand through seeding or admin interface. Room will store information like seating count.
I've been trying to find a method implementing this that feels right. Solutions seem to range between habtm or has_many :through but I can't see the need for a join table for this situation.
Theoretically, an Event has_one :room but the reverse relationship isn't belongs_to :event as a Room may be used for many events, this also requires the foreign key to be on the rooms table.
I've considered handling this manually in the model using a room_id foreign key in the event, I could then query for the relevant Room. I think this would work since I currently cannot see a requirement for finding all events using a Room.
class Event < ActiveRecord::Base
belongs_to :schedule
def room
Room.find(room_id)
end
end
Everything I've done so far in Rails has felt 'right', but all the solutions I've come up with here doesn't and I feel like I'm missing something; or maybe I'm just expecting that extra bit of magic.
Is there a "Rails" way to do this?
Wouldn't Event just have a belongs_to relationship to Room in this case?
class Event < ActiveRecord::Base
belongs_to :schedule
belongs_to :room
end
If you think about it from a schema perspective, your "events" table almost certainly has "schedule_id" and "room_id" columns.
The other side of the relationship is that a Room has_many Events.

What's the best way to destroy both sides of a self-referential association?

In my project, I have a self-referential association.
I have a User model:
class User < ActiveRecord::Base
has_many :relationships, :dependent => :destroy
has_many :peers, :through => :relationships
end
And a Relationship model:
class Relationship < ActiveRecord::Base
belongs_to :user
belongs_to :peer, :class_name => "User"
end
When two users are peers with one another, there are obviously two records in the database.
When one user opts to end a relationship, I'd like this to destroy both records - not just one side of the relationship.
Is there a better way to go about doing this rather than loading the relationship twice in the controller (once for each side of the relationship)?
Couple of ways this can be done
Firstly is an after delete trigger, this is a pretty controversial way of doing things if you believe in the false promise of database agnosticism, however is one that works - in essence, you look at old.peer_id and old.user_id and then do a delete but reversing the roles. If you want to go down this route, you should consult your database manual as how to implement a trigger.
The second way is an after_destroy callback where you do a
after_destroy do |record|
other = Relationship.find_by_user_id_and_peer_id(record.peer_id, record.user_id)
other.destroy if other
end
The other - and probably more drastic measure is to rework the model, so that it has a boolean accepted field wherein both sides of the relationship are modelled by one record in the database, there is a constraint on records where (peer_id, user_id) = (user_id, peer_id). That way you wont have to worry about deleting both sides, nor having duplicate records.

Resources