轨道半复杂STI,具有规划路线和控制器的祖先数据模型

时间:2012-03-22 14:58:28

标签: ruby-on-rails ruby-on-rails-3 routes mongoid sti

我正在尝试找出管理特定用例的控制器和模型的最佳方法。

我正在构建一个审核系统,用户可以使用Polymorphic Reviewable建立对多个不同类型的评论。

Country (has_many reviews & cities)
Subdivision/State (optional, sometimes it doesnt exist,  also reviewable, has_many cities) 
City (has places & review)
Burrow (optional, also reviewable ex: Brooklyn)
Neighborhood (optional & reviewable, ex: williamsburg)
Place (belongs to city)

我也想知道增加更多的复杂性。我还想偶尔包括细分...即对于美国,我可能会添加德克萨斯州或德国,Baveria并且也可以审查它,但并非每个国家都有地区,甚至那些可能永远不会被审查的地区。所以它一点也不严格。我希望它尽可能简单灵活。

如果用户可以登陆一个表单并选择城市或国家,然后使用Foursquare中的数据向下钻取以查找城市中的特定位置并进行审核,那就太好了。

我真的不确定应该走哪条路?例如,如果我有一个国家和一个城市会发生什么......然后我决定添加一个陋居?

我可以将地点标签(即威廉斯堡,布鲁克林)归属于纽约市并且标签属于纽约吗?

标签更灵活,可选择解释它们可能属于哪些区域,标签属于某个城市,但也有地点和可审核?

所以我正在为那些做过相关事情的人寻找建议。

使用Rails 3.2和mongoid。

4 个答案:

答案 0 :(得分:3)

我已经建立了非常相似的东西,发现两种完全不同的方式,两者都运作良好。

方式1:国家»次国家»城市»邻里

对我有用的第一种方式是使用国家,子国家,城市,邻居。这很好地映射到主要的地理编码服务,并且足以用于大多数简单的用途。这可以是STI(如您的示例所示)或多个表(我是如何做到的)。

在你的例子中,你写了“细分/状态”。我的两分钱是为了避免使用这些术语,而是使用“Subcountry”,因为它是ISO标准,当另一个开发人员认为细分是一个小房子附近,或者当你有一个非美国国家时,你会省去一些困惑不使用状态,而是使用省份。

这是我在尝试模型名称(如地区,区域,区域,区域等)的许多实验后所确定的,并且放弃这些过于模糊或过于具体。在您的STI案例中,可以使用更多名称。

令人惊讶的是,编写多层次的关联是一个很大的帮助,例如说country.cities(跳过子类)。这是因为有时中间模型不存在(即没有子国家)。在您的STI中,这可能会更棘手。

如果你对表格进行非规范化,你也会获得很大的加速,所以例如我的城市表有一个国家字段。这使得更新信息有点棘手,但值得。您的STI可以通过使用标签来实现与此相当的效果。

方式2:带有边界框

的纬度/经度形状列表的区域

第二种方法是使用任意区域模型并存储纬度经度形状。这为您提供了极大的灵活性,您可以预先计算形状何时包含其他形状或与其相交。所以你的“向下钻取”变成“在这一个内部立即显示我的形状”。

Postgres为此提供了一些很好的地理编码助手,您可以通过执行min / max lat / lng的边界框来加速查找。我们还存储了数据,例如区域的预期中心点(我们将地图放置在地图上的位置)和半径(用于计算查询,例如“向我显示y距离内的所有x项目)。”/ p >

通过这种方法,我们能够做一些有趣的区域,比如“纽约的百老汇”,这里并不是长街,而是“亚马逊盆地”,它是由河而不是国家定义的。

答案 1 :(得分:2)

具有祖先和多元关系的STI模型

我为之前的项目构建了类似的东西,并且为了具有祖先的STI,因为它非常灵活并且允许对节点树进行建模。并非所有中间节点都必须存在(如您的State / Subdivision / Subcountry示例中所示)。

对于Mongoid,至少有两个祖先宝石:mongoid-ancestry和mongestry(下面的链接)。

作为使用带有祖先的STI的额外好处,您还可以为其他与位置相关的节点建模,例如餐馆或其他地方。

您还可以向所有节点添加地理位置信息lat / lon,以便对其进行地理标记。 在下面的示例中,我只使用了一组地理位置坐标(中心点) - 但您当然也可以添加几个地理位置来为边界框建模。

您可以按自己喜欢的顺序排列节点,例如通过this_node.children.create(...)。 使用带有祖先的MySQL时,可以传入新创建的节点的类型。 mongoid-ancestry必须有类似的方法(还没试过)。

除了树形结构的节点之外,您还可以使用多态集合来对评论进行建模,还可以使用标签(好吧,act_as_taggable有一个宝石,所以您不必自己模拟标签)。

与使用自己的集合对每个类进行建模相比,这种STI方法更灵活,并且使模式更简单。稍后添加新类型的节点非常容易。

此范例可与Mongoid或SQL数据存储一起使用。

# app/models/geo_node.rb

class GeoNode  # this is the parent class; geo_nodes is the table name / collection name.
  include Mongoid::Document
  has_ancestry   # either this
  has_mongestry  # or this
  has_many :reviews, :as => :reviewable

  field :lat, type: Float
  field :lon, type: Float

  field :name, type: String
  field :desc, type: String
  # ...
end

# app/models/geo_node/country.rb
class Country < GeoNode
end

# app/models/geo_node/subcountry.rb
Class Subcountry < GeoNode
end

# app/models/geo_node/city.rb
class City < GeoNode
end


# app/models/review.rb
class Review
  include Mongoid::Document
  belongs_to :reviewable, :polymorphic => true
  field :title
  field :details
end

检查以下链接:

非常感谢Stefan Kroes他出色的祖先宝石,以及Anton Orel将其改编为Mongoid(mongoid-ancestry)。祖先是我见过的最有用的宝石。

答案 2 :(得分:1)

听起来像是嵌套路由/资源的一个很好的候选者。在routes.rb中,执行以下操作:

resources :cities do
  resources :reviews
end

resources :countries do
  resources :reviews
end

resources :places do
  resources :reviews
end

哪个应该产生rake routes

的内容
   reviews_cities GET /cities/:id/reviews     {:controller=>"reviews", :action=>"index"}
reviews_countries GET /countries/:id/reviews  {:controller=>"reviews", :action=>"index"}
   reviews_places GET /countries/:id/reviews  {:controller=>"reviews", :action=>"index"}

...etc., etc.

在控制器操作中,您查找匹配:id reviewable条记录,并仅发回附加到该reviewable对象的评论。

另外,请参阅Rails Routing Guidenested resources部分和this RailsCast on Polymorphic relationships部分,其中包含有关路由的快速部分,并让所有内容正确排列。

答案 3 :(得分:1)

我可能会保持我的数据模型非常不受限制,并处理与控制器/视图中显示的过滤器相关的任何细节。制作一个映射表,您可以在其中以多态方式将属性(即城市,自治市镇,州,国家/地区)映射到reviewable

通过假定多对多,您的架构尽可能灵活,并且您可以使用模型中的验证或过滤器来限制要创建的映射。

它基本上是使用标记,就像你所说的那样,但并不真正使用标签模型本身,而是与不同模型的多态关联,它们都像标签一样。

保持数据库架构清洁,并将业务逻辑保留在ruby中。