tl; dr
query = Order.unscoped.where(uuid: uuids, id: ids)
Order.where(query.where_values.inject(:or))
#=> Order Load (0.7ms) SELECT "orders".* FROM "orders" WHERE ("orders"."uuid" IN ('5459eed8350e1b472bfee48375034103', '21313213jkads', '43ujrefdk2384us') OR "orders"."id" IN (2, 3, 4))
Visto que o or
método não está disponível no Rails 4, é complicado realizar consultas OR com arrays como argumentos. Claro que podemos escrever SQL puro, mas existe uma maneira mais simples de fazer isso?
Meu colega de trabalho me enviou um link para uma discussão no ruby-forum.com . É onde encontramos o seguinte código:
recipe_query = Recipe.where("pastry_id = 1").where("filling_id = 1")
Recipe.where(recipe_query.where_values.join(" OR "))
No entanto, não funcionou. where_values
retorna uma matriz de Arel::Nodes::In
objetos e não podemos simplesmente juntá-los.
Há uma postagem no Stack Overflow onde o autor se aprofunda nos métodos Arel.
Category.top_level.where_values.map(&:to_sql)
=> ["`categories`.`parent_id` IS NULL"]
Oh sim, como eu poderia esquecer to_sql
? Strings em uma matriz como esta podem ser facilmente unidas, portanto, em nosso caso, o código se parece com o seguinte:
query = Order.where(uuid: uuids, id: ids)
Order.where(query.where_values.map(&:to_sql).join(" OR "))
#=> Order Load (0.7ms) SELECT "orders".* FROM "orders" WHERE ("orders"."uuid" IN ('5459eed8350e1b472bfee48375034103', '21313213jkads', '43ujrefdk2384us') OR "orders"."id" IN (2, 3, 4))
É isso aí! Onde os valores agora são unidos or
, não and
.
Mas espere! Podemos fazer melhor. Cameron Martin no StackOverflow postou uma versão um pouco mais elegante:
query = Order.where(uuid: uuids, id: ids)
Order.where(query.where_values.inject(:or))
Rails 4.1+
O Rails 4.1 trata default_scope
apenas como um escopo regular . O escopo padrão (se houver) é incluído no where_values
resultado e adicionará a instrução entre o escopo padrão e seus s. Isso é ruim.inject(:or)
or
where
Para resolver isso, você só precisa remover o escopo da consulta.
query = Order.unscoped.where(uuid: uuids, id: ids)
Order.where(query.where_values.inject(:or))
Possíveis ressalvas
Ao realizar uma consulta, os elementos da matriz são convertidos em tipos correspondentes, portanto, tome cuidado.
ids = ["5459eed8350e1b472bfee48375034103", "2", "3"]
query = Order.where(uuid: ids, id: ids)
Order.where(query.where_values.map(&:to_sql).join(" OR "))
#=> Order Load (0.6ms) SELECT "orders".* FROM "orders" WHERE ("orders"."uuid" IN ('5459eed8350e1b472bfee48375034103', '2', '3') OR "orders"."id" IN (5459, 2, 3))