복붙노트

[RUBY-ON-RAILS] 액티브 Arel OR 조건

RUBY-ON-RAILS

액티브 Arel OR 조건

당신은 어떻게 논리적 OR 대신 AND 사용이 개 다른 조건을 결합 할 수 있습니다?

참고 :이 조건은 레일 범위로 생성하고 쉽게 경우 ( "X 또는 Y")와 같은 무언가로 변경할 수 없습니다 직접.

간단한 예 :

admins = User.where(:kind => :admin)
authors = User.where(:kind => :author)

그것은 (이 특정 경우에 의미가있는) 적용 및 조건을 쉽게 :

(admins.merge authors).to_sql
#=> select ... from ... where kind = 'admin' AND kind = 'author'

그러나 당신은 어떻게 이미 2 개 개의 다른 Arel 관계를 갖는 다음 쿼리를 생성 할 수 있습니다?

#=> select ... from ... where kind = 'admin' OR kind = 'author'

그것은 (Arel의 추가 정보에 따라) 같다 :

그러나 나는 여기에 적용하고 뭔가를 작성하는 기대하지 않는다 희망한다 :

(admins.or authors).to_sql

해결법

  1. ==============================

    1.나는 파티에 조금 늦게 해요,하지만 여기에 내가 가지고 올 수있는 최선의 제안이다 :

    나는 파티에 조금 늦게 해요,하지만 여기에 내가 가지고 올 수있는 최선의 제안이다 :

    admins = User.where(:kind => :admin)
    authors = User.where(:kind => :author)
    
    admins = admins.where_values.reduce(:and)
    authors = authors.where_values.reduce(:and)
    
    User.where(admins.or(authors)).to_sql
    # => "SELECT \"users\".* FROM \"users\"  WHERE ((\"users\".\"kind\" = 'admin' OR \"users\".\"kind\" = 'author'))"
    
  2. ==============================

    2.액티브 쿼리 액티브 :: 관계 객체 (강합니다 지원하거나하지 않는)이며,하지 (할) 객체를 Arel.

    액티브 쿼리 액티브 :: 관계 객체 (강합니다 지원하거나하지 않는)이며,하지 (할) 객체를 Arel.

    [UPDATE는 : 레일 5에서, "또는"액티브지지된다 : 관계; https://stackoverflow.com/a/33248299/190135 참조]

    그러나 다행히도, 그 곳 방법은 ARel 쿼리 개체를 받아들입니다. 사용자 <액티브 :: 자료 그렇다면 ...

    users = User.arel_table
    query = User.where(users[:kind].eq('admin').or(users[:kind].eq('author')))
    

    query.to_sql 이제 안심을 보여줍니다

    SELECT "users".* FROM "users"  WHERE (("users"."kind" = 'admin' OR "users"."kind" = 'author'))
    

    명확하게하기 위해, 당신은 몇 가지 일시적인 부분 쿼리 변수를 추출 할 수 있습니다 :

    users = User.arel_table
    admin = users[:kind].eq('admin')
    author = users[:kind].eq('author')
    query = User.where(admin.or(author))
    

    쿼리가 일단 자연적으로, 당신은 실제 데이터베이스 호출을 실행하는 query.all 사용할 수 있습니다.

  3. ==============================

    3.실제 arel 페이지에서 :

    실제 arel 페이지에서 :

  4. ==============================

    4.레일 5으로서 우리는이 작업을 수행 할 수 있도록 액티브 :: 관계 #이 있거나 :

    레일 5으로서 우리는이 작업을 수행 할 수 있도록 액티브 :: 관계 #이 있거나 :

    User.where(kind: :author).or(User.where(kind: :admin))
    

    ... 당신이 기대할 SQL로 변환됩니다되는 :

    >> puts User.where(kind: :author).or(User.where(kind: :admin)).to_sql
    SELECT "users".* FROM "users" WHERE ("users"."kind" = 'author' OR "users"."kind" = 'admin')
    
  5. ==============================

    5.나는 mongoid의 #any_of에 액티브 대안을 찾고 같은 문제에 충돌했습니다.

    나는 mongoid의 #any_of에 액티브 대안을 찾고 같은 문제에 충돌했습니다.

    @jswanner의 대답은 좋지만, 어디에서 매개 변수가 해시 경우에만 작동합니다 :

    > User.where( email: 'foo', first_name: 'bar' ).where_values.reduce( :and ).method( :or )                                                
    => #<Method: Arel::Nodes::And(Arel::Nodes::Node)#or>
    
    > User.where( "email = 'foo' and first_name = 'bar'" ).where_values.reduce( :and ).method( :or )                                         
    NameError: undefined method `or' for class `String'
    

    두 문자열과 해시를 사용할 수있게하려면이 사용할 수 있습니다 :

    q1 = User.where( "email = 'foo'" )
    q2 = User.where( email: 'bar' )
    User.where( q1.arel.constraints.reduce( :and ).or( q2.arel.constraints.reduce( :and ) ) )
    

    사실, 그것은 추한, 당신은 매일하는 것이 사용하지 않습니다. 여기에 내가 만든 한 일부 #any_of 구현은 다음과 같습니다 https://gist.github.com/oelmekki/5396826

    그것은 그렇게 할 수 있습니다 :

    > q1 = User.where( email: 'foo1' ); true                                                                                                 
    => true
    
    > q2 = User.where( "email = 'bar1'" ); true                                                                                              
    => true
    
    > User.any_of( q1, q2, { email: 'foo2' }, "email = 'bar2'" )
    User Load (1.2ms)  SELECT "users".* FROM "users" WHERE (((("users"."email" = 'foo1' OR (email = 'bar1')) OR "users"."email" = 'foo2') OR (email = 'bar2')))
    

    편집 : 그 이후로, 나는 도움 건물 또는 쿼리에 보석을 발표했습니다.

  6. ==============================

    6.그냥 OR 조건에 대한 범위를합니다

    그냥 OR 조건에 대한 범위를합니다

    scope :author_or_admin, where(['kind = ? OR kind = ?', 'Author', 'Admin'])
    
  7. ==============================

    7.그것은 다음과 같이 뭔가 것 SmartTuple 사용 :

    그것은 다음과 같이 뭔가 것 SmartTuple 사용 :

    tup = SmartTuple.new(" OR ")
    tup << {:kind => "admin"}
    tup << {:kind => "author"}
    User.where(tup.compile)
    

    또는

    User.where((SmartTuple.new(" OR ") + {:kind => "admin"} + {:kind => "author"}).compile)
    

    당신은 내가 바이어스라고 생각 할 수 있습니다,하지만, 난 여전히 훨씬 더 명확하고이 특정 경우에 체인 방법보다 편리되는 전통적인 데이터 구조 작업을 고려하십시오.

  8. ==============================

    8.(멋진 솔루션이 실제로 어느 날 도움) jswanner 답변을 확장 사람들을 인터넷 검색을위한하려면 :

    (멋진 솔루션이 실제로 어느 날 도움) jswanner 답변을 확장 사람들을 인터넷 검색을위한하려면 :

    이 같은 범위를 적용 할 수 있습니다

    scope :with_owner_ids_or_global, lambda{ |owner_class, *ids|
      with_ids = where(owner_id: ids.flatten).where_values.reduce(:and)
      with_glob = where(owner_id: nil).where_values.reduce(:and)
      where(owner_type: owner_class.model_name).where(with_ids.or( with_glob ))
    }
    
    User.with_owner_ids_or_global(Developer, 1, 2)
    # =>  ...WHERE `users`.`owner_type` = 'Developer' AND ((`users`.`owner_id` IN (1, 2) OR `users`.`owner_id` IS NULL))
    
  9. ==============================

    9.이 방법에 대한 어떤 : http://guides.rubyonrails.org/active_record_querying.html#hash-conditions (그리고 2.3.3 확인)

    이 방법에 대한 어떤 : http://guides.rubyonrails.org/active_record_querying.html#hash-conditions (그리고 2.3.3 확인)

    admins_or_authors = User.where(:kind => [:admin, :author])
    
  10. ==============================

    10.우리가 여기에 해킹 할 필요가 불행하게도 그것은 기본적으로 지원되지 않습니다.

    우리가 여기에 해킹 할 필요가 불행하게도 그것은 기본적으로 지원되지 않습니다.

    그리고 꽤 비효율적 인 SQL (희망 DBA가 그것을보고하지 않는 :-))는이 같은 해킹 외모 :

    admins = User.where(:kind => :admin)
    authors = User.where(:kind => :author)
    
    both = User.where("users.id in (#{admins.select(:id)}) OR users.id in (#{authors.select(:id)})")
    both.to_sql # => where users.id in (select id from...) OR users.id in (select id from)
    

    이 subselets를 생성합니다.

    그리고 조금 더 나은 같은 외모 (SQL의 관점에서) 해킹 :

    admins_sql = admins.arel.where_sql.sub(/^WHERE/i,'')
    authors_sql = authors.arel.where_sql.sub(/^WHERE/i,'')
    both = User.where("(#{admins_sql}) OR (#{authors_sql})")
    both.to_sql # => where <admins where conditions> OR <authors where conditions>
    

    이것은 적절한 OR 조건을 생성하지만 분명히 그것은 단지 계정으로 범위의 WHERE 참여한다.

    내가 그것을 어떻게 수행을 볼 때까지 나는 1 일을 선택했다.

    어떤 경우에는, 당신은 꽤 조심하고 생성 된 SQL을보고해야합니다.

  11. from https://stackoverflow.com/questions/7976358/activerecord-arel-or-condition by cc-by-sa and MIT license