Rails sort from model association - ruby-on-rails

I have the following associations:
class Question < ApplicationRecord
belongs_to :shopper
has_many :question_messages, dependent: :destroy
end
class QuestionMessage < ApplicationRecord
belongs_to :question
belongs_to :shopper, optional: true
belongs_to :store, optional: true
end
As you can see from the associations above I have two different user types(store, shopper) that can create question messages. I have an index page for the shopper user where I want to sort his questions according to the last question message he created. I'm trying this with the following code:
#questions = Question.where(shopper_id: current_shopper)
.group(:id).joins(:question_messages)
.order('max(question_messages.created_at) asc')
The issue with this code is that it sorts the questions according to last questions messages created and not specifically the last question message created by the shopper. Any ideas on how to implement this so I can sort the questions according to the last question message that was created by the shopper?

Well if you want them to be sorted only by shopper's question messages then you need to apply the max only on question messages from that shopper:
Question.where(shopper_id: current_shopper)
.group(:id)
.joins(:question_messages)
.where(question_messages: {shopper_id: current_shopper })
.order('max(question_messages.created_at) asc')
The solution above will not return questions if the shopper doesn't have a QuestionMessage for it.
IF you need all questions regardless if the shopper has a question message pointing to the question, than you can use this:
Question.where(shopper_id: current_shopper)
.group(:id)
.joins("LEFT JOIN question_messages
ON question_messages.question_id = questions.id
AND question_messages.shopper_id = #{current_shopper.to_i}")
.order('max(question_messages.created_at) asc')

Related

Creating and querying questions/answers in Ruby-on-rails

We're creating a Ruby-on-rails application where the user is presented with a series of questions where they can give multiple different answers to one question. For now it's it's toggles but in the future we might be interested in doing freeform text as well. Each group of answers is connected to a pin on a map.
For now, each pin simply has a coplumn of questions and answers, which is a hash, but we'd really like to make it nicer and more scaleable. Some of the queries we'd like to do are e.g. count the amount of people that have answered 'It's quiet here' to the question 'Why do you come here?'
For now, I thought of the following models:
# pin.rb
class Pin < ApplicationRecord
has_many :questions
end
# question.rb
class Question < ApplicationRecord
belongs_to :pin
has_many :answers
end
# answer.rb
class Answer < ApplicationRecord
belongs_to :questions
end
Is this the right way of approaching this? I'd then fill seed.rbwith the questions and possible corresponding answers.
What's the best way of then creating new objects or rows, when a user answers a given question?
This looks good to me. If your Answer defines possible choices for a question, then for storing user answers/selections, you'll have to create another model/table
class UserAnswer < ApplicationRecord
belongs_to :question
belongs_to :answer
# attribute :value
end

RubyOnRails: Decoupling models and refactoring filters

In my Rails 4 app I have three models:
class User < ActiveRecord::Base
has_many :user_question_results, dependent: :destroy
end
class Question < ActiveRecord::Base
has_many :user_question_results, dependent: :destroy
end
class UserQuestionResult < ActiveRecord::Base
belongs_to :question
belongs_to :user
end
On my Questions index page I want to show all questions in the system with results specific to the user, and filter questions depending on the results for each question. Eg:
Question 1 - 0%, not started
Question 2 - 25%, started
etc
If I try something like UserQuestionResult.where(user: user) it returns, of course, questions with user results only, not all questions.
So I added this method to the Question class:
def self.user_info(user)
joins_sql = <<-SQL
LEFT OUTER JOIN user_question_results
ON questions.id=user_question_results.question_id
AND user_question_results.user_id=#{user.id}
SQL
select_sql = <<-SQL
questions.*,
ifnull(user_question_results.percentage, 0) percentage,
user_question_results.percentage IS NOT NULL as started
SQL
self.joins(joins_sql).select(select_sql)
end
To use filters I added scopes to the Question model, like this:
scope :user_correct, -> { where('percentage >= 99') }
So in my QuestionsController I user something like this:
Question.user_info(u).user_correct
It does work, but I wonder if there is a better solution. Especially, I'm worried about two points:
Question model got tightly coupled with the UserQuestionResult and User models.
Named scopes in the Question model depends would only work if user_info was called previously. It would break otherwise. I think it's a very bad design.

has_many order by model and another table

I have a Question model.
every question can have many answers. And answers have status id which is a number.
So: I have:
class Question < ActiveRecord::Base
has_many :answers, :order =>'status_cat_id'
Now, answers are related to users, and I want the order of answers to be first by status_is and then by user name.
So, when I'll call: #question.answers I will get the answers ordered first by status and then by the user name.
Is it possible to achieve that through the model?
The answer is:
has_many :answers, :include => :user, :order =>'status_cat_id, users.name'

How to create voting to a multiple choice application using ruby on rails

I am working on a multiple choice question and answer application using Ruby on Rails and I have the following model.
class User < ActiveRecord::Base
has_many :questions
end
class Question < ActiveRecord::Base
belongs_to :user
has_many :answers
end
class Answer < ActiveRecord::Base
belongs_to :question
has_many :votes
end
class Vote < ActiveRecord::Base
belongs_to :user
belongs_to :answer
end
My problem is a user can choose all the answers.
How can I fix it so that it updates to the new answer?
For example, a has 5 votes and b 3 votes.
User clicks on a and a increments to 6 votes, same user comes back and clicks on b, a decrements back to 5 votes and b increments to 4 votes.
My first guess is that I need to add another model for example, user_choice with user_id and vote_id to track the previous answer.
You have your answer within the models. Just add a point system to the question model.
Under the question model,
def points
self.answers.count
end
Or, if answers have different values attached to them, make points an attribute of the answer instances.
def points
self.answers.pluck(:points).inject(:+)
end
This will sum up all the points from an answer belonging to a question, which allows you to order the questions by point count.
However, I am assuming you will need a Choice model for a question, unless that is in fact what the votes are for.
question has_many choices
choices belong_to question, etc.
I'm not sure what you mean when you say:
How can I fix it so that it updates to the new answer?
EDIT
Er, okay so you just need exactly what I mentioned earlier. A vote is really just the number of times an answer has been chosen.
If your problem is that users can choose more than 1 answer, it is likely that you should implement view based validations via javascript, or better yet, just disable their ability to select multiple choices, which if you were using a select tag, is actually the default. You must've added
multiple: true
in the select tag options for the user to select multiple entries.
On the backend, you can do this:
class Choice < ActiveRecord::Base
validates_uniqueness_of :user_id, scope: [:question_id, :answer_id]
end

Difficulty sorting entries based on attributes of related models

I am having trouble implementing a feature that allows users to see questions (my model) sorted or filtered based on various attributes belonging to it (i.e. was the question answered? how many answered per question, etc), which would be based on the Question model's attributes, or that of attributes of related models to Question.
I have the following models:
class Question < ActiveRecord::Base
belongs_to :course
belongs_to :user
has_many :answers, inverse_of: :question
belongs_to :accepted_answer, class_name: :answer, foreign_key: :accepted_answer_id
default_scope order: 'questions.created_at DESC'
end
class Answer < ActiveRecord::Base
belongs_to :user
belongs_to :question, inverse_of: :answers
has_many :a_votes
default_scope order: 'answers.created_at DESC'
def accepted?
return false if new_record?
question.try( :accepted_answer_id ) == id
# an alternative is to use question.try( :accepted_answer ) == self
end
end
What I would be add are sorts or filters in the controller such as "See only answered questions", where only questions that has question.accepted_answer == true. What would be the best way to achieve this? And are there any guides that I should consult on ActiveRecord filtering/sorting that I could use for future reference?
Thanks!!
Addendum
I am displaying the questions as rendered _question.html.erb and calling it via the show function of question's parent, Group (so, each Group will has_many questions)
class CoursesController < ApplicationController
def show
#course = Course.find(params[:id])
# #questions = #course.questions.all this is the default selection
#questions = #course.questions.by_answer_count #maybe Im not calling the scope correctly (and sorry if I am making a noob mistake..)
end
#There are other methods not shown
end
I achieve this sort of thing by defining a scope on the parent model using joins and grouping.
For example, this scope would order the questions by number of answers (descending).
class Question
scope :by_answer_count, -> {
joins(:answers).reorder("count(answers.id) DESC").group(:id)
}
end
I hope that helps.

Resources