AND 대신 OR로 범위 쿼리를 연결하는 방법은 무엇입니까?


137

Rails3, ActiveRecord를 사용하고 있습니다

AND가 아닌 OR 문으로 범위를 연결하는 방법이 궁금합니다.

예 :

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

일반적으로 다음을 반환합니다.

name = 'John' AND lastname = 'Smith'

그러나 나는 싶습니다 :

`name = 'John' OR lastname = 'Smith'

도움이 될 수 있습니다 : 액티브 OR 쿼리 해시 표기법
potashin

답변:


107

당신은 할 것

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

현재 새로운 AR3 구문에 의한 다른 OR 지원은 없습니다 (즉, 일부 타사 gem을 사용하지 않음).


21
안전을 위해서 이것을 Person.where ( "name =? OR lastname =?", 'John', 'Smith')로 표현하는 것이 좋습니다.
CambridgeMike

6
이것은 여전히 ​​Rails 4에도 적용됩니까? Rails 4에서 더 좋은 것이 없었나요?
Donato

11
Rails 4에는 변화가 없지만 Rails 5 에는 .or내장되어 있을 것 입니다 .
lime

3
이것은 범위가 아니며 2 개의 where 절이거나 매우 구체적인 범위입니다. 예를 들어 조인을 사용하여 더 복잡한 범위를 연결하려면 어떻게해야합니까?
miguelfg

2
Rails 5를 사용하면 Person.where ( "name = 'John'"). or (Person.where ( "lastname = 'Smith'"))
shyam

59

에 따르면 이 풀 요청 , 레일 (5)는 이제 쿼리를 체인에 대해 다음 구문을 지원합니다 :

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

이 gem을 통해 Rails 4.2에 기능의 백 포트도 있습니다 .


48

ARel 사용

t = Person.arel_table

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

1
Postgres 에서이 유형의 쿼리가 중단되는지 여부에 대해 의견을 말할 수 있습니까?
Olivier Lacan

ARel은 DB에 구애받지 않아야합니다.
Simon Perepelitsa

이것은 좋은 아이디어처럼 보이지만 ARel은 공개 API가 아니며이를 사용하면 응용 프로그램이 예기치 않은 방식으로 중단 될 수 있으므로 ARel API의 진화에주의를 기울이지 않으면 반대 의견을 제시합니다.
Olivier Lacan

7
@OlivierLacan Arel은 공개 API이거나 더 정확하게는 노출합니다. Active Record는 단지 그것을 사용하는 편리한 방법을 제공합니다. 자체적으로 사용하는 것은 완전히 유효합니다. 시맨틱 버전 관리를 따르므로 주요 버전 (3.xx => 4.xx)을 변경하지 않는 한 변경 사항을 깨뜨리지 않아도됩니다.
그레이

41

동일한 열 또는 쿼리에 대해 배열 구문을 게시하면 엿볼 수 있습니다.

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

2
에 대한 이름과 스미스에 대한 질문 체크 존 LASTNAME
steven_noble

11
@steven_noble-그래서 "나 같은 열"을 또는 쿼리로 굵게 표시 한 것입니다. 댓글을 모두 삭제할 수는 있지만 "또는"질문을 자주하는 사람들에게 도움이 될 것이라고 생각했습니다.
daino3

38

Rails4 업데이트

타사 보석이 필요하지 않습니다

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

이전 답변

타사 보석이 필요하지 않습니다

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

5
이것은 실제로 자원과 방법 측면에서 OP의 질문에 대한 유일한 순수한 대답입니다. 다른 답변은 (a) 타사 API가 필요하거나 (b) or텍스트 블록 내의 보간 또는 다른 연산자가 필요 하며 OP 코드에는 반영되지 않습니다.
allenwlee

6
여기 설명하는 괜찮은 기사입니다 coderwall.com/p/dgv7ag/...
allenwlee

4
사용 ...where_values.join(' OR ')내 경우
샤시

2
.tap {|sc| sc.bind_values = [a, b].map(&:bind_values) }스코프에 바인딩이 필요한 변수가없는 경우 부품을 생략 할 수 있습니다 .
fatuhoku


22

누구 든지이 답변에 대한 업데이트 된 답변을 찾고 있다면 Rails https://github.com/rails/rails/pull/9052 로 가져 오는 기존 풀 요청이있는 것처럼 보입니다 .

ActiveRecord ( https://gist.github.com/j-mcnally/250eaaceef234dd8971b )에 대한 @ j-mcnally의 원숭이 패치 덕분에 다음을 수행 할 수 있습니다.

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

더 중요한 것은 다음과 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) }

그런 다음 이름 또는 성 또는 이름을 가진 부모를 가진 모든 사람을 찾을 수 있습니다

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

이것을 사용하는 가장 좋은 예는 아니지만 질문에 맞추려고합니다.


2
어떤 버전 또는 Rails가 필요합니까?
Sash

3
이것을 Rails 코어로 만드는 최신 토론과 풀 요청은 여기에서 찾을 수 있습니다 : github.com/rails/rails/pull/16052 Rails 5.0은 공식 .or체인 기능 릴리스를 목표로합니다 . Rails> = 3.2에서 언급 한 원숭이 패치를 사용할 수있었습니다. 그러나 Rails 5가 코드에 오랜 변화를 가져 오기를 기다릴 수 있습니다.
codenamev

1
네, Rails 5와 합병 되어 릴리스되었습니다.
amoebe

10

나를 위해 (Rails 4.2.5) 다음과 같이 작동합니다.

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

8

Rails 3.0 이상을 사용하는 경우 MetaWhere에 적합한 후보이지만 Rails 3.1에서는 작동하지 않습니다. 대신에 압박 을 시도해 볼 수 있습니다 . 같은 저자에 의해 만들어졌습니다. OR 기반 체인을 수행하는 방법은 다음과 같습니다.

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

당신은 다른 많은 멋진 것들 중에서 AND / OR를 혼합하고 일치시킬 수 있습니다 .


8

업데이트 된 Rails / ActiveRecord 버전은이 구문을 기본적으로 지원할 수 있습니다. 다음과 유사합니다.

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

이 풀 요청에 명시된 바와 같이 https://github.com/rails/rails/pull/9052

지금은 단순히 다음을 고수하는 것이 좋습니다.

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

5
Rails 4.2.5에서 지원되지 않음
fatuhoku

1
Foo.where(foo: 'bar1').or.where(bar: 'bar2')작동하지 않습니다. Foo.where (foo : 'bar1'). or.where (Foo.where (bar : 'bar2'))
Ana María Martínez Gómez

6

레일 4 + 스코프 + 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와 운이없는 스코프라는 체인을 시도했지만 부울 세트가있는 것을 찾는 데 효과적이었습니다. 같은 SQL을 생성

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

4

레일 4

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

4

전체 데이터 세트에서 명시 적으로 작업하는 대신 범위를 제공하려는 경우 Rails 5로 수행해야 할 작업은 다음과 같습니다.

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

또는:

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

이것은 정확하지만 stackoverflow.com/a/42894753/1032882가 기술적으로 수행 하는 것과 동일한 것보다 더 문자 그대로의 대답 입니다.
RudyOnRails

3

"or"문을 포함하기 위해 where 절을 수동으로 작성할 수없는 경우 (즉, 두 범위를 결합하려는 경우) union을 사용할 수 있습니다.

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

(출처 : ActiveRecord Query Union )

쿼리와 일치하는 모든 레코드를 반환합니다. 그러나 이것은 arel이 아닌 배열을 반환합니다. 아레를 실제로 반환하려면 https://gist.github.com/j-mcnally/250eaaceef234dd8971b 요점을 확인 하십시오 .

원숭이 패치 레일이 마음에 들지 않는 한이 작업이 수행됩니다.


2

다음 관련 질문도 참조하십시오 : here , herehere

레일 4를 기반으로 이 문서이 원래 대답

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

말에 기사의주의 :

레일스 4.1+

Rails 4.1은 default_scope를 일반 범위처럼 취급합니다. 기본 범위 (있는 경우)는 where_values ​​결과에 포함되며 inject (: or)는 기본 범위와 위치 사이에 추가하거나 설명합니다. 그 나쁜.

이를 해결하려면 쿼리 범위를 지정하지 않아도됩니다.


1

이것은 매우 편리한 방법이며 Rails 5에서 잘 작동합니다 :

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

squeel보석이 작업을 수행 할 수있는 매우 쉬운 방법을 제공합니다 (전 @의 coloradoblue의 방법 등이 내가 사용하는 뭔가) :

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

0

따라서 원래 질문에 대한 대답은 'and'대신 'or'로 범위에 참여할 수 있습니까? 그러나 작업을 수행하는 완전히 다른 범위 나 쿼리를 직접 코딩하거나 MetaRecord 또는 Squeel과 같은 ActiveRecord와 다른 프레임 워크를 사용할 수 있습니다. 내 경우에는 유용하지 않다

pg_search에 의해 생성 된 범위를 '또는'선택하고 있습니다. 선택보다 약간 더 많은 것을 포함합니다 .ASC에 의한 순서가 포함되어있어 깨끗한 노동 조합을 혼란스럽게 만듭니다. pg_search에서 할 수없는 일을하는 손수 만든 범위로 '또는'하고 싶습니다. 그래서 나는 이것을 이렇게해야했습니다.

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

즉, 범위를 sql로 바꾸고 각 괄호를 묶고 함께 결합 한 다음 생성 된 sql을 사용하여 find_by_sql을 찾으십시오. 약간 쓰레기이지만 작동합니다.

아니요, pg_search에서 "against : [: name, : code]"를 사용할 수 있다고 말하지 마십시오. 그렇게하고 싶지만 'name'필드는 hstore이며 pg_search는 처리 할 수 ​​없습니다 아직. 따라서 이름으로 범위를 직접 만든 다음 pg_search 범위와 통합해야합니다.


-2
names = ["tim", "tom", "bob", "alex"]
sql_string = names.map { |t| "name = '#{t}'" }.join(" OR ")
@people = People.where(sql_string)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.