我有一张名为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或其他任何地方使用它吗?
答案 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