2017-01-12 11 views
1

これが既に存在する場合、私は見てきましたが、この正確な質問を見つけることができません。Rails、グループクエリの結果を繰り返す

私は3つのActiveRecordモデルContinent,Country、およびTownがあります。 Townには属性​​があります。私は、各国の人口を、大陸別にグループ分けし、各町の人口を集計することで分解したいと考えています。

国の多くの町が、大陸では比較的少数の国、そして唯一のいくつかの大陸があるので、私はこのような何かやって進むかもしれません:

continents = {} 
Continent.each do |continent| 
    country_breakdown = {} 
    continent.countries.each do |country| 
    country_breakdown[country] = country.towns.sum(:population) 
    end 
    continents[continent] = country_breakdown 
end 

...にcontinentsを反復処理に続いてのビュー。

問題は、これはSQLの複雑さがO(n)であることです。ここで、nは国の数です。

私の質問は次のとおりです。このグループ化された結果をSQLに生成させる方法はありますか?それでは、Railsのビュー側で通常のネストされたHashのように繰り返すことができますか?

私はActiveRecord::QueryMethods#groupで作業しようとしましたが、その結果をどのように反復するのか全くわかりません。

答えて

1

はい、あります。あなたは完全に記述あなたの関係はしていると仮定すると:

class Continent 
    has_many :countries 
    has_many :towns, through: :countries 
end 

class Country 
    belongs_to :continent 

    has_many :towns 
end 

class Town 
    belongs_to :country 

    has_one :continent, through: :country 
end 

あなたは実行することができます:フォーマット[[continent_id, country_id], population]であなたのハッシュを与える

results = Continent.joins(:town).group(:continent_id, :country_id).sum(:population) 

。ザ・参加

e.g. {[2, 38]=>39993, [1, 31]=>127425, [5, 12]=>113556, [5, 76]=>2966, [1, 10]=>263473, [1, 34]=>154492, [2, 37]=>55087...} 

あなたはContinentが原因has_many through:関係の知っているあなたのクエリでTownモデル、にアクセスすることができます。

あなたはよりあなたが好きな形式でデータを取得するために、そのハッシュに繰り返すことができます。そう、この手順を実行するために、より効率的な方法があります

formatted = results.each_with_object({}) do |((continent_id, country_id), pop), m| 
    m[continent_id] ||= {} 
    m[continent_id][country_id] = pop 
end 

e.g. { continent_id => { country_id => population } } 
    {2=>{38=>39993, 37=>55087}, 1=>{31=>127425, 10=>263473...} 

。どの項目がどの値を参照しているか分からないように、各項目を実際のオブジェクトにマップすると便利です。しかし、いずれにしても、データベースへのアクセスが1つのクエリにまで減少し、元の実装よりもはるかに高速になります。

+0

ああ、完璧!あなたが資格を持っているので、私たちの団体は正しく定義されていなかったので、 'group'は私のために働いていませんでした。ありがとうございました! – dfaulken

関連する問題