Kapsam sorgularını AND yerine OR ile nasıl zincirleyebilirim?


Yanıtlar:


107

Yapardın

Person.where('name=? OR lastname=?', 'John', 'Smith')

Şu anda, yeni AR3 sözdizimi tarafından başka bir VEYA desteği yoktur (yani 3. taraf mücevher kullanmadan).


21
ve sadece güvenlik uğruna, bunu Person.where ("name =? OR lastname =?", 'John', 'Smith') olarak ifade etmeye değer
CambridgeMike

6
Bu hala Raylar 4'te uygulanabilir mi? Rails 4'te daha iyi bir şey gelmedi mi?
Donato

11
Rails 4'te değişiklik yok, ancak Rails 5 yerleşik olacak.or .
kireç

3
bu kapsamlar değildir, sadece 2 cümle vardır veya çok özel bir kapsam durumu. Örneğin daha karmaşık kapsamları zincirlerle birleştirmek istersem.
miguelfg

2
Raylar 5 ile aynı zamanda, Person.where ( "name = 'John'") gibi bir şey yapabilirsiniz ya da (Person.where ( "soyad = 'Smith'")).
shyam

59

Göre bu çekme isteği , Raylar 5 şimdi sorguları zincirleme için aşağıdaki sözdizimini destekler:

Post.where(id: 1).or(Post.where(id: 2))

Ayrıca bu mücevher aracılığıyla Rails 4.2 içine işlevselliği bir backport var .


48

ARel kullanın

t = Person.arel_table

results = Person.where(
  t[:name].eq("John").
  or(t[:lastname].eq("Smith"))
)

1
Postgres'de bu tür bir sorgunun kesilip kesilmediğine dair kimse yorum yapabilir mi?
Olivier Lacan

ARel'in DB-agnostik olması gerekiyor.
Simon Perepelitsa

Bu iyi bir fikir gibi görünse de, ARel herkese açık bir API değildir ve bunu kullanmak uygulamanızı beklenmedik şekillerde bozabilir, bu yüzden ARel API'nin evrimine çok dikkat etmediyseniz buna karşı tavsiye ederim.
Olivier Lacan

7
@OlivierLacan Arel herkese açık bir API veya daha doğrusu bir tanesini ortaya koyuyor. Aktif Kayıt sadece kaputun altında kullanan kolaylık yöntemleri sağlar, Kendi başına kullanmak tamamen geçerlidir. Anlamsal sürüm oluşturmayı izler, bu nedenle büyük sürümleri değiştirmediğiniz sürece (3.xx => 4.xx), değişiklikleri bozma konusunda endişelenmenize gerek yoktur.
Gri

41

Gözetmenlere yardımcı olmak için aynı sütun VEYA sorguları için Dizi sözdizimini yayınlamanız yeterlidir.

Person.where(name: ["John", "Steve"])

2
Karşı adı ve Smith karşı soru onay John lastname
steven_noble

11
@steven_noble - bu yüzden veya sorgusu ile "aynı sütun" u kalınlaştırdım. Yorumu tamamen silebilirim, ancak "veya" SO sorularını sorgulayan insanlar için yararlı olacağını düşündüm.
daino3

38

Rails4 Güncellemesi

3. taraf mücevher gerektirmez

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

Eski cevap

3. taraf mücevher gerektirmez

Person.where(
    Person.where(:name => "John").where(:lastname => "Smith")
      .where_values.reduce(:or)
)

5
bu gerçekten OP'nin kaynaklar ve yöntem açısından sorusuna tek saf cevap. diğer yanıtlar ya (a) 3. taraf apis gerektirir ya da (b) orikisi de OP koduna yansıtılmayan bir metin bloğu içinde enterpolasyon veya diğer operatörler gerektirir .
allenwlee

6
İşte açıklayan iyi bir makale coderwall.com/p/dgv7ag/…
allenwlee

4
Kullanılan ...where_values.join(' OR ')benim durumumda
Kanat

2
.tap {|sc| sc.bind_values = [a, b].map(&:bind_values) }Kapsamlarınızın bağlayıcı olması gereken değişkenleri yoksa bölümü atlayabilirsiniz .
fatuhoku


22

Herhangi birinin bu soruya güncel bir cevap araması durumunda, bunu Rails'e almak için mevcut bir çekme isteği var gibi görünüyor: https://github.com/rails/rails/pull/9052 .

@ J-mcnally'ın ActiveRecord için maymun yaması sayesinde ( https://gist.github.com/j-mcnally/250eaaceef234dd8971b ) aşağıdakileri yapabilirsiniz:

Person.where(name: 'John').or.where(last_name: 'Smith').all

Daha da değerli olan, kapsamları aşağıdakilerle zincirleme yeteneğidir OR:

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

Ardından, soyadı veya soyadı olan tüm kişileri bulabilirsiniz.

Person.first_or_last_name('John Smith').or.parent_last_name('Smith')

Bunun kullanımı için en iyi örnek değil, sadece soruya uymaya çalışmak.


2
Bunun için hangi sürüme veya raylara ihtiyacınız var?
Kanat

3
Bunu Rails çekirdeğine dönüştürecek en son tartışma ve çekme isteği burada bulunabilir: github.com/rails/rails/pull/16052 Rails 5.0, .orzincir işlevselliğinin resmi sürümü için hedeflenmiştir . Rails> = 3.2'de bahsettiğim maymun yamasını kullanabildim; ancak, Rails 5'in kodunuzda uzun süreli değişiklikler yapmasını bekleyebilirsiniz.
codenamev

1
Evet, Rails 5 ile birleştirildi ve serbest bırakıldı
amoebe

10

Benim için (Rails 4.2.5) sadece şu şekilde çalışır:

{ where("name = ? or name = ?", a, b) }

8

Bu, Rails 3.0+ kullanıyorsanız MetaWhere için iyi bir aday olacaktır, ancak Rails 3.1 üzerinde çalışmaz. Bunun yerine squeel'i denemek isteyebilirsiniz . Aynı yazar tarafından yapılmıştır. OR tabanlı bir zinciri şu şekilde gerçekleştirirsiniz:

Person.where{(name == "John") | (lastname == "Smith")}

AND / VEYA'yı diğer birçok harika şeyin yanı sıra karıştırabilir ve eşleştirebilirsiniz .


8

Rails / ActiveRecord'un güncellenmiş bir sürümü bu sözdizimini yerel olarak destekleyebilir. Şuna benzer görünecektir:

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

Bu çekme isteğinde belirtildiği gibi https://github.com/rails/rails/pull/9052

Şimdilik, sadece aşağıdakilere sadık kalmak harika çalışıyor:

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

5
Rails
4.2.5'te

1
Foo.where(foo: 'bar1').or.where(bar: 'bar2')çalışmıyor. Foo.where (foo: 'bar1') veya orwhere (Foo.where (bar: 'bar2')) olmalıdır
Ana María Martínez Gómez

6

Raylar 4 + Kapsam + 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

.Or ve şanssız olarak adlandırılan kapsamları zincirlemeyi denedim, ancak bu, bu booleans setiyle bir şey bulmak için çalıştı. SQL gibi üretir

SELECT 'CREATURES'.* FROM 'CREATURES' WHERE ((('CREATURES'.'is_cat' = 1 OR 'CREATURES'.'is_dog' = 1) OR 'CREATURES'.'eats_children' = 0))

4

Raylar 4

scope :combined_scope, -> { where("name = ? or name = ?", 'a', 'b') }

4

Bir kapsam sağlamak istiyorsanız (tüm veri kümesinde açıkça çalışmak yerine), Rails 5 ile yapmanız gerekenler:

scope :john_or_smith, -> { where(name: "John").or(where(lastname: "Smith")) }

Veya:

def self.john_or_smith
  where(name: "John").or(where(lastname: "Smith"))
end

Bu doğrudur, ancak stackoverflow.com/a/42894753/1032882'nin teknik olarak yaptığıdan daha gerçek bir cevap .
RudyOnRails

3

"Veya" ifadesini dahil etmek için where yan tümcesini el ile yazamazsanız (örneğin, iki kapsamı birleştirmek istiyorsanız), birleşmeyi kullanabilirsiniz:

Model.find_by_sql("#{Model.scope1.to_sql} UNION #{Model.scope2.to_sql}")

(kaynak: ActiveRecord Sorgu Birliği )

Bu, sorgulardan herhangi biriyle eşleşen tüm kayıtları döndürür. Ancak, bu bir arel değil bir dizi döndürür. Gerçekten bir arel iade etmek istiyorsanız, bu özü kontrol edin : https://gist.github.com/j-mcnally/250eaaceef234dd8971b .

Maymun yama raylarını önemsemediğiniz sürece bu işi yapacak.


2

Ayrıca şu ilgili sorulara bakın: burada , burada ve burada

Raylar 4 için, bu makaleye ve bu orijinal cevaba dayanarak

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

Not sonunda makalenin dikkatli:

Raylar 4.1+

Rails 4.1, default_scope'u normal bir kapsam gibi ele alır. Varsayılan kapsam (varsa) where_values ​​sonucuna dahil edilir ve inject (: veya), varsayılan kapsam ve konumlarınız arasına ekler veya ifadeler ekler. Bu kötü.

Bunu çözmek için sorguyu çözmeniz yeterlidir.


1

Bu çok uygun bir yoldur ve Rails 5'de iyi çalışır:

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")
  )

0

squeelmücevher Bunu gerçekleştirmek için inanılmaz kolay bir yol sağlar (önceki @ coloradoblue yöntemi gibi bu kullandığım bir şeye):

names = ["Kroger", "Walmart", "Target", "Aldi"]
matching_stores = Grocery.where{name.like_any(names)}

0

Yani asıl sorunun cevabı, 'veya' yerine 'ile kapsamlara katılabilir misiniz? Ve “hayır yapamazsınız” gibi görünüyor. Ancak işi yapan tamamen farklı bir kapsamı veya sorguyu elle kodlayabilir veya ActiveRecord'dan MetaWhere veya Squeel gibi farklı bir çerçeve kullanabilirsiniz. Benim durumumda yararlı değil

Ben 'veya' seçmekten biraz daha fazlasını yapar pg_search tarafından oluşturulan bir kapsam, temiz bir birlik karışıklık yapar ASC tarafından sipariş içerir. Pg_search'te yapamayacağım şeyler yapan el işi bir kapsamla 'veya' istiyorum. Bu yüzden böyle yapmak zorunda kaldım.

Product.find_by_sql("(#{Product.code_starts_with('Tom').to_sql}) union (#{Product.name_starts_with('Tom').to_sql})")

Kapsamları sql'ye çevirin, her birinin etrafına parantez koyun, birleştirin ve üretilen sql'i kullanarak find_by_sql. Biraz saçma, ama işe yarıyor.

Hayır, bana pg_search'te "karşı: [: name,: code]" kullanabileceğimi söyleme, bunu yapmak istiyorum, ancak 'name' alanı pg_search'ün işleyemeyeceği bir hstore hala. Bu nedenle, isme göre kapsamın elle hazırlanması ve daha sonra pg_search kapsamıyla birleştirilmesi gerekir.


-2
names = ["tim", "tom", "bob", "alex"]
sql_string = names.map { |t| "name = '#{t}'" }.join(" OR ")
@people = People.where(sql_string)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.