Rails 5: ActiveRecord OU requête


Réponses:


228

La possibilité de chaîner la orclause avec la whereclause dans la ActiveRecordrequête sera disponible dans Rails 5 . Voir la discussion associée et la demande d'extraction .

Ainsi, vous pourrez effectuer les opérations suivantes dans Rails 5 :

Pour obtenir un postavec id1 ou 2:

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

Quelques autres exemples:

(A && B) || C:

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

(A || B) && C:

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

3
Comment puis-je obtenir (A || B) && (C || D). J'ai essayé Post.where (a) .or (Post.where (b)). Where (c) .or (Post.where (d)) mais il produit comme: (A || B) && C || D
Imran Ahmad

3
@Imran je crois que ce serait que Post.where(a).or(Post.where(b)).where(Post.where(c).or(Post.where(d)))cela devrait créer (a || b) && (c || d)
engineermnky

1
@Imran Cela ne semble pas fonctionner pour moi: je reçoisArgumentError: Unsupported argument type: #<MyModel::ActiveRecord_Relation:0x00007f8edbc075a8> (MyModel::ActiveRecord_Relation)
Suan

5
L'équivalent de .or qui prend une relation et produit un et est .merge. (A || B) && (C || D) peut être produit par Post.where (a) .ou (Post.where (b)). Merge (Post.where (c) .or (Post.where (d )))
Siim Liiser

2
@MathieuJ. C'est ActiveRecord :: Relation # merge. api.rubyonrails.org/classes/ActiveRecord/…
Siim Liiser

13

Nous n'avons pas besoin d'attendre que les rails 5 utilisent cette ORrequête. Nous pouvons également l'utiliser avec rails 4.2.3. Il y a un backport ici .

Merci à Eric-Guo pour la gemme où-ou , maintenant nous pouvons ajouter cette ORfonctionnalité en >= rails 4.2.3utilisant également cette gemme.


6

(Juste un ajout à la réponse de KM Rakibul Islam.)

En utilisant des étendues, le code peut devenir plus joli (selon les yeux qui regardent):

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

scope a_or_b, -> { a.or(b) }

5

J'avais besoin de faire un (A && B) || (C && D) || (E && F)

Mais dans 5.1.4l'état actuel de Rails , cela est trop compliqué à accomplir avec la chaîne ou Arel. Mais je voulais toujours utiliser Rails pour générer autant de requêtes que possible.

J'ai donc fait un petit hack:

Dans mon modèle, j'ai créé une méthode privée appelée sql_where:

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

Ensuite, dans mon champ d'application, j'ai créé un tableau pour contenir les 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 '))
}

Ce qui produira le résultat de la requête prévue: SELECT * FROM `models` WHERE ((A AND B) OR (C AND D) OR (E AND F)).

Et maintenant, je peux facilement combiner cela avec d'autres portées, etc. sans aucun OR erroné.

La beauté étant que mon sql_where prend des arguments normaux de clause where: sql_where(name: 'John', role: 'admin')générera (name = 'John' AND role = 'admin').


Je pense que vous pouvez utiliser .mergecomme équivalent de &&, et construire un arbre approprié pour capturer vos parents. Quelque chose comme ... (scopeA.merge(scopeB)).or(scopeC.merge(scopeD)).or(scopeE.merge(scopeF)), en supposant que chacune des portées ressemble à quelque chose commeModel.where(...)
nar8789

Vérifiez ceci avant d'utiliser la fusion - github.com/rails/rails/issues/33501
Aarthi le

1

Rails 5 a la capacité de orclause avec where. Par exemple.

User.where(name: "abc").or(User.where(name: "abcd"))
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.