Schienen 5: ActiveRecord ODER Abfrage

Antworten:

228

Die Möglichkeit, orKlauseln zusammen mit whereKlauseln in ActiveRecordAbfragen zu verketten, ist in Rails 5 verfügbar . Siehe die zugehörige Diskussion und die Pull-Anfrage .

In Rails 5 können Sie also Folgendes tun :

So erhalten Sie eine postmit id1 oder 2:

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

Einige andere Beispiele:

(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
quelle
3
Wie kann ich (A || B) && (C || D) bekommen? Ich habe versucht, Post.where (a) .oder (Post.where (b)). Where (c) .oder (Post.where (d)), aber es erzeugt als: (A || B) && C || D
Imran Ahmad
3
@ Imran Ich glaube, es wäre, dass Post.where(a).or(Post.where(b)).where(Post.where(c).or(Post.where(d)))dies (a || b) && (c || d)
Engineersmnky
1
@ Imran Das scheint nicht für mich zu funktionieren: Ich bekommeArgumentError: Unsupported argument type: #<MyModel::ActiveRecord_Relation:0x00007f8edbc075a8> (MyModel::ActiveRecord_Relation)
Suan
5
Das Äquivalent zu .oder, das eine Beziehung aufnimmt und ein und erzeugt, ist .merge. (A || B) && (C || D) kann von Post.where (a) .oder (Post.where (b)). Merge (Post.where (c) .oder (Post.where (d) erstellt werden )))
Siim Liiser
2
@ MathieuJ. Es ist ActiveRecord :: Relation # Merge. api.rubyonrails.org/classes/ActiveRecord/…
Siim Liiser
13

Wir müssen nicht auf Schienen 5 warten, um diese ORAbfrage zu verwenden. Wir können es auch mit verwenden rails 4.2.3. Es gibt eine Rückportierung hier .

Vielen Dank an Eric-Guo für das Juwel wo-oder . Jetzt können wir diese ORFunktionalität hinzufügen, >= rails 4.2.3indem wir dieses Juwel auch verwenden.

Dipak Gupta
quelle
6

(Nur eine Ergänzung zur Antwort von KM Rakibul Islam.)

Bei Verwendung von Bereichen kann der Code hübscher werden (abhängig von den Augen):

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

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

Ich musste eine machen (A && B) || (C && D) || (E && F)

Aber in Rails 5.1.4 'aktuellem Zustand ist dies zu kompliziert, um es mit der Arel-Kette zu erreichen. Aber ich wollte immer noch Rails verwenden, um so viel Abfrage wie möglich zu generieren.

Also habe ich einen kleinen Hack gemacht:

In meinem Modell habe ich eine private Methode namens erstellt sql_where:

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

Als nächstes habe ich in meinem Bereich ein Array erstellt, das die OPs enthält

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

Welches wird das erwartete Abfrageergebnis erzeugen : SELECT * FROM `models` WHERE ((A AND B) OR (C AND D) OR (E AND F)).

Und jetzt kann ich dies problemlos mit anderen Bereichen usw. kombinieren, ohne dass es zu falschen OPs kommt.

Das Schöne ist, dass mein sql_where normale where-Klausel-Argumente verwendet: sql_where(name: 'John', role: 'admin')generiert (name = 'John' AND role = 'admin').

mtrolle
quelle
Ich denke, Sie können .mergedas Äquivalent von && verwenden und einen geeigneten Baum erstellen, um Ihre Eltern zu erfassen. Etwas wie ... (scopeA.merge(scopeB)).or(scopeC.merge(scopeD)).or(scopeE.merge(scopeF)), vorausgesetzt, jeder der Bereiche sieht Model.where(...)
ungefähr
Überprüfen Sie dies, bevor Sie merge verwenden - github.com/rails/rails/issues/33501
Aarthi
1

Schienen 5 hat die Fähigkeit zur orKlausel mit where. Beispielsweise.

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