与地理编码器 has_many 和 belongs_to 关联的非常奇怪的结果

Very weird result with geocoder has_many and belongs_to association

我正在使用 Geocoder Gem,它显示有线行为。

我有位置模型

class Location < ActiveRecord::Base
has_many :events
geocoded_by :address
after_validation :geocode
def full_address
    "#{postal_code}, #{city}, #{street}"
  end
end

和事件模型

class Event < ActiveRecord::Base
    belongs_to :location
    accepts_nested_attributes_for :location
end

我想查找当前用户位置附近的所有事件。

我尝试按照以下步骤查找附近的位置..

nearby_loc = current_user_location.nearby

它正在返回当前用户位置"locations"附近的所有

然后我试了

nearby_loc.events

但它给我错误

NoMethodError: undefined method `events' for #<ActiveRecord::Relation::ActiveRecord_Relation_Location:0x0000000958c088>

请帮帮我...

events是在一个位置上定义的,并且 nearby 将为您提供一个位置列表,因此您必须遍历该列表。

简单地说:

all_related_events  = []
nearby_locations.includes(:events).each do |location|
  all_related_events += location.events
end 

如果您的变量名更正确地反映了它们包含的内容,这也会有所帮助,因此请使用 nearby_locations 而不是 nearby_loc

[更新]

为了尽量减少查询数量,我添加了 .includes(:events),它将在一个查询中获取所有事件。

NoMethodError: undefined method `events' for ActiveRecord::Relation::ActiveRecord_Relation_Location

您的 nearby_locActiveRecord::Relation,因此 nearby_loc.events 导致错误。您应该遍历 nearby_loc 以使其正常工作。

nearby_loc.each do |n|
n.events
end

我更喜欢从我想要获得的模型开始查询。除了获得 "a collection from collection of collections",您还可以...

Location 加入所有事件,以便您可以获取所有 Event,其 Location 符合特定条件。大多数时候范围只是一堆条件,可以是 merged.

像这样:

Event.joins(:location)                    # Results in `INNER JOIN`
     .merge(current_user_location.nearby) # Adds in the conditions for locations

但事情并没有那么简单!

Geocoder 在幕后做了一个非常复杂的 select,并添加了一些有用的字段,例如 distance,这些字段取决于输入范围的点。我们不能失去这些,对吧?查询将不再有意义。

一个选项是以非常奇怪的方式执行INNER JOIN:通过指定FROM子句从two tables(稍后会详细介绍)并在 WHERE 子句中指定连接条件。为此,我们需要一些 Arel,所以让我们提前获取 tables:

locations = Location.arel_table
events    = Event   .arel_table # Yeah, call me an indentation maniac

现在有一个问题:我们将使用 current_user_location.nearby 形成的子查询的结果,而不是 locations table。如何?我们将向 from 提供一系列我们想要使用的东西:

Event.from([current_user_location.nearby.as('locations'), events])
           # ^ an array, yeah!

我们这里有:

select events.* from (geocoder subquery) locations, events

现在呢?一个连接条件。正如我所说,由于我们正在创建一个 奇怪的连接 ,我们将在 where 中指定连接条件。我们必须。

Event.from([current_user_location.nearby.as('locations'), events])
     .where(location_id: locations[:id])

...这应该可以正常工作。至少完全由数据库完成。

在这种情况下

nearby_loc = current_user_location.nearby

returns 位置列表,而不是单个位置。

要遍历它们并找到每个位置的事件,您可以使用

nearby_events = nearby_loc.map {|loc| loc.events}

但是,就总查询而言,这效率不高。