将activerecord查询的循环减少为一个查询

时间:2013-10-24 21:26:35

标签: sql ruby-on-rails activerecord

我有一个列表模型,它有一个类别列和大小列。对于每个类别,我都有一系列大小。我想只返回每个类别中与大小数组对应的列表。 (我也有一系列设计师作为params [:designer]的条件。)

Params hash:

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

我创建了一个循环来执行此操作:

@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

但我需要将它全部放在一个查询中,因为我正在使用kaminari gem(.page调用)对其进行分页。

3 个答案:

答案 0 :(得分:1)

您可以将数组传递给where

@listings = Listing.where(category: params[:category], s...

答案 1 :(得分:1)

我最终使用了非常好的Arel。 Arel允许您构建所需的任何查询,然后在Model.where()上调用它。它有点复杂但是我找到的唯一解决方案。

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)

编辑:

可以使用.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)

答案 2 :(得分:0)

将其转换为IN查询:

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

更新

上面的代码似乎并不是我想要的,所以我改了一下。如果我理解正确,你需要生成一个包含大量OR的大查询。你可以做到这一点,而无需去参加:

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)

它生成此查询(在分页之前):

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')))"