1

I am finding something like below. Constructing a where clause using condition. Is it possible in ruby? or I need to separate it into two where clause?

Post
  .where(tag: "A") if condition A
  .where(tag: "B") if condition B
  .where(user_id: 1)
  .order(....)

Actually, my case is like this. Is there any way to handle?

def this_function                      
    @questions = Question.joins(:comment_threads)
                          .tagged_with(tag_variable, wild: true, any: true) if tag_variable.present? 
                          .where(index_where_clause)
                          .where("questions.created_at < ?", query_from_date_time)
                          .order(created_at: :desc).limit(5)
end
  def index_where_clause
    where_clause = {}
    where_clause[:user_detail_id] = current_user_detail.id if params[:type] == "my_question"
    where_clause[:comments] = {user_detail_id: current_user_detail.id} if params[:type] == "my_answer"
    where_clause[:wine_question_score_id] = params[:wine_question_score_id] if params[:wine_question_score_id].present?
    where_clause
  end
2
  • I have just edit this question. Commented Jan 23, 2016 at 19:24
  • It's not necessary to tell us you've edited anything. We can tell. Instead, simply add the changes in the body where they would/should have gone initially. Commented Jan 23, 2016 at 22:06

5 Answers 5

2

The methods you're using return relations so you can say things like this:

@questions = Question.joins(:comment_threads)
@questions = @questions.where("questions.created_at < ?", query_from_date_time)
@questions = @questions.tagged_with(tag_variable, wild: true, any: true) if tag_variable.present? 
@questions = @questions.where(:user_detail_id => current_user_detail.id) if params[:type] == "my_question"
@questions = @questions.where(:comments => { user_detail_id: current_user_detail.id}) if params[:type] == "my_answer"
@questions = @questions.where(:wine_question_score_id => params[:wine_question_score_id]) if params[:wine_question_score_id].present?
@questions = @questions.order(created_at: :desc).limit(5)

and build the query piece by piece depending on what you have in params.

I'd probably break it down a little more:

  def whatever
    @questions = Question.joins(:comment_threads)
    @questions = @questions.where("questions.created_at < ?", query_from_date_time)
    @questions = with_tag(@questions, tag_variable)
    #...
    @questions = @questions.order(created_at: :desc).limit(5)
  end

private

  def with_tag(q, tag)
    if tag.present?
      q.tagged_with(tag, wild: true, any: true)
    else
      q
    end
  end

  #...

and bury all the noisy bits in little methods to make things cleaner and easier to read. If you're doing this more than once then you could use scopes to hide the noise in the model class and re-use it as needed.

Sign up to request clarification or add additional context in comments.

3 Comments

This is the answer I was going to add if it wasn't here already. Active Relations are just objects like any other... not magical train wrecks that can't be uncoupled.
@WayneConrad: Yeah, people seem to forget that Ruby has objects and classes and not just hashes and whatever Rails gives you, period.
Thanks. this should be the best answer
1

#tap can be helpful for modifying an object in place to apply conditional logic, in this case the object would be your .where conditions:

Post
.where(
  { user_id: 1 }
  .tap do |conditions|
    conditions[:tag] = 'A' if condition A
    conditions[:tag] = 'B' if condition B
  end
)
.order(...)

Or, perhaps it's a little cleaner if you create a helper method:

def specific_conditions
  { user_id: 1 }.tap do |conditions|
    conditions[:tag] = 'A' if condition A
    conditions[:tag] = 'B' if condition B
  end
end

Post.where(specific_conditions).order(...)

But as a side note, if there's a case where condition A and condition B can both be true, the second conditions[:tag] = ... line will override the first. If there is not a case where both can be true, you might try to use some kind of collection to look up the proper value for tag.

CONDITION_TAGS = {
  a: 'A'.freeze,
  b: 'B'.freeze,
}.freeze

def specific_conditions
  { user_id: 1 }
  .tap do |conditions|
    conditions[:tag] = CONDITION_TAGS[condition_value] if condition_value
  end
end

Post.where(specific_conditions).order(...)

Comments

1
#in Question class
scope :with_user_detail, -> (user_detail_id, flag=true) do
  where("user_detail_id = ?", user_detail_id) if flag
end

scope :with_user_detail_comments, -> (user_detail_id, flag=true) do
  joins(:comment_threads).where("comments.user_detail_id = ?", user_detail_id) if flag
end

scope :with_wine_question_score, -> (wine_question_score_id) do
  where("wine_question_score_id = ?", wine_question_score_id) if wine_question_score_id.present?
end

scope :tagged_with_condition, -> (tag_variable, wild, any) do
  tagged_with(tag_variable, wild, any) if tag_variable.present? 
end

def this_function
  my_question_flag = params[:type] == "my_question"
  my_answer_flag = params[:type] == "my_answer"
  Question.with_user_detail(current_user_detail.id, my_question_flag)
    .tagged_with_condition(tag_variable, wild: true, any: true)
    .with_user_detail_comments(current_user_detail.id, my_answer_flag)
    .with_wine_question_score(params[:wine_question_score_id])
    .order(created_at: :desc).limit(5)
end

Comments

0

You can do the following:

condition = {:tag => "A"} if condition A
condition = {:tag => "B"} if condition B

Post
   .where(condition)
   .where(:user_id => 1)
   .order(....)

2 Comments

So the best way is to separate the query into two?
I would determine the condition first and then use it to create the query. This way you could even separate out the computation of condition in a separate method.
0

you have to use scope :

scope :my_scope, -> (variable) { where(some: vatiable) if my_condition }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.