2015-04-04 42 views
1

我有一个事件模型。并且此事件模型has_one地址模型。用户模型也有一个地址模型。我想在将来获取所有事件的列表,但是根据事件地址和用户地址之间的距离进行排序。这个“距离”是我的地址模型中的一种方法。我如何写活动记录的轨道查询?我已经尝试了以下的许多变体,但似乎没有任何工作。Rails根据与子模型方法的关联对结果进行排序

Event.where('end_time > ?', Time.now).includes(Address).order(address.distance_from(User.find(3).primary_address)) 

我总是得到这样的错误:NameError:未定义的局部变量或方法'地址主:对象

事件的定义和用户:

class User < ActiveRecord::Base 
    # Include default devise modules. Others available are: 
    # :confirmable, :lockable, :timeoutable and :omniauthable 

    has_many :addresses 
    belongs_to :primary_address, :class_name => "Address" 


class Event < ActiveRecord::Base 
    extend TimeFromPieces 

    attr_accessor :start_date_string 

    belongs_to :address 
    delegate :timezone, to: :address 

这里是我的地址模型:

class Address < ActiveRecord::Base 
    belongs_to :user 
    has_many :events 

    validates_presence_of :name, :address1, :city, :state, :zip 

    geocoded_by :address_as_string do |obj, results| 
    Rails.logger.debug { "results: #{results}" } 
    obj.lat = results.first.coordinates[0] 
    obj.lng = results.first.coordinates[1] 
    Rails.logger.debug { "Neighborhoods: #{results.first.address_components_of_type(:neighborhood)}" } 
    neighborhoods = results.first.address_components_of_type(:neighborhood) 
    if neighborhoods.count > 1 
     obj.neighborhood = neighborhoods.first["long_name"] 
    end 
    end 

    after_validation :geocode, :if => :address1_changed? 
    after_validation :time_zone_for_address, :if => :address1_changed? 

    before_save :set_primary_if_only_address_for_user 
    before_save :normalize_fields 

    scope :near, ->(lat, lng, distance_in_meters = 2000) { 
    where(%{ 
     ST_Dwithin(
     ST_GeographyFromText(
      'SRID=4326;POINT(' || addresses.lng || ' ' || addresses.lat || ')' 
     ), 
     ST_GeographyFromText('SRID=4326;POINT(%f %f)'), 
     %d 
    ) 
     } % [lng, lat, distance_in_meters]) 
    } 

    def self.distance(address1, address2) 
    query = %{ SELECT 
     ST_Distance(
     ST_GeographyFromText('SRID=4326;POINT(%f %f)'), 
     ST_GeographyFromText('SRID=4326;POINT(%f %f)') 
    ) 
    } % [address1.lng, address1.lat, address2.lng, address2.lat] 

    meters = ActiveRecord::Base.connection.execute(query).values.first.first.to_i 
    meters/1609.34 
    end 

    def distance_from(address) 
    Address.distance(self, address) 
    end 
end 
+0

什么是提供'geocoded_by'的gem? – 2015-04-04 19:26:36

+0

有一点需要注意的是,你被“铁轨魔术”所迷惑了。如果你仔细看看你的代码的这一部分:.order(address.distance_from(User.find(3).primary_address)),你会看到你的错误来自哪里。我猜测变量地址不在范围内。 – digidigo 2015-04-04 21:34:20

+1

你也不能像你在做的那样将ruby代码传递到ActiveRecord方法链中。这可能需要适配器作为SQL查询的一部分执行ruby。这将是超级酷!正如@MohammadAbuShady在这里指导你们一样,用地理能力来扩展AR的宝石是这里的一种方式。 – digidigo 2015-04-04 21:37:45

回答

0

除了使用红宝石地理编码器,你可以使用像宝石geokit-rails ,它提供了对主动记录的地理编码能力,它给了你很好的方法,允许你通过距离直接查询数据库,所以你不需要获取所有的对象来计算它们的距离

你可以说地址是可映射对象,并指出该纬度和经度字段(如果他们没有使用由创业板所使用的默认名称)

class Address 
    acts_as_mappable 
end 

然后,你可以说,事件还通过连接到它的地址对象可映射

class Event 
    acts_as_mappable through: :address 
end 

查询看起来是这样的

Event.where('end_time < ?', Time.now).by_distance(origin: address) 
相关问题