7

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

我正在构建一个评论系统,其中用户可以使用多态可评论来构建几种不同类型的评论。

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)

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

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

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

我可以给地方标签(即威廉斯堡,布鲁克林)belong_to NY City 并且标签属于 NY 吗?

标签更灵活,可以选择性地解释它们可能在哪些区域,标签属于一个城市,但也有地点并且可以审查?

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

使用 Rails 3.2 和 mongoid。

4

4 回答 4

3

我已经构建了一些非常相似的东西,并发现了两种完全不同的方式,它们都运作良好。

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

对我有用的第一种方法是使用 Country、Subcountry、City、Neighborhood。这很好地映射到主要的地理编码服务,足以满足大多数简单的用途。这可以是 STI(如您的示例)或多个表(我是如何做到的)。

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

这是我经过多次尝试尝试模型名称(例如 Region、District、Area、Zone 等)并放弃这些过于模糊或过于具体的实验后所决定的。在您的 STI 情况下,使用更多名称可能会很好。

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

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

方式 2:区域是带有边界框的 lat/lng 形状列表

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

Postgres 有一些很好的地理编码助手,你可以通过做 min/max lat/lng 的边界框来加快查找速度。我们还存储了数据,例如区域的预期中心点(我们将在地图上放置图钉的位置)和半径(用于计算诸如“显示 y 距离内的所有 x 项)之类的查询。

通过这种方法,我们能够制作有趣的区域,例如“纽约的百老汇”,它实际上不是一个街区,而是长街,以及由河流而非国家定义的“亚马逊盆地”。

于 2012-03-30T12:17:18.060 回答
2

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

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

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

作为将 STI 与祖先结合使用的额外好处,您还可以对其他与位置相关的节点进行建模,比如餐馆或其他地方。

您还可以将地理位置信息纬度/经度添加到所有节点,以便您可以对它们进行地理标记。在下面的示例中,我只使用了一组地理位置坐标(中心点) - 但您当然也可以添加几个地理位置来模拟边界框。

您可以按您喜欢的任何顺序排列节点,例如通过 this_node.children.create(...). 当使用带有祖先的 MySQL 时,您可以传入新创建的节点的类型。mongoid-ancestry 一定有类似的方式(没试过)。

除了树状结构节点之外,您还可以使用多态集合对评论和标签进行建模(嗯,acts_as_taggable 有一个 gem,因此您不必自己对标签进行建模)。

与使用自己的集合对每个类进行建模相比,这种 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)。祖先是我见过的最有用的宝石。

于 2012-03-31T17:37:03.547 回答
1

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

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

它基本上是使用标签,就像你躲避的那样,但并不是真正使用标签模型本身,而是与所有行为都像标签的不同模型的多态关联。

保持您的数据库架构干净,并将业务逻辑保持在 ruby​​ 中。

于 2012-03-26T23:27:39.323 回答
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.

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

另外,请参阅Rails 路由指南nested resources部分,以及关于多态关系的 RailsCast部分,其中有一个关于路由的快速部分,并让所有内容正确排列。

于 2012-03-22T15:06:46.270 回答