我在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_1
,Model_2
,Model_3
和Model_4
。 Model_3
是Model_2
的孩子,Model_4
是Model_3
的孩子。
这是我想要实现的输出(Model
个实例的嵌套哈希):
{
#<Model_1...> => {},
#<Model_2...> => {
#<Model_3...> => {
#<Model_4...> => {}
}
}
}
有什么想法吗?
更新:树已经恢复 - 无论是CollectionProxy,Relation还是其他任何数组结构。我不想将那棵树排成嵌套哈希的哈希值。
答案 0 :(得分:1)
我将它命名为parent_id
字段。
belongs_to :parent, class_name: "Model"
has_many :children, class_name: "Model", foreign_key: "parent_id"
如果您有哈希,则可以使用sort
或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
答案 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