英文:
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 B
、Location C
、Location D
的数组。
英文:
You can use gem ancestry
.
Please have a look at the official doc here
Add Gem in your gemfile
gem 'ancestry'
Create migration
rails g migration add_Add_ancestry_to_location ancestry:string:index
In you Location
model add it as below:
class Location < ApplicationRecord
has_ancestry
end
now you can use is as below:
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
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: "Earth.Europe")
Location.find_by(path: "Earth.Europe.France").root
Location.find_by(path: "Earth.Europe.France.Paris").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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论