Search code examples
ruby-on-railsrubyfindassociationsnamed-scope

Find all associated objects by specific condition



class QuestionGroup < ActiveRecord::Base
  has_many :questions
end

class Question < ActiveRecord::Base
  belongs_to :question_group
  has_many :question_answers
  has_many :question_users_answers, :through => :question_answers, :source => :user_question_answers

  def self.questions_without_answers(user_id)
    select {|q| q.question_users_answers.where(:user_id=>user_id).empty?}
  end
end

class QuestionAnswer < ActiveRecord::Base
  belongs_to :question
  has_many :user_question_answers
end

I need find all Questions if they have no user answers I did it by class method self.questions_without_answers(user_id)

But how can I find all QuestionGroups where present questions_without_answers and for particular user?

P.S: I need to find all unanswered questions and all groups that own these questions, can I do it by find or named-scope?

UPDATED:

  def self.groups_without_answers(user_id)
    questions_ids = Question.questions_without_answers(user_id).map {|q| q.id}
    all(:conditions => "id in (select distinct question_group_id from questions where id in (#{questions_ids.join(',')}))")
  end

But I think it is not good or maybe I wrong?


Solution

  • class QuestionGroup < ActiveRecord::Base
      has_many :questions
    
      def self.without_answers(user_id)
        joins(%"inner join questions on question_groups.id = questions.question_group_id
                inner join question_answers
                  on question_answers.question_id = questions.id
                inner join question_groups
                  on question_answers.question_users_answers_id = question_users_answers.id").where("user_question_answers.user_id" => user_id).select { |qq| ... }
        end
      end
    end
    

    You can change some of the inner joins to left out join to pick up records where the table you are joining to doesn't have a match, for instance where there is no answer. The fields of the table you are joining to will have NULL values for all the fields. Adding a where id is null will even filter to just the questions with no answers.

    Keep in mind that this is just an alternative technique. You could programmatically solve the problem simple with:

    class QuestionGroup
      def self.question_groups_without_answers(user_id)
        select {|qq| qq.question_users_answers.where(:user_id=>user_id).empty?}.map{ |qq| qq.question_group }
      end
    end
    

    An advantage of doing the joins is that the database does all the work, and you don't send several SQL queries to the database, so it can be much faster.