Rails Arel, выберите из одного столбца, но несколько значений
У меня есть стол cherry_picker_cherries
, Эти записи полиморфно принадлежат модели под названием Pickable
, Они могут иметь несколько записей в таблице для одного и того же выбора, но он всегда имеет разные cherry_pick_id
,
Моя проблема: у меня есть вишня под названием Favorite Tree
и отдельный cherry_pick называется Favorite Fruit
, Я хочу быть в состоянии отфильтровать коллекцию сборов по нескольким вишневым киркам. Например, я хочу найти все кирки, у которых есть любимое дерево 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, рельсах или чем-то еще?
2 ответа
Следующее решение на самом деле является частью более крупной проблемы, но мне удалось заставить его работать, используя кусочки, которые вы мне дали:
def self.with_cherries(params={})
# See also: http://stackru.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
Можете ли вы дать этому шанс?
CherryPicker::Cherry.joins("join cherry_picker_cherries t2 using(pickable_id)").where("cherry_picker_cherries.value = ? and t2.value = ?", 'orange', 'cherry')
Если это работает, вы можете перебрать фрукты, к которым хотите присоединиться, и сделать еще один joins
с увеличенным именем переменной (t3, t4.. и т. д.), потому что 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
s
Было бы здорово, если бы вы могли предложить правки, чтобы я мог улучшить этот пост, так как это действительно трудно проверить без ваших таблиц =)