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

Getting all nested objects belongs to a model

提问人:cnn 提问时间:6/16/2023 最后编辑:cnn 更新时间:6/19/2023 访问量:48

问:

我有一个模型,该模型可以具有多个子位置或一个父位置。 假设位置 A 是父位置,位置 B 和位置 C 是子位置。但位置 B 也有一个子位置 D。Location

如何获取父位置 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

Ruby-on-Rails 红宝石 Ruby-3

评论

1赞 engineersmnky 6/16/2023
迭代绝对不是你最好的选择。虽然您可以实现 CTE 来处理这种情况,但也有一些宝石已经解决了许多细节,并提供了其他有用的功能,其中祖先是最受欢迎的功能之一。

答:

2赞 Amol Mohite 6/16/2023 #1

您可以使用 gem 。ancestry

请看一下这里的官方文档

将 Gem 添加到您的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

0赞 Nick M 6/18/2023 #2

如果您愿意对数据模型进行更改并且正在使用 Postgres,也许您可以快速浏览一下pg_ltree

使用此 Gem,您可以使用位置表上的 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 脸上露出灿烂的笑容。

显然有一些缺点与处理长字符串作为叶子名称有关,但如果你的树很高,你总是可以使用某种形式的主键或辅助键。