travistam
travistam

Reputation: 43

How to construct where clause in ruby using if

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

Upvotes: 1

Views: 529

Answers (5)

Raghvendra Parashar
Raghvendra Parashar

Reputation: 4053

#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

Upvotes: 1

jnfjnjtj
jnfjnjtj

Reputation: 479

#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(...)

Upvotes: 1

mu is too short
mu is too short

Reputation: 434585

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.

Upvotes: 2

Oleh  Sobchuk
Oleh Sobchuk

Reputation: 3722

you have to use scope :

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

Upvotes: 0

Atri
Atri

Reputation: 5831

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(....)

Upvotes: 0

Related Questions