ActiveRecord O consulta

181

¿Cómo se hace una consulta OR en Rails 3 ActiveRecord? Todos los ejemplos que encuentro solo tienen consultas AND.

Editar: el método OR está disponible desde Rails 5. Ver ActiveRecord :: QueryMethods

pho3nixf1re
fuente
77
Aprende SQL. ActiveRecord facilita que las cosas funcionen, pero aún necesita saber qué están haciendo sus consultas, de lo contrario su proyecto podría no escalar. También pensé que podría sobrevivir sin tener que lidiar con SQL, y estaba muy equivocado al respecto.
ryan0
1
@ ryan0, tienes mucha razón. Podemos usar gemas elegantes y otras cosas, pero debemos ser conscientes de lo que estas gemas están haciendo dentro y cuál es la tecnología subyacente, y tal vez usar las tecnologías subyacentes sin la ayuda de la gema, si es necesario, por el bien de actuación. Las gemas se crean con un número específico de casos de uso en mente, pero puede haber situaciones en las que uno de los casos de uso en nuestro proyecto podría ser diferente.
rubyprince
2
Puede ser de ayuda: ActiveRecord O consulta Notación de hash
potashin
1
Desde Rails 5, en mi humilde opinión, la respuesta aceptada debería ser la versión de Greg Olsen:Post.where(column: 'something').or(Post.where(other: 'else'))
Philipp Claßen
66
Esta pregunta tiene una etiqueta de ruby-on-rails-3. ¿Por qué la respuesta aceptada se relaciona solo con Rails 5?
iNulty

Respuestas:

109

Use ARel

t = Post.arel_table

results = Post.where(
  t[:author].eq("Someone").
  or(t[:title].matches("%something%"))
)

El SQL resultante:

ree-1.8.7-2010.02 > puts Post.where(t[:author].eq("Someone").or(t[:title].matches("%something%"))).to_sql
SELECT     "posts".* FROM       "posts"  WHERE     (("posts"."author" = 'Someone' OR "posts"."title" LIKE '%something%'))
Dan McNevin
fuente
Se siente un poco desordenado, pero al menos no estoy escribiendo sql, ¡lo cual se siente mal! Voy a tener que estudiar el uso de Arel más.
pho3nixf1re
55
No puedo creer lo elegante que es Sequel
Macario
3
No hay nada malo con SQL. Lo que puede salir mal es cómo construimos la cadena SQL, es decir, la inyección SQL . Por lo tanto, tendremos que desinfectar la entrada del usuario antes de proporcionarla a una consulta SQL que debe ejecutarse en la base de datos. Esto será manejado por ORM s, y estos han estado manejando los casos límite, que tendemos a perder. Por lo tanto, siempre es aconsejable usar ORM para crear consultas SQL.
rubyprince
55
Otro problema con SQL es que no es independiente de la base de datos. Por ejemplo matches, producirá LIKEpara sqlite (mayúsculas y minúsculas por defecto) y ILIKEen postgres (necesita mayúsculas y minúsculas explícitas como operador).
amee
1
@ToniLeigh ActiveRecord está utilizando SQL bajo el capó. Es solo una sintaxis para escribir consultas SQL que maneja la desinfección de SQL y hace que sus consultas sean independientes de DB. La única sobrecarga es donde Rails convierte la sintaxis a la consulta SQL. Y es solo cuestión de milisegundos. Por supuesto, debe escribir la consulta SQL de mejor rendimiento e intentar convertirla a la sintaxis ActiveRecord. Si el SQL no se puede representar en la sintaxis de ActiveRecord, entonces debe optar por SQL simple.
rubyprince
208

Si desea usar un operador OR en el valor de una columna, puede pasar una matriz a .wherey ActiveRecord usará IN(value,other_value):

Model.where(:column => ["value", "other_value"]

salidas:

SELECT `table_name`.* FROM `table_name` WHERE `table_name`.`column` IN ('value', 'other_value')

Esto debería lograr el equivalente de un ORen una sola columna

deadkarma
fuente
13
esta es la respuesta más
clara
10
Gracias @koonse, no es exactamente una consulta 'OR', pero produce el mismo resultado para esta situación.
deadkarma
Ayuda si puedes - stackoverflow.com/questions/15517286/…
Pratik Bothra
80
Esta solución no reemplaza a OR ya que no puede usar dos columnas diferentes de esta manera.
Fotanus
152

en Rails 3, debería ser

Model.where("column = ? or other_column = ?", value, other_value)

Esto también incluye SQL sin procesar, pero no creo que haya una forma en ActiveRecord para hacer la operación OR. Tu pregunta no es una pregunta novata.

rubyprince
fuente
41
Incluso si hay sql en bruto, esta declaración me parece mucho más clara que Arel. Tal vez incluso más seco.
jibiel
66
si necesita encadenar, otras uniones de tabla que pueden tener las columnas con los mismos nombres de columna, debe usarlo así,Page.where("pages.column = ? or pages.other_column = ?", value, other_value)
rubyprince
1
Y eso falla si la consulta alias las tablas. Entonces es cuando brilla Arel.
DGM
58

Una versión actualizada de Rails / ActiveRecord puede admitir esta sintaxis de forma nativa. Se vería similar a:

Foo.where(foo: 'bar').or.where(bar: 'bar')

Como se señaló en esta solicitud de extracción https://github.com/rails/rails/pull/9052

Por ahora, simplemente seguir con lo siguiente funciona muy bien:

Foo.where('foo= ? OR bar= ?', 'bar', 'bar')

Actualización: según https://github.com/rails/rails/pull/16052, la orfunción estará disponible en Rails 5

Actualización: la característica se ha fusionado con la rama Rails 5

Christian Fazzini
fuente
2
orestá disponible ahora Rails 5 pero no se implementa de esta manera porque espera que se pase 1 argumento. Espera un objeto Arel. Ver la respuesta aceptada
El'Magnifico
2
El ormétodo en ActiveRecord funciona si lo está usando directamente: pero puede romper sus expectativas si se usa en un ámbito que luego se encadena.
Jeremy List
Lo que dijo @JeremyList es acertado. Eso me mordió duro.
courtimas
23

Rails 5 viene con un ormétodo. (l tinta a la documentación )

Este método acepta un ActiveRecord::Relationobjeto. p.ej:

User.where(first_name: 'James').or(User.where(last_name: 'Scott'))
Santhosh
fuente
14

Si desea utilizar matrices como argumentos, el siguiente código funciona en Rails 4:

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))

Más información: O consultas con matrices como argumentos en Rails 4 .

Rafał Cieślak
fuente
9
O esto: Order.where(query.where_values.inject(:or))usar arel todo el tiempo.
Cameron Martin
> O consultas con matrices como argumentos en Rails 4. ¡Enlace útil! ¡Gracias!
Zeke Fast
7

El complemento MetaWhere es completamente sorprendente.

¡Mezcle fácilmente OR y AND, unir condiciones en cualquier asociación e incluso especificar OUTER JOIN!

Post.where({sharing_level: Post::Sharing[:everyone]} | ({sharing_level: Post::Sharing[:friends]} & {user: {followers: current_user} }).joins(:user.outer => :followers.outer}
Duque
fuente
6

Simplemente agregue un OR en las condiciones

Model.find(:all, :conditions => ["column = ? OR other_column = ?",value, other_value])
Toby Hede
fuente
44
Esta es más la sintaxis de Rails 2, y requiere que escriba al menos una parte de una cadena sql.
pho3nixf1re
3

Acabo de extraer este complemento del trabajo del cliente que le permite combinar ámbitos con .or., por ejemplo. Post.published.or.authored_by(current_user). Squeel (la implementación más reciente de MetaSearch) también es excelente, pero no le permite O los ámbitos, por lo que la lógica de consulta puede ser un poco redundante.

Woahdae
fuente
3

Con rails + arel, una forma más clara:

# Table name: messages
#
# sender_id:    integer
# recipient_id: integer
# content:      text

class Message < ActiveRecord::Base
  scope :by_participant, ->(user_id) do
    left  = arel_table[:sender_id].eq(user_id)
    right = arel_table[:recipient_id].eq(user_id)

    where(Arel::Nodes::Or.new(left, right))
  end
end

Produce:

$ Message.by_participant(User.first.id).to_sql 
=> SELECT `messages`.* 
     FROM `messages` 
    WHERE `messages`.`sender_id` = 1 
       OR `messages`.`recipient_id` = 1
itsnikolay
fuente
3

Podrías hacerlo como:

Person.where("name = ? OR age = ?", 'Pearl', 24)

o más elegante, instala rails_or gem y hazlo como:

Person.where(:name => 'Pearl').or(:age => 24)
khiav reoy
fuente
-2

Me gustaría agregar que esta es una solución para buscar múltiples atributos de un ActiveRecord. Ya que

.where(A: param[:A], B: param[:B])

buscará A y B.

Tomás Gaete
fuente
-4
Book.where.any_of(Book.where(:author => 'Poe'), Book.where(:author => 'Hemingway')
Matthew Rigdon
fuente
2
Agregue algunas explicaciones a su respuesta
kvorobiev
1
Creo que Matthew se refería a activerecord_any_of gem que agrega soporte para esta sintaxis.
DannyB
2
Si es cierto, gracias @DannyB. La respuesta debe explicar su contexto.
Sebastialonso