ActiveRecord OR zapytanie

182

Jak wykonać zapytanie OR w Rails 3 ActiveRecord. Wszystkie znalezione przykłady mają tylko zapytania ORAZ.

Metoda Edit: OR jest dostępna od Railsów 5. Zobacz ActiveRecord :: QueryMethods

pho3nixf1re
źródło
7
Naucz się SQL. ActiveRecord ułatwia pracę, ale nadal musisz wiedzieć, co robią Twoje zapytania, w przeciwnym razie projekt może nie zostać skalowany. Pomyślałem również, że poradzę sobie bez konieczności zajmowania się SQL i bardzo się myliłem.
ryan0
1
@ ryan0, masz rację. Możemy używać fantazyjnych klejnotów i innych rzeczy, ale powinniśmy być świadomi tego, co te klejnoty robią w środku i jaka jest podstawowa technologia, i może używać podstawowych technologii bez pomocy klejnotu, jeśli zajdzie taka potrzeba, ze względu na występ. Klejnoty są tworzone z myślą o określonej liczbie zastosowań, ale mogą wystąpić sytuacje, w których jeden z przypadków użycia w naszym projekcie może być inny.
rubyprince
2
Może pomóc: ActiveRecord LUB zapytanie Notacja hash
potashin Kwietnia
1
Od czasu Rails 5, IMHO akceptowaną odpowiedzią powinna być wersja Grega Olsena:Post.where(column: 'something').or(Post.where(other: 'else'))
Philipp Claßen
6
To pytanie ma tag ruby-on-rails-3. Dlaczego zaakceptowana odpowiedź dotyczyłaby tylko Rails 5?
iNulty

Odpowiedzi:

110

Użyj ARel

t = Post.arel_table

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

Wynikowy kod 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
źródło
Czuję się trochę niechlujnie, ale przynajmniej nie piszę sql, co po prostu wydaje się złe! Będę musiał bardziej się przyjrzeć używaniu Arel.
pho3nixf1re
58
Nie mogę uwierzyć, o ile bardziej elegancki jest Sequel
Macario
3
Nie ma nic złego w SQL. To, co może się nie udać, to sposób, w jaki tworzymy ciąg SQL, a mianowicie SQL Injection . Dlatego będziemy musieli oczyścić dane wejściowe użytkownika przed przekazaniem ich do zapytania SQL, które ma zostać uruchomione w bazie danych. Zostanie to obsłużone przez ORM- y, które zajmują się skrajnymi przypadkami, które zwykle pomijamy. Dlatego zawsze zaleca się używanie ORM do tworzenia zapytań SQL.
rubyprince
5
Innym problemem związanym z SQL jest to, że nie jest on agnostyczny względem bazy danych. Na przykład matcheswyprodukuje LIKEdla sqlite (domyślnie niewrażliwa na wielkość liter) i ILIKEna postgres (wymaga jawnego niewrażliwości na wielkość liter, jak operator).
ameby,
1
@ToniLeigh ActiveRecord używa SQL pod maską. Jest to po prostu składnia do pisania zapytań SQL, która obsługuje oczyszczanie SQL i sprawia, że ​​zapytania są agnostyczne. Jedynym narzutem jest to, że Railsy konwertują składnię na zapytanie SQL. I to tylko kwestia milisekund. Oczywiście powinieneś napisać najbardziej wydajne zapytanie SQL i spróbować przekonwertować je na składnię ActiveRecord. Jeśli SQL nie może być reprezentowany w składni ActiveRecord, powinieneś wybrać zwykły SQL.
rubyprince
210

Jeśli chcesz użyć operatora OR na wartości jednej kolumny, możesz przekazać tablicę do, .wherea ActiveRecord użyje IN(value,other_value):

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

wyjścia:

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

Powinno to osiągnąć równowartość w ORjednej kolumnie

deadkarma
źródło
13
to jest najczystsza, najbardziej „
szynowa
10
Dzięki @koonse, nie jest to dokładnie zapytanie „LUB”, ale daje taki sam wynik w tej sytuacji.
deadkarma
Pomóż, jeśli możesz - stackoverflow.com/questions/15517286/ ...
Pratik Bothra
81
To rozwiązanie nie zastępuje OR, ponieważ w ten sposób nie można użyć dwóch różnych kolumn.
fotanus
154

w Railsach 3 powinno być

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

Obejmuje to również surowy sql, ale nie sądzę, aby w ActiveRecord był sposób na wykonanie operacji OR. Twoje pytanie nie jest pytaniem noobowym.

rubyprince
źródło
41
Nawet jeśli istnieje surowy sql - to stwierdzenie wygląda dla mnie o wiele jaśniej niż Arel. Może nawet bardziej SUCHA.
jibiel
6
jeśli chcesz połączyć w łańcuch, inne łączenia tabel, które mogą mieć kolumny z tymi samymi nazwami kolumn, powinieneś użyć tego w ten sposób,Page.where("pages.column = ? or pages.other_column = ?", value, other_value)
rubyprince
2
A to się nie powiedzie, jeśli zapytanie utworzy aliasy tabel. Wtedy arel świeci.
DGM
59

Zaktualizowana wersja Rails / ActiveRecord może obsługiwać tę składnię natywnie. Wyglądałoby to podobnie do:

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

Jak wspomniano w tym żądaniu ściągnięcia https://github.com/rails/rails/pull/9052

Na razie po prostu trzymanie się następujących działa świetnie:

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

Aktualizacja: Według https://github.com/rails/rails/pull/16052or funkcja będzie dostępna w Rails 5

Aktualizacja: Funkcja została włączona do gałęzi Rails 5

Christian Fazzini
źródło
2
orjest teraz dostępny w Railsach 5, ale nie może być zaimplementowany w ten sposób, ponieważ oczekuje przekazania 1 argumentu. Oczekuje obiektu Arel. Zobacz zaakceptowaną odpowiedź
El'Magnifico
2
orMetoda ActiveRecord działa, jeśli używasz go bezpośrednio, lecz może złamać swoje oczekiwania, jeśli jest stosowany w zakresie, który jest następnie przykuty.
Jeremy List
To, co powiedział @JeremyList, jest na miejscu. To mnie mocno ugryzło.
courimas
23

Rails 5 zawiera ormetodę. (l atrament do dokumentacji )

Ta metoda akceptuje ActiveRecord::Relationobiekt. na przykład:

User.where(first_name: 'James').or(User.where(last_name: 'Scott'))
Santhosh
źródło
14

Jeśli chcesz używać tablic jako argumentów, następujący kod działa w Railsach 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))

Więcej informacji: LUB zapytania z tablicami jako argumentami w Railsach 4 .

Rafał Cieślak
źródło
9
Albo tak: Order.where(query.where_values.inject(:or))używać arel do końca.
Cameron Martin,
> LUB zapytania z tablicami jako argumentami w Railsach 4. Przydatne łącze! Dzięki!
Zeke Fast
7

MetaWhere Wtyczka jest zupełnie niesamowite.

Łatwo mieszaj OR i AND, dołącz warunki w dowolnej asocjacji, a nawet określ OUTER JOIN!

Post.where({sharing_level: Post::Sharing[:everyone]} | ({sharing_level: Post::Sharing[:friends]} & {user: {followers: current_user} }).joins(:user.outer => :followers.outer}
Książę
źródło
6

Po prostu dodaj OR w warunkach

Model.find(:all, :conditions => ["column = ? OR other_column = ?",value, other_value])
Toby Hede
źródło
4
Jest to bardziej składnia Rails 2 i wymaga ode mnie napisania przynajmniej części łańcucha sql.
pho3nixf1re,
4

Możesz to zrobić tak:

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

lub bardziej eleganckie, zainstaluj rails_or gem i zrób to tak:

Person.where(:name => 'Pearl').or(:age => 24)
khiav reoy
źródło
3

Właśnie wyodrębniłem tę wtyczkę z pracy klienta, która pozwala łączyć zakresy z .or., np. Post.published.or.authored_by(current_user). Squeel (nowsza implementacja MetaSearch) jest również świetny, ale nie pozwala na zakresy LUB, więc logika zapytań może stać się nieco zbędna.

Woahdae
źródło
3

Z railami + arel, bardziej przejrzysty sposób:

# 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

Produkuje:

$ Message.by_participant(User.first.id).to_sql 
=> SELECT `messages`.* 
     FROM `messages` 
    WHERE `messages`.`sender_id` = 1 
       OR `messages`.`recipient_id` = 1
itsnikolay
źródło
-4
Book.where.any_of(Book.where(:author => 'Poe'), Book.where(:author => 'Hemingway')
Matthew Rigdon
źródło
2
Dodaj kilka wyjaśnień do swojej odpowiedzi
kvorobiev
1
Myślę, że Matthew odnosił się do gemu activerecord_any_of, który dodaje obsługę tej składni.
DannyB
2
Jeśli to prawda, dzięki @DannyB. Odpowiedź musi wyjaśniać jej kontekst.
Sebastialonso,
-4

Chciałbym dodać, że jest to rozwiązanie do wyszukiwania wielu atrybutów ActiveRecord. Od

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

wyszuka A i B.

Tomás Gaete
źródło