Leahcim
Leahcim

Reputation: 42029

how to write a query on an association with conditions

I'm wondering if you can help me write a scope method for Rails that I'm having a little trouble completing. I have an app where users can ask questions. The questions are classified according to both categories and tags i.e.

Question.rb has_many :categories 
Question.rb has_many :tags

When I show a question, I want to have a feature that shows similar questions. In order to do that, I was going to show other questions according to the following conditions

First, at least one category is the same
Second, at least one tag is the same
Third, the other question has at least one answer already.

In my show action of the Questions controller, after I find the question, I then get the categories and tags for it

@question = Question.find(params[:id])
categories = @question.categories
tags= @question.tags

The local variable categories might look like this

[#<Category id: 3, name: "Wills & Estates", created_at: "2013-04-10 21:53:49", updated_at: "2013-04-10 21:53:49">, #<Category id: 4, name: "Business Law", created_at: "2013-04-10 21:53:49", updated_at: "2013-04-10 21:53:49">]

So what I do then is strip the name out of it like this

cats = Question.get_categories(categories)

with get_categories on the Question.rb model like this

  def self.get_categories(categories)
    categories = categories.map(&:name)
  end

I do the exact same thing for tags

cats = Question.get_tags(tags)

Question.rb

  def self.get_tags(tags)
     tags = tags.map(&:name)   
  end 

I then pass 'cats' and 'tags' (which are just strings of the categories and tags) into a scope on the Question.rb model

 @similarquestions = Question.similar_questions(cats, tags)

This is where I get lost...

 scope :similar_questions, lambda { |cats, tags|
  joins(:categories).where(:categories { :name => cats})
  joins(:tags).where(:tags { :name => tags })
 }

The local variable 'cats' and 'tags' might be a single string, or they might be multiple strings depending on how many categories and tags a particular question has. My criteria for locating similar questions, as stated above, is to have one category the same and one tag the same (with the question also having at least one answer).

Could you give me some pointers as to what I might do inside this scope to accomplish what I'm trying to do. I'm wondering if I should be passing arrays in as local variables and do something like

scope :similar_questions, lambda { |cats, tags|
      cats.each do |cat|
      joins(:categories).where(:categories { :name => cat})
      end 
      tags.each do |tag|
      joins(:tags).where(:tags { :name => tag })
      end
     }

but even then I'm having trouble figuring out a way to achieve what I'm trying to do. One of my concerns is that I don't want a maze of if/then clauses in the scope. I'm sure there's a tidier way to do what I want, but I can't figure it out. One of the reasons I thought of passing in an array was to check if something was 'in' the array etc.

Thanks in advance if you can help.

Update

Question.rb has_many :answers

Upvotes: 0

Views: 57

Answers (1)

PinnyM
PinnyM

Reputation: 35531

scope :similar_questions, lambda {|question|
  joins(:categories, :tags).includes(:answers).
  where( categories: {name: question.categories.pluck(:name)},
         tags: {name: question.tags.pluck(:name)} ).
  where("questions.id != ? AND answers.id IS NOT NULL", question.id)
}

Now you can do:

@similar_questions = Question.similar_questions(self)

Upvotes: 2

Related Questions