ruby-on-rails – Rails 3多模型查询

我有以下型号:

class Location < ActiveRecord::Base
    has_many :location_items
    has_many :items, :through=>:location_items
end

class Item < ActiveRecord::Base
    has_many :location_items
    has_many :locations, :through=>:location_items
end

class LocationItem < ActiveRecord::Base
    belongs_to :item
    belongs_to :location
end

此外,我可以为项目模型启用全文搜索(来自gem),我可以这样做
Item.search(‘keyword’) – ‘search’是gem提供的范围,用于获取所有名称或描述匹配关键字的项目,结果项目添加了’rank’属性以匹配相关性

我也可以为位置模型启用Geo搜索(来自gem),我可以这样做
Location.near(‘Toronto.ON’,100)—‘near’是宝石提供的范围,用于获取距离多伦多100公里范围内的所有位置,结果位置添加了距离给定距离的“距离”属性location – 这个例子中的多伦多

所以现在我要做的是获取一个location_item对象列表,这些对象的位置匹配某个给定的位置和项目匹配给定的关键字.
例如,搜索匹配“关键字”且位于多伦多100公里范围内的location_item对象.

如何通过一个查询实现此目的?并且还可以通过location_item对象内的关联项和位置访问距离和排名属性.

我似乎无法链接范围,因为他们只处理Item和Location,而不是LocationItem,

例如,下面的表达式将不起作用

LocationItem.joins(:items,locations).search(‘keyword’).near(‘Toronto,ON’,100)

希望我对我想要做的事情的描述是有道理的.你有什么主意吗?非常感谢你!

最佳答案 基本上,您将无法在一个查询中执行您想要的所有操作并保留您正在查找的常规LocationItem#location和LocationItem#item接口.我将假设您正在使用Postgres的全文搜索,因为如果您使用Solr或Sphinx **,这个问题没有意义*.

如果你想要一个查询,但不介意放弃你返回的元素的belongs_to接口:ActiveRecord :: Base自动分配查询的SELECT部分​​提供的属性,如下所示:Location.select( ‘id 1 as more_id’).first.more_id#=> 2这样您就可以使用它,并创建具有位置和item以及item_rank和location_dist的相应部分的属性.

class LocationItem < ActiveRecord::Base

  #This presumes that your geo and search gems actually return scopes that 
  # properly respond to to_sql (which they should if you're using rails 3).
  def self.local_matching_items(text, dist=100)
    LocationItem
      .joins("INNER JOIN #{Item.search(text).to_sql} as matching_items 
                                          on matching_items.id 
                                          = location_items.item_id")
      .joins("INNER JOIN #{Location.near(dist).to_sql} as nearby_locations
                                            on nearby_locations.id 
                                            = location_items.location_id")
      .select("location_items.id, nearby_locations.distance, matching_items.rank,
               nearby_locations.other_field_you_might_want as
                location_other_field_you_might_want,
               matching_items.name as item_name, etc")
      #returns LocationItems with #id, #distance, #rank,
              # location_other_field_you_might_want, #item_name, etc
              #It might be most helpful to distinguish between these
              # and normal location_item's by storing them in variables with a 
              # different naming convention like nearby_item_matches.
  end
end

*因为那时你会做两个查询,一个用于通过搜索提供商匹配关键字,一个用于从数据库中获取记录.

**如果您正在使用ThinkingSphinx,它已经支持searching by geographical distance,但您必须以不同方式定义索引并以不同方式调用LocationItem #search.

点赞