4 Stimmen

Wie um alles in der Welt funktioniert diese Schienenabfrage?

Ich habe gerade einen Ruby-Code optimiert, der in einer Controller-Methode war, und ihn durch eine direkte Datenbankabfrage ersetzt. Der Ersatz scheint zu funktionieren und ist viel schneller. Die Sache ist, ich habe keine Ahnung, wie Rails geschafft, herauszufinden, die richtige Abfrage zu verwenden!

Ziel der Abfrage ist es, die Anzahl der Markierungen für Ortsmodelle innerhalb einer bestimmten Entfernung zu einem bestimmten Breiten- und Längengrad zu ermitteln. Der Teil der Entfernung wird von der GeoKit Plugin (das im Wesentlichen Komfortmethoden hinzufügt, um die entsprechenden trigonometrischen Berechnungen zum Select hinzuzufügen), und der Tagging-Teil wird von der acts_as_taggable_on_steroids Plugin, das eine polymorphe Assoziation verwendet.

Nachstehend finden Sie den ursprünglichen Code:

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

wo die Klasse MyTag dies implementiert:

  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

Dies war mein (sehr hässlicher) erster Versuch, da ich ursprünglich Schwierigkeiten hatte, die richtigen Rails-Beschwörungen zu finden, um dies in SQL zu erreichen. Der neue Ersatz ist diese eine Zeile:

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

Das Ergebnis ist eine SQL-Abfrage wie diese:

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

Abgesehen von der Trigonometrie (die aus GeoKit stammt und aus den Parametern :within und :origin resultiert), kann ich nicht verstehen, wie Rails aus der Anweisung, 'tags' zu verbinden, herausfinden konnte, dass es 'taggings' in den JOIN einbeziehen musste (was es auch tut, da es keinen direkten Weg gibt, die Tabellen places und tags zu verbinden) und dass es das polymorphe Zeug verwenden musste.

Mit anderen Worten: Wie zum Teufel ist sie (korrekt) auf diesen Teil gekommen?

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

...da ich die Tagging-Tabelle im Code nie erwähnt habe! Graben in der taggable Plugin, der einzige Hinweis, dass Rails hat, scheint dies zu sein:

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

...
end

Kann jemand einen Einblick in die Magie geben, die hier unter der Haube vor sich geht?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X