1

我有一个Business模型:belongs_toCategory

类别的示例层次结构:

    • 餐厅
      • 寿司
      • 比萨
      • 中国人
      • 法语
      • 意大利语

我正在使用acts_as_tree层次Category结构。

如何Businesses在餐厅类别下找到所有内容?

4

3 回答 3

2

如果您想在树状层次结构中获取节点的所有后代,您有两种选择:

  1. 使用经典acts_as_tree,预加载所选类别,启动级联查询以检索所有子代、孙代等,直到您只获得叶子(没有更多子代的节点)。这种方法和听起来一样好。

  2. 使用更高级的树表示,例如嵌套集或闭包树。在这些表示下,您只需一个查询即可获得特定节点的所有后代。

然后在Businesses中获取收集的类别和查询:

Business.where(:category => categories)

(技术解释:嵌套集)

在嵌套集表示下,每个节点都有两个索引,分配方式如下:想象每个节点是一个有两个窗户的房子,东和西,树就像一条分叉的道路,所有孩子或多或少都到他们父母的北边。所以你从根房子的东边开始,在你遇到的窗户上放一个序号。你从不穿过任何马路,你只被允许绕过没有进一步通往北方的道路的房屋。最后你会再次回到根屋,并在西窗上放一个号码。

分配的数字将具有以下属性:

  • 所有东窗都有偶数,所有西窗都有奇数(前提是你从 0 开始)
  • 每个房子的delta(东西数之差)总是1 + 后代房子的数量
  • 任何 H 宫的所有子孙宫都将其东、西编号严格包含在 H 自己的编号之间
  • 反之亦然,所有数字严格包含在 H 的数字之间的房子实际上都是 H 的后代
  • 如果两所房子不是另一所房子的祖先(所以一个人的名字包含在另一个房子之间),那么它们是完全分开的,也就是说,一个房子的两个数字都严格小于另一个房子的两个数字。

因此,虽然在树中插入一个新元素的成本很高(它需要更新许多索引),但检索整个后代(所有子代、孙代......)非常容易,只需选取“东”和“西”数字介于您选择的类别的东和西之间。您实际上可以做得更好,但这并不重要。

https://github.com/collectiveidea/awesome_nested_set这样的库将为您管理所有这些,您只需调用

categories = @category.self_and_descendants.to_a

(技术解释:闭包树)

这种方法需要一个附件表,您可以在其中存储child->parent关系的传递闭包(请参阅http://en.wikipedia.org/wiki/Reflexive_transitive_closure#P_closures_of_binary_relations

该表将包含所有祖先-后代对,因此您可以以智能方式加入该表以获得几乎任何层次结构的切片。

同样,像https://github.com/mceachen/closure_tree这样的库将为您完成艰苦的工作,您将能够做到

categories = @category.self_and_descendants.to_a
于 2012-10-30T15:03:51.320 回答
0

你能不能只做这样的事情:

Category.find_by_name("Restaurants").businesses

编辑:

没有意识到您也想要餐厅子类别中的企业,呵呵。

对于多个层次结构,您首先需要获取所有类别,然后遍历每个类别,找到业务,然后将它们连接在一起

class Company < ActiveRecord::Base

  ...

  def all_children
    all = []
    self.children.each do |c|
      all << c
      root_cs = c.all_children.flatten
      all << root_cs unless root_cs.empty?
    end
    return all.flatten
  end
end

然后你可以打电话:

root_category = Category.find_by_name("Restaurants")
categories = root_category.all_children
businesses = categories.map{ |c| c.businesses }.flatten

这应该会返回您的企业列表。虽然看起来不太好,但我觉得应该有一个更优化的方法。

希望无论如何它应该给你一些思考的食物。

于 2012-10-30T12:50:26.690 回答
0
category = Category.find_by_name("Restaurants")

那么对于业务

category.business

如果你想要孩子喜欢(寿司、比萨、中餐……)那么

category.childrens

要查找所有类别,请在 category.rb 中添加以下方法

  def all_children
    all = []
    self.children.each do |category|
      all << category
      root_children = category.all_children.flatten
      all << root_children unless root_children.empty?
    end
    return all.flatten
  end

然后使用

@category.all_children

已编辑 查找餐厅类别和餐厅所有子类别的企业。

Business.where("category_id = ? OR category_id in (?)", category.id, category.all_children.map(&:id))
于 2012-10-30T12:48:57.623 回答