Skip to content

Commit 9e1f7bf

Browse files
Fix eager loading of hierarchies by defining the primary key
When preloading an association via join (e.g. by using `eager_load`, or by referencing the associated table in a `where` condition), rails uses the primary key of the associated table in order to deduplicate the rows and build up the correct association. But the default generated hierarchies table doesn't have an `id` column or anything else that rails can automatically detect as a primary key. This leads rails to incorrectly treat all the records as equal, and return only one at random. For example, calling `MyModel.eager_load(:ancestor_hierarchies).map(&:ancestor_hierarchies)` currently returns incorrect data - only one hierarchy per model, no matter how deep the hierarchy actually is. This PR tells rails to use the combination of the three columns (which do have a unique index on them already in the generator!) as the primary key, and allows the example code to correctly return all the hierarchies for each model. It also would presumably fix #294 though I haven't tested that.
1 parent b6f19d2 commit 9e1f7bf

File tree

1 file changed

+4
-0
lines changed

1 file changed

+4
-0
lines changed

lib/closure_tree/support.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ def hierarchy_class_for_model
3636
# Rails 8.1+ requires an implicit_order_column for models without a primary key
3737
self.implicit_order_column = 'ancestor_id'
3838

39+
# Rails uses the primary key to correctly match associations when using a join to preload (e.g. via `eager_load`).
40+
# The migration generator adds a unique index across these three columns so this is safe.
41+
self.primary_key = [:ancestor_id, :descendant_id, :generations]
42+
3943
belongs_to :ancestor, class_name: model_class_name
4044
belongs_to :descendant, class_name: model_class_name
4145
def ==(other)

0 commit comments

Comments
 (0)