2016-12-05 7 views
0

検索結果をフィルタリングする動的なクエリメソッドを構築しようとしています。Rails-検索結果をフィルタリングする動的クエリを構築する

私のモデルは:

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 

私は、デフォルトですべての注文を表示するページがあります。私は結果のフィルタリングを可能にするチェックボックスを持っています。フィルターには、年、州、セールス、カウンセルがあります。クエリの例は次のとおりです。sales_id unlimited idsを持つ状態(has_many〜)NJ、PA、CAなどの2016年、2015年のすべての注文(「order.title_number LIKE?」、「%NYN#{year}%」) counsel_id無制限のcounsel_id。

ナッツシェルでは、ユーザーがチェックするすべてのオプションを考慮した1つのクエリを作成する方法を理解しようとしています。ここに私の現在のクエリのコードは次のとおりです。

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

これらすべてのクエリを呼び出すメソッドが1つ必要であることを意味していますか?あなたのコードではあなたがby_stateを呼び出すので、私は年が別のものであると仮定します。 1つのクエリではありません。その場合(4つのクエリの場合)、私は実際に何が問題なのかわかりません。 –

+0

はい1つのクエリですべてを呼び出すとします。現在、私はクエリメソッドに渡された引数に基づいて個別にクエリを呼び出そうとしています。引数の例は{"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:OR論理を使用しての代わりに、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年です。私はどのようにして@orderを通過でき、具体的に言うと、これらの4州で3年間の注文が必要なのですか?私は現在、それぞれのカテゴリーごとに1つの選択肢だけを使ってあなたのソリューションを見ています。あなたの助けをもう一度ありがとう。 –

+0

ANDロジックではなく、ORロジックが必要なようです。上記のコードは、AND型構文を使用して反復的にフィルタリングします。それがORロジック(2015年または2016年の注文)になりたい場合は、ロジックをわずかに変更する必要があります。コードは上記で更新されました。 – Genzume

+0

私はANDとORロジックの両方を探しています。さまざまなフィルタリングオプション(状態、年など)のANDロジック。フィルタリングオプションの倍数がある場合のORロジック。例。 2016年から2015年までのすべての注文は、NY、NJ、PA、MAのセールスマン1,2,3によって行われます。 –

関連する問題