ActiveRecord ODER Abfrage

180

Wie führen Sie eine ODER-Abfrage in Rails 3 ActiveRecord durch? Alle Beispiele, die ich finde, haben nur UND-Abfragen.

Bearbeiten: Die OR- Methode ist seit Rails 5 verfügbar. Siehe ActiveRecord :: QueryMethods

pho3nixf1re
quelle
7
Lernen Sie SQL. ActiveRecord macht es einfach, Dinge zum Laufen zu bringen, aber Sie müssen immer noch wissen, was Ihre Abfragen tun, sonst kann Ihr Projekt möglicherweise nicht skaliert werden. Ich dachte auch, ich könnte durchkommen, ohne mich jemals mit SQL befassen zu müssen, und ich habe mich sehr geirrt.
Ryan0
1
@ Ryan0, du hast so recht. Wir können ausgefallene Edelsteine ​​und andere Dinge verwenden, aber wir sollten uns darüber im Klaren sein, was diese Edelsteine ​​im Inneren tun und was die zugrunde liegende Technologie ist, und möglicherweise die zugrunde liegenden Technologien ohne die Hilfe des Edelsteins verwenden, falls dies erforderlich sein sollte Performance. Edelsteine ​​werden unter Berücksichtigung einer bestimmten Anzahl von Anwendungsfällen erstellt. Es kann jedoch Situationen geben, in denen einer der Anwendungsfälle in unserem Projekt unterschiedlich sein kann.
Rubyprince
2
Kann
1
Seit Rails 5 sollte IMHO die akzeptierte Antwort Greg Olsen Version sein:Post.where(column: 'something').or(Post.where(other: 'else'))
Philipp Claßen
6
Diese Frage hat ein Tag von Ruby-on-Rails-3. Warum sollte sich die akzeptierte Antwort nur auf Rails 5 beziehen?
iNulty

Antworten:

109

Verwenden Sie ARel

t = Post.arel_table

results = Post.where(
  t[:author].eq("Someone").
  or(t[:title].matches("%something%"))
)

Das resultierende SQL:

ree-1.8.7-2010.02 > puts Post.where(t[:author].eq("Someone").or(t[:title].matches("%something%"))).to_sql
SELECT     "posts".* FROM       "posts"  WHERE     (("posts"."author" = 'Someone' OR "posts"."title" LIKE '%something%'))
Dan McNevin
quelle
Fühlt sich ein bisschen chaotisch an, aber zumindest schreibe ich kein SQL, was sich einfach falsch anfühlt! Ich werde mich mehr mit Arel befassen müssen.
Pho3nixf1re
54
Ich kann nicht glauben, wie viel eleganter Sequel ist
Macario
3
An SQL ist nichts auszusetzen. Was schief gehen kann, ist, wie wir die SQL-Zeichenfolge SQL Injection erstellen . Daher müssen wir die Benutzereingaben bereinigen, bevor wir sie einer SQL-Abfrage zur Verfügung stellen, die für die Datenbank ausgeführt werden muss. Dies wird von ORMs erledigt , und diese haben die Randfälle behandelt, die wir tendenziell übersehen. Daher ist es immer ratsam, ORM zum Erstellen von SQL-Abfragen zu verwenden.
Rubyprince
5
Ein weiteres Problem mit SQL ist, dass es nicht datenbankunabhängig ist. Zum Beispiel matcheswird LIKEfür SQLite (standardmäßig ohne ILIKEBerücksichtigung der Groß- und Kleinschreibung) und auf Postgres (benötigt explizite Groß- und Kleinschreibung wie Operator) erzeugt.
Amöbe
1
@ToniLeigh ActiveRecord verwendet SQL unter der Haube. Es ist nur eine Syntax zum Schreiben von SQL-Abfragen, die die SQL-Bereinigung behandelt und Ihre Abfragen DB-unabhängig macht. Der einzige Aufwand besteht darin, dass Rails die Syntax in eine SQL-Abfrage konvertiert. Und es ist nur eine Frage von Millisekunden. Natürlich sollten Sie die leistungsstärkste SQL-Abfrage schreiben und versuchen, sie in die ActiveRecord-Syntax zu konvertieren. Wenn SQL nicht in der ActiveRecord-Syntax dargestellt werden kann, sollten Sie sich für einfaches SQL entscheiden.
Rubyprince
208

Wenn Sie einen ODER-Operator für den Wert einer Spalte verwenden möchten, können Sie ein Array an übergeben, .whereund ActiveRecord verwendet IN(value,other_value):

Model.where(:column => ["value", "other_value"]

Ausgänge:

SELECT `table_name`.* FROM `table_name` WHERE `table_name`.`column` IN ('value', 'other_value')

Dies sollte das Äquivalent von a ORauf einer einzelnen Spalte erreichen

Deadkarma
quelle
13
Dies ist die sauberste "Rails Way" Antwort, hätte akzeptiert werden sollen
Koonse
10
Dank @koonse handelt es sich nicht gerade um eine ODER-Abfrage, aber für diese Situation wird das gleiche Ergebnis erzielt .
Deadkarma
Hilfe, wenn du kannst - stackoverflow.com/questions/15517286/…
Pratik Bothra
79
Diese Lösung ist kein Ersatz für OR, da Sie auf diese Weise nicht zwei verschiedene Spalten verwenden können.
Fotanus
151

in Rails 3 sollte es sein

Model.where("column = ? or other_column = ?", value, other_value)

Dies schließt auch Raw SQL ein, aber ich glaube nicht, dass es in ActiveRecord eine Möglichkeit gibt, eine ODER-Operation durchzuführen. Ihre Frage ist keine Noob-Frage.

Rubyprince
quelle
40
Selbst wenn es rohes SQL gibt - diese Aussage sieht für mich viel klarer aus als Arel. Vielleicht sogar DRYer.
Jibiel
6
Wenn Sie andere Tabellenverknüpfungen verketten müssen, die möglicherweise Spalten mit denselben Spaltennamen haben, sollten Sie sie wie Page.where("pages.column = ? or pages.other_column = ?", value, other_value)
folgt verwenden
1
Und das schlägt fehl, wenn die Abfrage die Tabellen aliasisiert. Dann scheint Arel.
DGM
58

Eine aktualisierte Version von Rails / ActiveRecord unterstützt diese Syntax möglicherweise nativ. Es würde ähnlich aussehen wie:

Foo.where(foo: 'bar').or.where(bar: 'bar')

Wie in dieser Pull-Anfrage angegeben, https://github.com/rails/rails/pull/9052

Im Moment funktioniert es großartig, einfach bei Folgendem zu bleiben:

Foo.where('foo= ? OR bar= ?', 'bar', 'bar')

Update: Laut https://github.com/rails/rails/pull/16052 wird die orFunktion in Rails 5 verfügbar sein

Update: Die Funktion wurde mit dem Rails 5-Zweig zusammengeführt

Christian Fazzini
quelle
2
orist jetzt verfügbar Rails 5, kann jedoch nicht auf diese Weise implementiert werden, da erwartet wird, dass 1 Argument übergeben wird. Es erwartet ein Arel-Objekt. Siehe die akzeptierte Antwort
El'Magnifico
2
Die orMethode in ActiveRecord funktioniert, wenn Sie sie direkt verwenden. Sie kann jedoch Ihre Erwartungen brechen, wenn sie in einem Bereich verwendet wird, der dann verkettet wird.
Jeremy List
Was @JeremyList gesagt hat, ist genau richtig. Das hat mich hart gebissen.
Courtsimas
23

Schienen 5 werden mit einer orMethode geliefert. (l Tinte zur Dokumentation )

Diese Methode akzeptiert ein ActiveRecord::RelationObjekt. z.B:

User.where(first_name: 'James').or(User.where(last_name: 'Scott'))
Santhosh
quelle
14

Wenn Sie Arrays als Argumente verwenden möchten, funktioniert der folgende Code in Rails 4:

query = Order.where(uuid: uuids, id: ids)
Order.where(query.where_values.map(&:to_sql).join(" OR "))
#=> Order Load (0.7ms)  SELECT "orders".* FROM "orders" WHERE ("orders"."uuid" IN ('5459eed8350e1b472bfee48375034103', '21313213jkads', '43ujrefdk2384us') OR "orders"."id" IN (2, 3, 4))

Weitere Informationen: ODER-Abfragen mit Arrays als Argumente in Rails 4 .

Rafał Cieślak
quelle
9
Oder dies: Order.where(query.where_values.inject(:or))Arel den ganzen Weg benutzen.
Cameron Martin
> ODER Abfragen mit Arrays als Argumente in Rails 4. Nützlicher Link! Vielen Dank!
Zeke Fast
7

Das MetaWhere- Plugin ist absolut erstaunlich.

Mischen Sie einfach ORs und ANDs, schließen Sie sich den Bedingungen für jede Zuordnung an und geben Sie sogar OUTER JOINs an!

Post.where({sharing_level: Post::Sharing[:everyone]} | ({sharing_level: Post::Sharing[:friends]} & {user: {followers: current_user} }).joins(:user.outer => :followers.outer}
Herzog
quelle
6

Fügen Sie einfach ein ODER in die Bedingungen ein

Model.find(:all, :conditions => ["column = ? OR other_column = ?",value, other_value])
Toby Hede
quelle
4
Dies ist eher die Syntax für Rails 2, und ich muss mindestens einen Teil eines SQL-Strings schreiben.
Pho3nixf1re
3

Ich habe gerade dieses Plugin aus der Client-Arbeit extrahiert , mit der Sie Bereiche mit z .or.. Post.published.or.authored_by(current_user). Squeel (neuere Implementierung von MetaSearch) ist ebenfalls großartig, lässt Sie jedoch keine ODER-Bereiche zu, sodass die Abfragelogik etwas redundant werden kann.

Woahdae
quelle
3

Mit Rails + Arel ein klarerer Weg:

# Table name: messages
#
# sender_id:    integer
# recipient_id: integer
# content:      text

class Message < ActiveRecord::Base
  scope :by_participant, ->(user_id) do
    left  = arel_table[:sender_id].eq(user_id)
    right = arel_table[:recipient_id].eq(user_id)

    where(Arel::Nodes::Or.new(left, right))
  end
end

Produziert:

$ Message.by_participant(User.first.id).to_sql 
=> SELECT `messages`.* 
     FROM `messages` 
    WHERE `messages`.`sender_id` = 1 
       OR `messages`.`recipient_id` = 1
itsnikolay
quelle
3

Sie könnten es tun wie:

Person.where("name = ? OR age = ?", 'Pearl', 24)

oder eleganter, installiere rails_or gem und mache es wie folgt :

Person.where(:name => 'Pearl').or(:age => 24)
Khiav Reoy
quelle
-2

Ich möchte hinzufügen, dass dies eine Lösung ist, um mehrere Attribute eines ActiveRecord zu durchsuchen. Schon seit

.where(A: param[:A], B: param[:B])

sucht nach A und B.

Tomás Gaete
quelle
-4
Book.where.any_of(Book.where(:author => 'Poe'), Book.where(:author => 'Hemingway')
Matthew Rigdon
quelle
2
Fügen Sie einige Erklärungen zu Ihrer Antwort hinzu
kvorobiev
1
Ich glaube, Matthew bezog sich auf das Juwel activerecord_any_of, das diese Syntax unterstützt.
DannyB
2
Wenn ja, danke @DannyB. Die Antwort muss ihren Kontext erklären.
Sebastialonso