Ich verwende Rails3, ActiveRecord
Ich frage mich nur, wie ich die Gültigkeitsbereiche mit OR -Anweisungen anstatt mit AND verketten kann.
z.B.
Person.where(:name => "John").where(:lastname => "Smith")
Das gibt normalerweise name = 'John' AND lastname = 'Smith' zurück, aber ich möchte:
name = 'John' OR lastname = 'Smith'
Du würdest
Person.where('name=? OR lastname=?', 'John', 'Smith')
Im Moment gibt es keine andere OR -Unterstützung durch die neue AR3-Syntax (d. H. Ohne Verwendung eines Drittanbieter-Gems).
Verwenden Sie ARel
t = Person.arel_table
results = Person.where(
t[:name].eq("John").
or(t[:lastname].eq("Smith"))
)
Laut dieser Pull-Anfrage unterstützt Rails 5 nun folgende Syntax für die Verkettung von Abfragen:
Post.where(id: 1).or(Post.where(id: 2))
Es gibt auch einen Backport der Funktionalität in Rails 4.2 über dieses Juwel.
Update für Rails4
erfordert keine Edelsteine von Drittanbietern
a = Person.where(name: "John") # or any scope
b = Person.where(lastname: "Smith") # or any scope
Person.where([a, b].map{|s| s.arel.constraints.reduce(:and) }.reduce(:or))\
.tap {|sc| sc.bind_values = [a, b].map(&:bind_values) }
Alte Antwort
erfordert keine Edelsteine von Drittanbietern
Person.where(
Person.where(:name => "John").where(:lastname => "Smith")
.where_values.reduce(:or)
)
Sie müssen lediglich die Array-Syntax für same column OR - Abfragen bereitstellen, um den Blick zu erleichtern.
Person.where(name: ["John", "Steve"])
Falls jemand nach einer aktualisierten Antwort auf diese Frage sucht, scheint es eine vorhandene Pull-Anforderung zu geben, um dies in Rails zu erhalten: https://github.com/Rails/rails/pull/9052 .
Dank des @ j-mcnally-Affenpatches für ActiveRecord ( https://Gist.github.com/j-mcnally/250eaaceef234dd8971b ) können Sie Folgendes tun:
Person.where(name: 'John').or.where(last_name: 'Smith').all
Noch wertvoller ist die Möglichkeit, Bereiche mit OR
zu verketten:
scope :first_or_last_name, ->(name) { where(name: name.split(' ').first).or.where(last_name: name.split(' ').last) }
scope :parent_last_name, ->(name) { includes(:parents).where(last_name: name) }
Dann können Sie alle Personen mit Vor- oder Nachnamen oder deren Eltern mit Nachnamen finden
Person.first_or_last_name('John Smith').or.parent_last_name('Smith')
Nicht das beste Beispiel dafür, sondern nur die Frage zu beantworten.
Sie können auch MetaWhere gem verwenden, um Ihren Code nicht mit SQL-Informationen zu verwechseln:
Person.where((:name => "John") | (:lastname => "Smith"))
Eine aktualisierte Version von Rails/ActiveRecord kann diese Syntax nativ unterstützen. Es würde ähnlich aussehen:
Foo.where(foo: 'bar').or.where(bar: 'bar')
Wie in dieser Pull-Anfrage angegeben, https://github.com/Rails/rails/pull/9052
Für den Moment ist es einfach toll, bei den folgenden Arbeiten zu bleiben:
Foo.where('foo= ? OR bar= ?', 'bar', 'bar')
Dies wäre ein guter Kandidat für MetaWhere, wenn Sie Rails 3.0+ verwenden, aber es funktioniert nicht auf Rails 3.1. Vielleicht möchten Sie stattdessen squeel ausprobieren. Es wurde vom selben Autor erstellt. So würden Sie eine OR basierte Kette ausführen:
Person.where{(name == "John") | (lastname == "Smith")}
Sie können UND/ODER unter vielen anderen großartigen Dingen kombinieren.
Für mich (Rails 4.2.5) funktioniert es nur so:
{ where("name = ? or name = ?", a, b) }
Schienen 4 + Scope + Arel
class Creature < ActiveRecord::Base
scope :is_good_pet, -> {
where(
arel_table[:is_cat].eq(true)
.or(arel_table[:is_dog].eq(true))
.or(arel_table[:eats_children].eq(false))
)
}
end
Ich habe versucht, benannte Bereiche mit .or und ohne Glück zu verketten, aber dies funktionierte, um etwas mit diesen Booleschen Geräten zu finden. Erzeugt SQL wie
SELECT 'CREATURES'.* FROM 'CREATURES' WHERE ((('CREATURES'.'is_cat' = 1 OR 'CREATURES'.'is_dog' = 1) OR 'CREATURES'.'eats_children' = 0))
Schienen 4
scope :combined_scope, -> { where("name = ? or name = ?", 'a', 'b') }
Wenn Sie die where-Klausel nicht manuell ausschreiben können, um die Anweisung "oder" einzuschließen (dh Sie möchten zwei Bereiche kombinieren), können Sie union verwenden:
Model.find_by_sql("#{Model.scope1.to_sql} UNION #{Model.scope2.to_sql}")
(Quelle: ActiveRecord Query Union )
Auf diese Weise werden alle Datensätze zurückgegeben, die mit einer der Abfragen übereinstimmen. Dies gibt jedoch ein Array zurück, nicht ein Feld. Wenn Sie wirklich einen Artikel zurückgeben möchten, checken Sie diesen Gist aus: https://Gist.github.com/j-mcnally/250eaaceef234dd8971b .
Dies erledigt den Job, solange Sie nichts dagegen haben, dass Affen Rails patchen.
Siehe auch diese verwandten Fragen: hier , hier und hier
Für Rails 4, basierend auf diesem Artikel und dieser Originalantwort
Person
.unscoped # See the caution note below. Maybe you want default scope here, in which case just remove this line.
.where( # Begin a where clause
where(:name => "John").where(:lastname => "Smith") # join the scopes to be OR'd
.where_values # get an array of arel where clause conditions based on the chain thus far
.inject(:or) # inject the OR operator into the arels
# ^^ Inject may not work in Rails3. But this should work instead:
.joins(" OR ")
# ^^ Remember to only use .inject or .joins, not both
) # Resurface the arels inside the overarching query
Beachten Sie die Warnung des Artikels am Ende:
Schienen 4.1+
In Rails 4.1 wird default_scope wie ein regulärer Bereich behandelt. Der Standard scope (falls vorhanden) ist im Ergebnis von where_values und .__ enthalten. inject (: oder) fügt zwischen dem Standardbereich und Ihrem .__ eine Anweisung oder eine Anweisung ein. wo ist. Das ist schlecht.
Um dies zu lösen, müssen Sie die Abfrage nur abbrechen.
Dies ist ein sehr praktischer Weg und funktioniert in Rails 5 einwandfrei:
Transaction
.where(transaction_type: ["Create", "Correspond"])
.or(
Transaction.where(
transaction_type: "Status",
field: "Status",
newvalue: ["resolved", "deleted"]
)
)
.or(
Transaction.where(transaction_type: "Set", field: "Queue")
)
der squeel
Gem bietet eine unglaublich einfache Möglichkeit, dies zu erreichen (zuvor habe ich etwas wie die Methode von @ coloradoblue verwendet):
names = ["Kroger", "Walmart", "Target", "Aldi"]
matching_stores = Grocery.where{name.like_any(names)}
Wenn Sie einen Geltungsbereich bereitstellen möchten (anstatt explizit an der gesamten Datenmenge zu arbeiten), sollten Sie Folgendes mit Rails 5 tun:
scope :john_or_smith, -> { where(name: "John").or(where(lastname: "Smith")) }
Oder:
def self.john_or_smith
where(name: "John").or(where(lastname: "Smith"))
end
Die Antwort auf die ursprüngliche Frage: Können Sie Bereiche mit 'oder' anstelle von 'verbinden und' scheint nicht zu sein '. Sie können jedoch einen vollständig anderen Bereich oder eine andere Abfrage eingeben, die den Job ausführt, oder ein anderes Framework als ActiveRecord verwenden, z. MetaWhere oder Squeel. In meinem Fall nicht nützlich
Ich bin 'oder' ein von pg_search generierter Bereich, der ein bisschen mehr tut als select, er enthält die Reihenfolge nach ASC, wodurch eine saubere Vereinigung entsteht. Ich möchte 'oder' es mit einem handgefertigten Bereich machen, der Dinge erledigt, die ich in pg_search nicht tun kann. Also musste ich es so machen.
Product.find_by_sql("(#{Product.code_starts_with('Tom').to_sql}) union (#{Product.name_starts_with('Tom').to_sql})")
Das heißt Verwandeln Sie die Bereiche in SQL, setzen Sie sie in Klammern, verbinden Sie sie miteinander und suchen Sie dann mit dem generierten SQL die Suche nach _by_sql. Es ist ein bisschen Müll, aber es funktioniert.
Nein, sagen Sie mir nicht, dass ich in pg_search "gegen: [: name,: code]" verwenden kann. Ich würde das gerne so machen, aber das 'name'-Feld ist ein Hstore, das pg_search nicht verarbeiten kann noch. Daher muss der Gültigkeitsbereich anhand des Namens von Hand erstellt und dann mit dem Gültigkeitsbereich pg_search vereinigt werden.