如何检索所有项目的孩子?递归?棘手的SQL查询?

时间:2015-07-09 15:01:08

标签: ruby-on-rails-4 activerecord recursion

Items通过parent_id属性组织在文件夹/树状结构中。因此可以创建以下层次结构: Folder structure of items

问题是 - 我想不出一种优雅的方法来检索Item的所有子项目树。一种解决方案是使用递归。 我的递归解决方案(recursive_subitems方法)目前无效。但即使它会起作用 - 我还有兴趣,如果还有其他的递归替代方案吗?也许一些棘手的SQL查询?

# == Schema Information
#
# Table name: items
#
#  id               :integer          not null, primary key
#  name             :string
#  parent_id        :integer

class Item < ActiveRecord::Base

  # for item with ID 1 must return items with IDs [2, 3]
  def immediate_subitems 
    Item.where(parent_id: self.id)
  end

  # My failing attempt to create a recursive function...
  def recursive_subitems(parent_id)        
    items_ids = Item.where(parent_id: parent_id).map(&:id)
    items_ids.each do |item_id|
        recursive_subitems(item_id)
    end
    items_ids
  end

  # for item with ID 1 must return items 
  # with IDs [2,3,4,5,6,7,8,9,10,11]
  def all_subitems_tree
    recursive_subitems(self.id)
  end
end

3 个答案:

答案 0 :(得分:1)

这里的工作递归方法:

def recursive_subitems(parent_id)
    # byebug
    result = []
    items_ids = Item.where(parent_id: parent_id).map(&:id)
    return [parent_id] if items_ids.empty?

    items_ids.each do |item_id|
        result << recursive_subitems(item_id)
    end
    result << parent_id
end

def all_subitems_tree
    puts "subitems tree for #{self.id}"
    ids = recursive_subitems(self.id).flatten
    ids.pop
    Item.find(ids)
end

答案 1 :(得分:0)

有一个名为ActsAsTree的简单宝石。 它是过去Rails的一部分

它有一些简化器,包括方法.walk_tree,它可以执行@yaru proposed

但是在this detailed post

中描述的关系数据库中还有许多其他更有效的方法来处理分层数据。

答案 2 :(得分:0)

在RDMS中有两种主要模式可以有效地表示树结构:物化路径和嵌套集。

https://github.com/stefankroes/ancestryhttps://github.com/ClosureTree/closure_tree都实现了物化路径,尽管有所不同。阅读这篇关于它们相对优点的文章 - https://www.hilman.io/blog/2015/09/comparing-ancestry-and-closure_tree/

https://github.com/collectiveidea/awesome_nested_set实现了嵌套集模式。

物化路径模式的基本思想是每个元素还包括从根节点到自身的完整路径。 Ancestry通过添加包含此路径的文本ancestry列来完成此操作:10/34/78/...。找到节点的所有子节点然后简化为一个简单而有效的索引前缀字符串搜索(where ancestry like 'id1/id2/%)。

Closure Tree将它保存在一个单独的表中,但也使这种搜索非常有效。

嵌套集的基本思想是除了parent_id之外,它还有leftright个边界,其值保持不变,以便所有子ID都在它们之间。在这种情况下,找到它们变得简单where id between left and right

所有这三种实现都允许您在大多数读操作中减少n + 1。写操作(添加,更改,删除,移动节点)具有不同的成本,因此如果您计划经常更新树,请务必检查它们。