简体   繁体   English

邻接数据结构到嵌套哈希

[英]Adjacency data structure to nested hash

I have the following model in rails: 我在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

I am using adjacency structure, which can have infinite depth. 我正在使用邻接结构,它可以具有无限深度。 I am on a Postgres database using recursive selects. 我使用递归选择在Postgres数据库上。

What will be the most sane way to get a nested hash of objects? 获得嵌套的对象哈希的最理智的方法是什么? I tried to select instances of Model and sort them, yet could not bring this to any usable result. 我试图选择Model实例并对它们进行排序,但却无法将其带入任何可用的结果。

Lets say I have four Model instances saved in my database: Model_1 , Model_2 , Model_3 and Model_4 . 比方说,我有四个Model保存在我的数据库实例: Model_1Model_2Model_3Model_4 Model_3 is a child of Model_2 and Model_4 is a child of Model_3 . Model_3Model_3的子Model_2Model_4Model_4的子Model_3

Here is an output I am trying to achieve (a nested hash of Model instances): 这是我想要实现的输出( Model实例的嵌套哈希):

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

Any ideas? 有任何想法吗?

Update: Tree is already recovered — either as a CollectionProxy, Relation or any other array-ish data structure. 更新:树已经恢复 - 作为CollectionProxy,Relation或任何其他array-ish数据结构。 I wan't to sort that tree into the hash of nested hashes. 我不想将那棵树排成嵌套哈希的哈希值。

I would name it parent_id field. 我将它命名为parent_id字段。

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

When you have the hash, you would use sort or sort_by : 当你有哈希时,你会使用sortsort_by

http://www.ruby-doc.org/core-2.1.0/Enumerable.html#method-i-sort_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

First, define the ff method inside your Model class: 首先,在Model类中定义ff方法:

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

Then do the ff to get the output you want: 然后执行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) }

The hash argument that you pass to includes should cover the depth of your nesting. 传递给包含的哈希参数应该覆盖嵌套的深度。 The argument passed to includes above will be the nesting for children with a depth of 3 descendants. 传递给上面includes的参数将是具有3个后代深度的子项的嵌套。 For as long as you includes all the nested children in your where query, generating the hash will not do any more db queries. 只要您在where查询中包含所有嵌套子项,生成哈希将不再执行任何数据库查询。

Hope that helps! 希望有所帮助!

Getting AR to do this without N+1 queries would be difficult. 在没有N + 1个查询的情况下让AR执行此操作将非常困难。 Will have to write something that works with the data in memory. 将不得不写一些与内存中的数据一起使用的东西。

You would have to write a custom function which looks something like: 您必须编写一个类似于以下内容的自定义函数:

 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM