Rails 5: ActiveRecord LUB zapytanie


Odpowiedzi:


228

Możliwość łączenia w łańcuch orklauzuli wraz z whereklauzulą ​​w ActiveRecordzapytaniu będzie dostępna w Railsach 5 . Zobacz powiązaną dyskusję i żądanie ściągnięcia .

W Rails 5 będziesz mógł więc wykonać następujące czynności :

Aby dostać postsię z id1 lub 2

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

Kilka innych przykładów:

(A && B) || DO:

    Post.where(a).where(b).or(Post.where(c))

(A || B) && C:

    Post.where(a).or(Post.where(b)).where(c)

3
Jak mogę uzyskać (A || B) && (C || D). Próbowałem Post.where (a). Lub (Post.where (b)). Where (c). Or (Post.where (d)), ale produkuje jako: (A || B) && C || D
Imran Ahmad

3
@Imran, wierzę, że Post.where(a).or(Post.where(b)).where(Post.where(c).or(Post.where(d)))powinno to stworzyć (a || b) && (c || d)
engineersmnky

1
@Imran To wydaje się nie działać dla mnie: dostajęArgumentError: Unsupported argument type: #<MyModel::ActiveRecord_Relation:0x00007f8edbc075a8> (MyModel::ActiveRecord_Relation)
Suan

5
Odpowiednik .or, który przyjmuje relację i tworzy i jest .merge. (A || B) && (C || D) mogą być tworzone przez Post.where (a). Or (Post.where (b)). Merge (Post.where (c). Or (Post.where (d )))
Siim Liiser

2
@MathieuJ. To scalanie ActiveRecord :: Relation #. api.rubyonrails.org/classes/ActiveRecord/…
Siim Liiser

13

Nie musimy czekać, aż rails 5 użyje tego ORzapytania. Możemy go również używać z rails 4.2.3. Jest backport tutaj .

Dziękuję Ericowi-Guo za klejnot, gdzie-lub , teraz możemy dodać tę ORfunkcjonalność >= rails 4.2.3również za pomocą tego klejnotu.


6

(To tylko dodatek do odpowiedzi KM Rakibul Islam.)

Korzystając z lunet, kod może stać się ładniejszy (w zależności od spojrzenia):

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

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

5

Musiałem zrobić (A && B) || (C && D) || (E && F)

Ale w 5.1.4obecnym stanie Railsów jest to zbyt skomplikowane, aby wykonać to z Arelem lub łańcuchem. Ale nadal chciałem używać Railsów do generowania jak największej liczby zapytań.

Więc zrobiłem mały hack:

W swoim modelu stworzyłem prywatną metodę o nazwie sql_where:

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

Następnie w moim zakresie utworzyłem tablicę do przechowywania 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 '))
}

Który przyniesie oczekiwanego rezultatu zapytania: SELECT * FROM `models` WHERE ((A AND B) OR (C AND D) OR (E AND F)).

A teraz mogę łatwo połączyć to z innymi celownikami itp. Bez niewłaściwych sal operacyjnych.

Piękno polega na tym, że mój sql_where przyjmuje normalne argumenty where-clause: sql_where(name: 'John', role: 'admin')wygeneruje (name = 'John' AND role = 'admin').


Myślę, że możesz użyć .mergejako odpowiednika && i zbudować odpowiednie drzewo, aby uchwycić swoje pareny. Coś jak ... (scopeA.merge(scopeB)).or(scopeC.merge(scopeD)).or(scopeE.merge(scopeF))zakładając, że każdy z teleskopów wygląda Model.where(...)
mniej więcej

Sprawdź to przed użyciem scalania - github.com/rails/rails/issues/33501
Aarthi

1

Rails 5 ma możliwość orklauzuli z where. Na przykład.

User.where(name: "abc").or(User.where(name: "abcd"))
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.