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)
KM Rakibul Islam
la source
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.

Dipak Gupta
la source
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) }
Nicolas
la source
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').

mtrolle
la source
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"))
Foram Thakral
la source