2009-07-08 3 views
4

コントローラメソッドにあったいくつかのRubyコードを最適化し、直接データベースクエリに置き換えました。交換が正常に動作しているように見え、はるかに高速です。それは、Railsがどのように正しいクエリを使い分けることができたか分かりません。このレールクエリはどのように地球上で動作していますか?

クエリの目的は、指定された緯度と経度から特定の距離内にあるPlaceモデルのタグ数を計算することです。距離部分はGeoKitプラグインで処理されます(選択に適切な三角法計算を追加する便利な方法が基本的に追加されています)。タグ付け部分は多形関連を使用するacts_as_taggable_on_steroidsプラグインによって行われます。以下は

元のコードです:

mytagというクラスがこれを実装し
places = Place.find(:all, :origin=>latlng, :order=>'distance asc', :within=>distance, :limit=>200) 
tag_counts = MyTag.tagcounts(places) 
deep_tag_counts=Array.new() 
tag_counts.each do |tag| 
    count=Place.find_tagged_with(tag.name,:origin=>latlng, :order=>'distance asc', :within=>distance, :limit=>200).size 
    deep_tag_counts<<{:name=>tag.name,:count=>count} 
end 

:私はもともとそれが困難を思い付く見られるよう

def MyTag.tagcounts(places) 
    alltags = places.collect {|p| p.tags}.flatten.sort_by(&:name) 
    lasttag=nil; 
    tagcount=0; 
    result=Array.new 
    alltags.each do |tag| 
     unless (lasttag==nil || lasttag.name==tag.name) 
     result << MyTag.new(lasttag,tagcount) 
     tagcount=0 
     end 
     tagcount=tagcount+1 
     lasttag=tag 
    end 
    unless lasttag==nil then 
     result << MyTag.new(lasttag,tagcount) 
    end 
    result 
    end 

これは私の(非常に醜い)初めての試みでしたSQLでこれを行うための権利の手配。

deep_tag_counts=Place.find(:all,:select=>'name,count(*) as count',:origin=>latlng,:within=>distance,:joins=>:tags, :group=>:tag_id) 

このようなSQLクエリで結果:新しい交換は、この単一のラインで内と::

SELECT name,count(*) as count, (ACOS(least(1,COS(0.897378837271255)*COS(-0.0153398733287034)*COS(RADIANS(places.lat))*COS(RADIANS(places.lng))+ 
COS(0.897378837271255)*SIN(-0.0153398733287034)*COS(RADIANS(places.lat))*SIN(RADIANS(places.lng))+ 
SIN(0.897378837271255)*SIN(RADIANS(places.lat))))*3963.19) 
AS distance FROM `places` INNER JOIN `taggings` ON (`places`.`id` = `taggings`.`taggable_id` AND `taggings`.`taggable_type` = 'Place') INNER JOIN `tags` ON (`tags`.`id` = `taggings`.`tag_id`) WHERE (places.lat>50.693170735732 AND places.lat<52.1388692642679 AND places.lng>-2.03785525810908 AND places.lng<0.280035258109084 AND (ACOS(least(1,COS(0.897378837271255)*COS(-0.0153398733287034)*COS(RADIANS(places.lat))*COS(RADIANS(places.lng))+ 
COS(0.897378837271255)*SIN(-0.0153398733287034)*COS(RADIANS(places.lat))*SIN(RADIANS(places.lng))+ 
SIN(0.897378837271255)*SIN(RADIANS(places.lat))))*3963.19) 
<= 50) GROUP BY tag_id 

GeoKitからである三角を(無視する、との結果原点パラメータ)、私がこれについて理解できないのは、Railsが「タグ」に参加するという指示からどのように理解できたか、JOINに「タグ付け」を含める必要があったかということです。場所やタグのテーブルに参加する直接的な方法)、多態的なものを使用しなければならないということです。いったいそれをどうやったのかつまり

、(正しく)このビットを思い付く:

INNER JOIN `taggings` ON (`places`.`id` = `taggings`.`taggable_id` AND `taggings`.`taggable_type` = 'Place') INNER JOIN `tags` ON (`tags`.`id` = `taggings`.`tag_id`) 

...私はコード内のタギング表を述べたことがないことを考えます!ここではフードの下で起こって魔法のいくつかの洞察を与えることができる

class Tag < ActiveRecord::Base 
    has_many :taggings, :dependent=>:destroy 

... 
end 

誰:タグ付けできるプラグインに掘り、Railsが持っている唯一の手掛かりはこれのようですか?

答えて

1

acts_as_taggable_on_steroidsプラグインは、あなたのPlaceモデルに、タギングからhas_manyタグを通知します。この関連付けが指定されると、ActiveRecordはタグテーブルに到達するためにタグ付けを結合する必要があることを認識します。 HABTMの関係についても同じことが言えます。たとえば、次のように

SELECT "people".* 
FROM "people" 
    INNER JOIN "people_tags" ON "people_tags".person_id = "people".id 
    INNER JOIN "tags" ON "tags".id = "people_tags".tag_id 
LIMIT 1 

class Person < ActiveRecord::Base 
    has_and_belongs_to_many :tags 
end 

class Tag < ActiveRecord::Base 
    has_and_belongs_to_many :people 
end 

>> Person.first(:joins => :tags) 

これは、次のSQLを生成します

関連する問題