2009-07-08 4 views
4

컨트롤러 메소드에 있던 Ruby 코드를 최적화하여 직접적인 데이터베이스 쿼리로 대체했습니다. 교체가 제대로 작동하고 훨씬 빨라졌습니다. 문제는 Rails가 올바른 쿼리를 찾아 낼 수 있었는지 전혀 모르겠습니다.레일즈 쿼리는 어떻게 작동합니까?

위 쿼리의 목적은 지정된 위도와 경도의 특정 거리 내에있는 장소 모델의 태그 수를 계산하는 것입니다. 거리 부분은 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에 '태그 지정'을 포함해야한다는 것입니다. 장소와 태그 테이블을 결합하는 직접적인 방법), 또한 다형성 (polymorphic)의 것들을 사용해야만했습니다. 즉

은 도대체 그것을 어떻게 (제대로)이 비트 마련 :

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

... 나는 코드에서 taggings 테이블을 언급 한 적이 주어진! 여기에 후드 아래에가는 마법에 대한 통찰력을 줄 수

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

... 
end 

누구나 다음 taggable 플러그인에 파고, 레일이있는 유일한 단서는이 것 같다?

답변

1

acts_as_taggable_on_steroids 플러그인은 장소 모델에 태그가있는 태그가 있음을 알립니다. 이 연관이 지정되면 ActiveRecord는 태그 테이블에 도달하기 위해 태그 지정을 조인해야한다는 것을 알고 있습니다. HABTM 관계에서도 마찬가지입니다. 예를 들면 :

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이 생성

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 
관련 문제