邻接数据结构到嵌套哈希

时间:2013-12-28 14:45:04

标签: ruby-on-rails ruby nested-lists adjacency-list

我在rails中有以下模型:

class Model < ActiveRecord::Base
  # id — integer
  # name — string
  # model_id — integer

  belongs_to :parent, class_name: 'Model', foreign_key: 'model_id'
  has_many :children, class_name: 'Model', foreign_key: 'model_id'
end

我正在使用邻接结构,它可以具有无限深度。我使用递归选择在Postgres数据库上。

获得嵌套的对象哈希的最理智的方法是什么?我尝试选择Model的实例并对它们进行排序,但无法将其带入任何可用的结果。

假设我的数据库中保存了四个Model个实例:Model_1Model_2Model_3Model_4Model_3Model_2的孩子,Model_4Model_3的孩子。

这是我想要实现的输出(Model个实例的嵌套哈希):

{
  #<Model_1...> => {},
  #<Model_2...> => {
    #<Model_3...> => {
      #<Model_4...> => {}
    }
  }
}

有什么想法吗?

更新:树已经恢复 - 无论是CollectionProxy,Relation还是其他任何数组结构。我不想将那棵树排成嵌套哈希的哈希值。

3 个答案:

答案 0 :(得分:1)

我将它命名为parent_id字段。

  belongs_to :parent, class_name: "Model"
  has_many :children, class_name: "Model", foreign_key: "parent_id"

如果您有哈希,则可以使用sortsort_by

http://www.ruby-doc.org/core-2.1.0/Enumerable.html#method-i-sort_by

def sort(hash)
  hash.sort { |m1, m2| m1.id <=> m2.id }
  sort(hash.children)
end

答案 1 :(得分:0)

首先,在Model类中定义ff方法:

def to_hash
  if children.empty?
    {self => {}}
  else
    {self => children.inject({}) {|hash, model| hash.merge(model.to_hash) } }
  end
end

然后执行ff以获得所需的输出:

top_level_models = #code that queries top-level models while eager-loading all nested children
hash_of_nested_models = top_level_models.inject({}) {|hash, ancestor| hash.merge(ancestor.to_hash) }

传递给包含的哈希参数应该涵盖嵌套的深度。上面传递给includes的参数将是具有3个后代深度的孩子的嵌套。只要您在where查询中包含所有嵌套子项,生成哈希将不再执行任何数据库查询。

希望有所帮助!

答案 2 :(得分:0)

在没有N + 1查询的情况下获取AR是很困难的。将不得不写一些与内存中的数据一起使用的东西。

您必须编写一个类似于:

的自定义函数
 def to_hash
  root_hash = {}
  # Load all the model into memory and into a hash to allow faster access when we have the id
  models = Hash[Model.all.collect {|m| [m.id, m]}]
  # The resultant hash for each child
  models_with_hash = Hash[map.values.collect {|m| [m.id, {}]} ]
  # Stitch them together
  models.each do |id, m| 
    if m.model_id.nil?
      root_hash.merge! m => models_with_hash[m.id]
    else
      # should name model_id to parent_id
      models_with_hash[m.model_id].merge! m => models_with_hash[m.id]
    end  
  end
  root_hash
end