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


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


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

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


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


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.


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


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

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 .


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}

6

Simplemente agregue un OR en las condiciones

Model.find(:all, :conditions => ["column = ? OR other_column = ?",value, other_value])

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.


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

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)

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


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.