0

I have a Listing model which has a category column and size column. For each category I have an array of sizes. I want to return only the Listings in each category that correspond to the size arrays. (I also have an array of designers as a condition with params[:designer].)

Params hash:

params[:category] => ['tops', 'bottoms', 'outerwear', 'footwear']
params['tops'] => ['M', 'L']
params['bottoms'] => []
params['outerwear'] => ['XL']
params['footwear'] => ['11', '12']

I've created a loop to do this:

@listings = []
params[:category].each do |category|
  @listings += Listing.where(category: category, size: params[category], designer: params[:designer], sold: nil).includes(:photos).page(params[:category_page]).per(@perpage)
end

But I need it to be all in one query since I'm using the kaminari gem (the .page call) to paginate it.

3 Answers 3

1

You could pass an array to where:

@listings = Listing.where(category: params[:category], s...
Sign up to request clarification or add additional context in comments.

1 Comment

I may be missing something but I don't think this addresses finding different sizes in different categories.
1

I ended up using Arel which is pretty good. Arel lets you build up whatever query you want and then call it on Model.where(). It's kind of complicated but was the only solution I found that worked.

t = Listing.arel_table
query = t[:category].eq('rooney')
params[:category].each do |category|
  if params[category]
    params[category].each do |size|
      query = query.or(t[:category].eq(category).and(t[:size].eq(size)))
    end
  end
end
dquery = t[:designer].eq('rooney')
params[:designer].each do |designer|
  dquery = dquery.or(t[:designer].eq(designer))
end
query = query.and(dquery)
@listings = Listing.where(query).includes(:photos).page(params[:category_page]).per(@perpage)

EDIT:

The designer query can be simplified using .eq_any().

t = Listing.arel_table
query = t[:category].eq('rooney')
params[:category].each do |category|
  if params[category]
    params[category].each do |size|
      query = query.or(t[:category].eq(category).and(t[:size].eq(size)))
    end
  end
end
dquery = t[:designer].eq_any(params[:designer])
query = query.and(dquery)
@listings = Listing.where(query).includes(:photos).page(params[:category_page]).per(@perpage)

Comments

0

Transform it to a IN query:

Listing.where(designer: params[:designer]).where("category IN (?)", params[:category])

Update:

seems above code is not what op wanted, so I changed it a bit. If I understand correctly you need to generate one big query with lots of ORs in it. You can do that without going to arel:

params[:category] = ['tops', 'bottoms', 'outerwear', 'footwear']
params['tops'] = ['M', 'L']
params['bottoms'] = []
params['outerwear'] = ['XL']
params['footwear'] = ['11', '12']

query = params[:category].map do |category|
  str = "(category = ?"
  str += " AND size IN (?)" if params[category].any?
  str += ")"
  str
end

scope = Listing.where(query.join(" OR "), *params[:category].map { |cat| [cat, params[cat].any? ? params[cat] : nil] }.flatten(1).compact)
scope = scope.where(designer: params[:designer], sold: nil)
scope = scope.page(params[:per_page]).per(@page)

It generates this query (before pagination):

2.0.0-p247 :095 > scope.to_sql
 => "SELECT \"listings\".* FROM \"listings\"  WHERE \"listings\".\"designer\" IS NULL  AND \"listings\".\"sold\" IS NULL AND ((category = 'tops' AND size IN ('M','L')) OR (category = 'bottoms') OR (category = 'outerwear' AND size IN ('XL')) OR (category = 'footwear' AND size IN ('11','12')))"

1 Comment

I may be missing something but I don't think this addresses finding different sizes in different categories.

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.