2017-04-10 4 views
-1

私はRailsを使用していますが、ここでの根本的な質問はもっと広く適用されます。私は自分のWebアプリケーション上で、フィルタリングしているものを指定し、それらのフィルタ(MongoDB)に基づいてデータベースを照会できるようにするためのレポートページを持っています。複数のオプションに基づいたクエリのクリーンアップ

データは、ホテルの周りに基づいており、ユーザが最初に(state_onestate_twostate_three)ホテルの領域を選択する必要があり、その後、ホテルのその後の状態(planningunder_constructionoperational)、任意の基準、価格帯(200,300,400)。ユーザーは、これらのオプションのそれぞれを複数選択できます。

私のやり方は空の配列を作成し、各領域を繰り返し、ユーザーがその領域を選択した場合は領域を配列にプッシュすることです。次に、その配列を繰り返して、その地域のホテルのステータスを評価します。ユーザーが選択したステータスのホテルがあれば、そのホテルを新しい空の配列に追加します。それから私は価格帯についても同じことをします。

これは動作しますが、コードが攻撃的乱雑で、ここでのコードの例です:

def find_hotel 
    hotels = find_all_hotels 
    first_array = [] 
    hotels.each do |hotel| 
    if params[:options][:region].include? 'state_one' and hotel.state == :one 
     first_array.push(hotel) 
    elsif params[:options][:region].include? 'state_two' and hotel.state == :two 
     first_array.push(hotel) 
    elsif params[:options][:region].include? 'state_three' and hotel.state == :three 
     first_array.push(hotel) 
    end 
    end 

    second_array = [] 
    first_array.each do |hotel| 
    if params[:options][:region].include? 'planning' and hotel.status == :planning 
     first_array.push(hotel) 
    elsif params[:options][:region].include? 'under_construction' and hotel.status == :under_construction 
     first_array.push(hotel) 
    elsif params[:options][:region].include? 'operational' and hotel.status == :operational 
     first_array.push(hotel) 
    end 
    end 

    third_array = [] 
    second_array.each do |hotel| 
    # More of the same here, this could go on forever 
    end 
end 

はこれを達成するためのいくつかのより良い方法は何ですか?これについて

+1

「hotel_state」が「:one」の代わりに「hotel_is_in_state_one」を持っているのはなぜですか? – tadman

+1

ActiveRecordの 'where'メソッドは、配列をSQLの' WHERE/IN'ステートメントに変換します。だから、 'Hotel.where(state:params [:options] [:region]、region:params [:options] [:region])'のようなことができるはずです。もちろん、攻撃から保護するために、permitted_pa​​ramsメカニズムを使用してパラメータをフィルタリングする必要があります。 – moveson

+0

@tadman私はこのコードをこの例題のために書いています。実際のコードではありません。私がしたいことを説明すると思っただけですが、実際のアプリケーションは異なりますが、それはすべて私のものではありません。 – Justin

答えて

1

方法:

STATES = [:one, :two, :three] 
STATUSES = [:planning, :under_construction, :operational] 
PRICES = [200, 300, 400] 

def find_hotel 
    region = params[:options][:region] 

    first_array = set_array(region, find_all_hotels, STATES, :state) 
    second_array = set_array(region, first_array, STATUSES, :status) 
    third_array = set_array(region, second_array, PRICES, :price_range) 
end 

def set_array(region, array, options, attribute) 
    array.each_with_object([]) do |element, result| 
    options.each do |option| 
     result << element if region.include?(option) && element[attribute] == option 
    end 
    end 
end 

UPDATE

は、あなたの更新の例を使用してコードを動作させるために、set_arrayattributeパラメータが追加されました。

+0

これの一般的な形状は良いですが、これらの配列は変わらないので、実際にはクラスレベルで宣言された定数値でなければなりません。 'OPTIONS =%w [option_1 option_2 option 3]'などです。 – tadman

+0

こんにちは@Gerry、申し訳ありませんが、私はこのすべてを初めて知り、 'each_with_object'がどのように動作するのか分かりません。私は今それを読んでいるが、おそらくあなたは私にここでそれを急いでleymansの言葉で説明することができますか?また、 'find_hotel'が検索基​​準に合った配列やハッシュのホテルを返すことを望んでいましたが、そのような結果を得るためにあなたの例をどのように拡張しますか? – Justin

+0

@Justin 'each_with_object'は配列をループし、その型がパラメータとして定義されているオブジェクトを返します。この場合、 'result'という名前の' Array'( 'each_with_object([])'を返します。最初に配列を作成し、通常の 'each'ループの中で使用するのと同じ結果です(例のように)。詳細については、ここをクリックしてください:http://stackoverflow.com/questions/19064209/how-is-each-with-object-supposed-to-work – Gerry

0

second_arrayは空ですので、それ以上の反復処理で取得したもの(おそらくthird_array)も空です。

def find_hotel 
    hotels = find_all_hotels 

    first_array = hotels 
    .select{|hotel| params[:options][:region].include?("state_#{hotel.state}")} 

    first_array += first_array 
    .select{|hotel| params[:options][:region].include?(hotel.status.to_s)} 

    second_array = third_array = [] 

    ... 
end 
関連する問題