Rails Arel, выберите из одного столбца, но из нескольких значений

У меня есть таблица под названием cherry_picker_cherries. Эти записи полиморфно принадлежат модели под названием Pickable. У них может быть несколько записей в таблице для одного и того же выбираемого, но у него всегда разные cherry_pick_id.

Моя проблема: у меня есть вишневый выбор под названием Favorite Tree и отдельный вишневый выбор под названием 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, рельсах или чем-то еще?


person Mark Holmberg    schedule 10.02.2014    source источник
comment
Дайте мне знать, если мой код ниже работает для вас, чтобы я мог отредактировать его, чтобы он был более общим =)   -  person Abdo    schedule 11.02.2014


Ответы (2)


Можешь попробовать?

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

Вы можете сделать то же самое, чтобы сгенерировать свои wheres

Было бы здорово, если бы вы предложили правки, чтобы я мог улучшить этот пост, так как его очень сложно протестировать без ваших таблиц =)

person Abdo    schedule 10.02.2014
comment
Круто.. Теперь вы можете привести пример с несколькими вариантами вишен? - person Abdo; 11.02.2014

Следующее решение на самом деле является частью более крупной проблемы, но мне удалось заставить его работать, используя кусочки, которые вы мне дали:

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
person Mark Holmberg    schedule 12.02.2014