Rails Arel,从同一列中选择但是多个值

时间:2014-02-10 23:07:56

标签: mysql ruby-on-rails ruby arel

我有一张名为cherry_picker_cherries的表格。这些记录多态地属于名为Pickable的模型。它们可以在表格中有多个条目用于相同的选择,但它始终具有不同的cherry_pick_id

我的问题:我有一个名为Favorite Tree的樱桃选择和一个名为Favorite Fruit的单独的cherry_pick。我希望能够通过多个樱桃选择来过滤一系列的选择。例如,我想查找所有具有cherry最喜欢的树并且有orange的收藏果实的可摘食物。

我可以使用以下方法以可怕的方式完成此任务:

CherryPicker::Cherry.find_by_sql("SELECT * from cherry_picker_cherries t1 JOIN cherry_picker_cherries t2 USING(pickable_id) WHERE t1.value = 'orange'AND t2.value = 'cherry'")

然而,这种方法存在几个缺点。它不能处理更多的Cherry Picks,现在只有两个。此外,结果集转换为数组,但我需要返回一个ActiveRecord关系,以便我可以继续将方法链接到它上面。

我试图看看AREL是否有某种解决方案来生成一个与上述相符的简单查询,但我陷入了死胡同。循环遍历参数的哈希很容易,允许你可以过滤2,3,4或X个樱桃选择。

甚至可以在AREL,rails或其他任何地方使用它吗?

2 个答案:

答案 0 :(得分:0)

你可以试一试吗?

CherryPicker::Cherry.joins("join cherry_picker_cherries t2 using(pickable_id)").where("cherry_picker_cherries.value = ? and t2.value = ?", 'orange', 'cherry')

如果这样做,你可以迭代你想要加入的果实,并用增量变量名称(t3,t4 ......等)做另一个joins,因为在需要之前不会执行sql查询。

以下是如何多次连接同一个表:

arr = ['orange', 'cherry', 'apple']

query = CherryPicker::Cherry.joins("join cherry_picker_cherries t0 using(pickable_id)")

arr.drop(2).each_with_index do |e,i|
  query = query.joins("join cherry_picker_cherries t#{i+1} using(pickable_id)")
end

您也可以这样做来生成where

如果您可以建议编辑,那将是很好的,所以我可以改进这篇文章,因为如果没有你的表,它真的很难测试=)

答案 1 :(得分:0)

以下解决方案实际上是更大问题的一部分,但我确实设法使用您给我的点点滴滴使其工作:

def self.with_cherries(params={})
  # See also: http://stackoverflow.com/questions/21689795/rails-arel-select-from-same-column-but-multiple-values
  result = all
  if (params.keys.length > 1)
    # filtering by 2 or more cherry picks
    result = result.joins(:cherries).where("cherry_picker_cherries.pickable_type = ?", self.name)

    # Setup the complicated joins
    params.each_with_index do |cherry_pick_and_value, index|
      result = result.joins("JOIN cherry_picker_cherries t#{index} USING(pickable_id)") unless index.zero?
    end

    my_query_string = ""
    params.values.each_with_index do |k, i|
      if i.zero?
        my_query_string << "cherry_picker_cherries.value = ?"
      else
        my_query_string << " AND t#{i}.value = ?"
      end
    end
    sanitized_string = self.sanitize_sql_array([my_query_string, params.values].flatten)
    result = result.where(sanitized_string).references(:cherries)
  else
    if params.present? && (params.keys.length == 1)
      # Only filtering by a single cherry pick
      result = result.with_cherry(params.first.first, params.first.last)
    end
  end
  result
end