Carriles 5: consulta O ActiveRecord

Respuestas:

228

La capacidad de encadenar orcláusula junto con wherecláusula en ActiveRecordconsulta estará disponible en Rails 5 . Consulte la discusión relacionada y la solicitud de extracción .

Por lo tanto, podrá hacer las siguientes cosas en Rails 5 :

Para obtener un postcon id1 o 2:

Post.where('id = 1').or(Post.where('id = 2'))

Algunos otros ejemplos:

(A && B) || C:

    Post.where(a).where(b).or(Post.where(c))

(A || B) && C:

    Post.where(a).or(Post.where(b)).where(c)
KM Rakibul Islam
fuente
3
¿Cómo puedo obtener (A || B) && (C || D). Probé Post.where (a) .or (Post.where (b)). Where (c) .or (Post.where (d)) pero produce como: (A || B) && C || D
Imran Ahmad
3
@Imran creo que sería Post.where(a).or(Post.where(b)).where(Post.where(c).or(Post.where(d)))esto debe crear (a || b) && (c || d)
engineersmnky
1
@Imran Esto no parece funcionar para mí: lo entiendoArgumentError: Unsupported argument type: #<MyModel::ActiveRecord_Relation:0x00007f8edbc075a8> (MyModel::ActiveRecord_Relation)
Suan
5
El equivalente a .or que toma una relación y produce un y es .merge. (A || B) && (C || D) puede ser producido por Correos donde (a). O (Publicar.donde (b)). Fusionar (Publicar.donde (c) .o (Publicar.donde (d )))
Siim Liiser
2
@MathieuJ. Es ActiveRecord :: Relation # merge. api.rubyonrails.org/classes/ActiveRecord/…
Siim Liiser
13

No necesitamos esperar a que los rieles 5 utilicen esta ORconsulta. También podemos usarlo con rails 4.2.3. Hay un puerto trasero aquí .

Gracias a Eric-Guo por gem where-or , ahora podemos agregar esta ORfuncionalidad >= rails 4.2.3también usando esta gema.

Dipak Gupta
fuente
6

(Solo una adición a la respuesta de KM Rakibul Islam).

Al usar ámbitos, el código puede volverse más bonito (dependiendo de los ojos que miran):

scope a,      -> { where(a) }
scope b,      -> { where(b) }

scope a_or_b, -> { a.or(b) }
Nicolás
fuente
5

Necesitaba hacer un (A && B) || (C && D) || (E && F)

Pero en el 5.1.4estado actual de Rails , esto es demasiado complicado de lograr con la cadena o de Arel. Pero todavía quería usar Rails para generar la mayor cantidad posible de consultas.

Así que hice un pequeño truco:

En mi modelo creé un método privado llamado sql_where:

private
  def self.sql_where(*args)
    sql = self.unscoped.where(*args).to_sql
    match = sql.match(/WHERE\s(.*)$/)
    "(#{match[1]})"
  end

A continuación, en mi alcance, creé una matriz para contener los OR

scope :whatever, -> {
  ors = []

  ors << sql_where(A, B)
  ors << sql_where(C, D)
  ors << sql_where(E, F)

  # Now just combine the stumps:
  where(ors.join(' OR '))
}

Que producirá el resultado de la consulta se esperaba: SELECT * FROM `models` WHERE ((A AND B) OR (C AND D) OR (E AND F)).

Y ahora puedo combinar esto fácilmente con otros alcances, etc. sin ningún quirófano incorrecto.

La belleza es que mi sql_where toma argumentos normales de cláusula where: sql_where(name: 'John', role: 'admin')generará (name = 'John' AND role = 'admin').

mtrolle
fuente
Creo que puede usarlo .mergecomo equivalente de && y construir un árbol adecuado para capturar sus parens. Algo como ... (scopeA.merge(scopeB)).or(scopeC.merge(scopeD)).or(scopeE.merge(scopeF)), suponiendo que cada uno de los ámbitos se parezca aModel.where(...)
nar8789
Mira esto antes de usar merge - github.com/rails/rails/issues/33501
Aarthi
1

Rails 5 tiene capacidad para orcláusula con where. Por ejemplo.

User.where(name: "abc").or(User.where(name: "abcd"))
Foram Thakral
fuente