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

huangapple go评论60阅读模式
英文:

Getting all nested objects belongs to a model

问题

我有一个“Location”模型,可以有许多子位置,或者一个父位置。
假设位置-A是一个父位置,有位置-B和位置-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

但我需要一种高效的方法。因为它会抛出“栈级别太深”的错误。

编辑:最终我使用了.reload在子位置上,并保持现有的方法不变。这样它就可以工作了。

英文:

I have a Location model that can have many sublocations, or one parent location.
Let's say location-A is a parent location, has location-B and location-C as sublocations. But location-B also has a sublocation location-D.

How can I get all sublocations of the parent location-A, including location-D?

My model:

  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

Currently I fetch them like this:

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

But I need an efficient way. Because it throws stack level too deep

Edit: I ended up using .reload on sublocations and keep the existing method as is. This way it worked.

答案1

得分: 2

你可以使用 gem ancestry

请查看官方文档这里

在你的 gemfile 中添加 Gem:

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

这里 sublocations 将会是一个包含 Location BLocation CLocation D 的数组。

英文:

You can use gem ancestry.

Please have a look at the official doc here

Add Gem in your gemfile

gem &#39;ancestry&#39;

Create migration

rails g migration add_Add_ancestry_to_location ancestry:string:index

In you Location model add it as below:

class Location &lt; ApplicationRecord
  has_ancestry
end

now you can use is as below:

location_a = Location.create(name: &#39;Location A&#39;)
location_b = Location.create(name: &#39;Location B&#39;, parent: location_a)
location_c = Location.create(name: &#39;Location C&#39;, parent: location_a)
location_d = Location.create(name: &#39;Location D&#39;, parent: location_b) 

parent_location = Location.find_by(name: &#39;Location A&#39;)
sublocations = parent_location.descendants

here sublocations will be an array with locations as Location B, Location C, Location D.

答案2

得分: 0

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

通过这个宝石,您可以在您的位置表上使用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笑容满面。

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

英文:

If you are willing to make changes to your data model and are using Postgres perhaps you can take a quick look at pg_ltree

With this gem you can organise your locations using a ltree column on your locations table, and issue queries such as:

Location.find_by(path: &quot;Earth.Europe&quot;)
Location.find_by(path: &quot;Earth.Europe.France&quot;).root
Location.find_by(path: &quot;Earth.Europe.France.Paris&quot;).children

If you need more advanced querying then you've come to the right place: ltree also supports regular expression matching which means you can write some pretty advanced query objects.

Also this will be quite fast because it offloads all the work to the database. Not saying you can't throw a cache in front of your queries anyway just to wow your PM and put a big smile on your DBA's face.

There are some downsides apparently related to handling of long strings as leaf names but you can always use some form of primary or secondary keys if your tree is tall.

huangapple
  • 本文由 发表于 2023年6月16日 16:01:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76488124.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定