2016-12-05 2 views
0

동적 검색 방법을 구축하여 검색 결과를 필터링하려고합니다.레일 - 검색 결과 필터링을위한 동적 쿼리 작성

내 모델 :

class Order < ActiveRecord::Base 
    scope :by_state, -> (state) { joins(:states).where("states.id = ?", state) } 
    scope :by_counsel, -> (counsel) { where("counsel_id = ?", counsel) } 
    scope :by_sales_rep, -> (sales) { where("sales_id = ?", sales) } 
    scope :by_year, -> (year) { where("title_number LIKE ?", "%NYN#{year}%") } 
    has_many :properties, :dependent => :destroy 
    has_many :documents, :dependent => :destroy 
    has_many :participants, :dependent => :destroy 
    has_many :states, through: :properties 
    belongs_to :action 
    belongs_to :role 
    belongs_to :type 
    belongs_to :sales, :class_name => 'Member' 
    belongs_to :counsel, :class_name => 'Member' 
    belongs_to :deal_name 
end 

class Property < ActiveRecord::Base 
    belongs_to :order 
    belongs_to :state 
end 

class State < ActiveRecord::Base 
    has_many :properties 
    has_many :orders, through: :properties 
end 

나는 기본적으로 모든 주문을 표시하는 페이지가 있습니다. 결과 필터링을 허용하는 확인란을 갖고 싶습니다. 필터는 Year, State, Sales 및 Counsel입니다. 검색어의 예는 다음과 같습니다. 상태 (has_many부터 NJ, PA, CA 등)의 2016 년, 2015 년 ("order.title_number LIKE?", "% NYN # {year} %")의 모든 주문은 sales_id 무제한 및 counsel_id 무제한 counsel_id.

너트 쉘에서 사용자가 확인하는 모든 옵션을 고려하여 하나의 쿼리를 작성하는 방법을 알아 내려고합니다. 현재 쿼리 코드는 다음과 같습니다.

def Order.query(opt = {}) 
    results = [] 
    orders = [] 

    if !opt["state"].empty? 
     opt["state"].each do |value| 
     if orders.empty? 
      orders = Order.send("by_state", value) 
     else 
      orders << Order.send("by_state", value) 
     end 
     end 
     orders = orders.flatten 
    end 

    if !opt["year"].empty? 
     new_orders = [] 

     opt["year"].each do |y| 
     new_orders = orders.by_year(y) 
     results << new_orders 
     end 
    end 

    if !opt["sales_id"].empty? 

    end 

    if !opt["counsel_id"].empty? 

    end 
    if !results.empty? 
     results.flatten 
    else 
     orders.flatten 
    end 
    end 

다음은 무제한 필터링을 허용하는 해결책입니다.

def self.query(opts = {}) 

    orders = Order.all 
    opts.delete_if { |key, value| value.blank? } 
    const_query = "" 
    state_query = nil 
    counsel_query = nil 
    sales_query = nil 
    year_query = nil 
    queries = [] 

    if opts["by_year"] 
     year_query = opts["by_year"].map do |val| 
     " title_number LIKE '%NYN#{val}%' " 
     end.join(" or ") 
     queries << year_query 
    end  

    if opts["by_sales_rep"] 
     sales_query = opts["by_sales_rep"].map do |val| 
     " sales_id = '#{val}' " 
     end.join(" or ") 
     queries << sales_query 
    end 

    if opts["by_counsel"] 
     counsel_query = opts["by_counsel"].map do |val| 
     " counsel_id = '#{val}' " 
     end.join(" or ") 
     queries << counsel_query 
    end 

    if opts["by_state"]  
     state_query = opts["by_state"].map do |val| 
     "states.id = '#{val}'" 
     end.join(" or ") 
    end 

    query_string = queries.join(" AND ") 

    if state_query 
     @orders = Order.joins(:states).where("#{state_query}") 
     @orders = @orders.where(query_string) 
    else 
     @orders = orders.where("#{query_string}") 
    end 

    @orders.order("title_number DESC") 
    end 
+0

모든 쿼리를 호출하는 하나의 메서드가 필요하다는 의미입니까? 왜냐하면 당신의 코드에서 by_state를 호출하기 때문에, 나는 1 년이라고 가정합니다. 그래서 하나의 쿼리가 아닙니다. 이 경우 (4 개의 쿼리 인 경우) 실제로 문제가 무엇인지 알지 못합니다 ... –

+0

예 하나의 쿼리로 모든 것을 호출하려고합니다. 현재 쿼리 메서드에 전달 된 인수를 기반으로 쿼리를 개별적으로 호출하려고합니다. 인수의 예는 { "year"=> [ "16", "15"], "state"=> [ "31", "39"]}입니다. 제 생각에는 _by_year를 _by_state 조건으로 여러 번 호출 할 수있는 능력이 있다고 생각합니다. 그러나 나는 몇 년 동안 국가가 파견 될지 모른다. @Joel_Blum –

+0

'scope '에 따라 opt_keys의 이름을 지어 보시지 않겠습니까? ''by_state : [ "31", "39"], by_year : "16"}'그러면 opts.inject (Order) {| order, (scope, value) | order.public_send (범위, 값)}'. 이렇게하면 지정된 모든 필터로 ActiveRecord :: Relation이 작성됩니다. 그런 다음에 행동 할 때 쿼리가 실제로 실행됩니다. BTW 여러 해를지기를 원하면'by_year' 범위를 수정해야합니다. – engineersmnky

답변

0

일반적인 패턴 인 검색어/필터 객체를 찾고 있습니까? 나는 이것과 비슷한 answer을 썼지 만 중요한 부분을 추출하려고 노력할 것이다.

먼저 논리를 해당 객체로 이동해야합니다. 검색/필터 개체가 초기화되면 관계 쿼리 (Order.all 또는 일부 기본 쿼리)로 시작한 다음 필터링을 수행해야합니다.

다음은 멋진 기본 예제로 올바른 트랙을 얻을 수 있습니다. 그렇게 부르면 orders = OrderQuery.call(params)입니다.

# /app/services/order_query.rb 
class OrderQuery 
    def call(opts) 
    new(opts).results 
    end 

    private 

    attr_reader :opts, :orders 

    def new(opts={}) 
    @opts = opts 
    @orders = Order.all # If using Rails 3 you'll need to use something like 
         # Order.where(1=1) to get a Relation instead of an Array. 
    end 

    def results 
    if !opt['state'].empty? 
     opt['state'].each do |state| 
     @orders = orders.by_state(state) 
     end 
    end 

    if !opt['year'].empty? 
     opt['year'].each do |year| 
     @orders = orders.by_year(year) 
     end 
    end 

    # ... all filtering logic 
    # you could also put this in private functions for each 
    # type of filter you support. 

    orders 
    end 
end 

EDIT : 사용하는 대신 또는 논리 AND 논리

# /app/services/order_query.rb 
class OrderQuery 
    def call(opts) 
    new(opts).results 
    end 

    private 

    attr_reader :opts, :orders 

    def new(opts={}) 
    @opts = opts 
    @orders = Order.all # If using Rails 3 you'll need to use something like 
         # Order.where(1=1) to get a Relation instead of an Array. 
    end 

    def results 
    if !opt['state'].empty? 
     @orders = orders.where(state: opt['state']) 
    end 

    if !opt['year'].empty? 
     @orders = orders.where(year: opt['year']) 
    end 

    # ... all filtering logic 
    # you could also put this in private functions for each 
    # type of filter you support. 

    orders 
    end 
end 

상기 구문은 기본적으로 말을 if state is in this array of statesyear is within this array of years 필터.

+0

고마워요, 제가 논리를 따라하고 이것을 구현하기 시작했습니다. 내 주요 관심사는 지금 내가 동시에 여러 개의 필터를 적용하려는 경우입니다. 예는 4x 주 및 3 년입니다. @orders를 어떻게 통과 할 수 있습니까? 구체적으로 말하자면,이 4 개 주에서 3 년간의 주문이 필요합니까? 현재 각 범주에 대해 하나의 선택만으로 작업하는 솔루션을 보았습니다. 도와 줘서 고마워. –

+0

AND 논리 대신 OR 논리를 원한 것처럼 들립니다. 위의 코드는 AND 형식 구문을 사용하여 반복적으로 필터링합니다. OR 논리 (2015 년 또는 2016 년 주문)를 원한다면 로직을 약간 수정해야합니다. 위 코드가 업데이트되었습니다. – Genzume

+0

나는 AND 논리와 OR 논리를 모두 찾고있다. 필터링 옵션의 배수가있을 때의 OR 로직에 대한 다양한 필터링 옵션 (주, 연도 등)에 대한 논리 예. 주 뉴욕, 뉴저지, 펜실바니아, MA에서 판매원 1,2,3에 의해 2016 년부터 2015 년까지의 모든 주문. –