ruby 获取属于某个模型的所有嵌套对象

wvmv3b1j  于 2023-06-22  发布在  Ruby
关注(0)|答案(2)|浏览(100)

我有一个Location模型,它可以有许多子位置,或者一个父位置。假设location-A是父位置,具有location-B和location-C作为子位置。但是位置-B也具有子位置位置-D。
如何获取父位置A的所有子位置(包括位置D)?
我的型号:

has_many :sub_locations, class_name: "Location", foreign_key: "parent_location_id", inverse_of: :parent_location
  belongs_to :parent_location, optional: true, class_name: "Location", foreign_key: "parent_location_id", inverse_of: :sub_locations

目前我是这样获取它们的:

def all_sub_location_ids
    [id] + sub_locations.map(&:all_sub_location_ids).flatten
  end

但我需要一个有效的方法。因为它抛出stack level too deep
编辑:我最终在子位置上使用了.reload,并保持现有的方法不变。这样就成功了。

relj7zay

relj7zay1#

您可以使用gem ancestry
请看官方文档here
在您的gemfile中添加宝石

gem 'ancestry'

创建迁移

rails g migration add_Add_ancestry_to_location ancestry:string:index

在您的Location模型中添加如下:

class Location < ApplicationRecord
  has_ancestry
end

现在你可以使用如下:

location_a = Location.create(name: 'Location A')
location_b = Location.create(name: 'Location B', parent: location_a)
location_c = Location.create(name: 'Location C', parent: location_a)
location_d = Location.create(name: 'Location D', parent: location_b) 

parent_location = Location.find_by(name: 'Location A')
sublocations = parent_location.descendants

这里,子位置将是位置为Location BLocation CLocation D的数组。

f5emj3cl

f5emj3cl2#

如果您愿意对数据模型进行更改并且正在使用Postgres,那么您可以快速浏览一下pg_ltree
有了这个gem,你可以使用locations表上的ltree列来组织你的位置,并发出如下查询:

Location.find_by(path: "Earth.Europe")
Location.find_by(path: "Earth.Europe.France").root
Location.find_by(path: "Earth.Europe.France.Paris").children

如果您需要更高级的查询,那么您来对地方了:ltree还支持正则表达式匹配,这意味着你可以编写一些非常高级的查询对象。
这也将是相当快的,因为它将所有的工作卸载到数据库。这并不是说您不能在查询前抛出一个缓存,只是为了让您的PM惊叹,并让DBA脸上露出笑容。
处理长字符串作为叶子名称有一些明显的缺点,但如果树很高,您总是可以使用某种形式的主键或辅键。

相关问题