@@ -37,15 +37,24 @@ def find_or_create_by_path(path, attributes = {})
3737    end 
3838
3939    def  find_all_by_generation ( generation_level ) 
40-       s  =  _ct . base_class . joins ( <<-SQL . squish ) 
41-         INNER JOIN ( 
42-           SELECT descendant_id 
43-           FROM #{ _ct . quoted_hierarchy_table_name }  
44-           WHERE ancestor_id = #{ _ct . quote ( id ) }  
45-           GROUP BY descendant_id 
46-           HAVING MAX(#{ _ct . quoted_hierarchy_table_name } #{ generation_level . to_i }  
47-         ) #{ _ct . t_alias_keyword } #{ _ct . quoted_table_name } #{ _ct . base_class . primary_key }  
48-       SQL 
40+       hierarchy_table  =  self . class . hierarchy_class . arel_table 
41+       model_table  =  self . class . arel_table 
42+ 
43+       # Build the subquery 
44+       descendants_subquery  =  hierarchy_table 
45+                              . project ( hierarchy_table [ :descendant_id ] ) 
46+                              . where ( hierarchy_table [ :ancestor_id ] . eq ( id ) ) 
47+                              . group ( hierarchy_table [ :descendant_id ] ) 
48+                              . having ( hierarchy_table [ :generations ] . maximum . eq ( generation_level . to_i ) ) 
49+                              . as ( 'descendants' ) 
50+ 
51+       # Build the join 
52+       join_source  =  model_table 
53+                     . join ( descendants_subquery ) 
54+                     . on ( model_table [ _ct . base_class . primary_key ] . eq ( descendants_subquery [ :descendant_id ] ) ) 
55+                     . join_sources 
56+ 
57+       s  =  _ct . base_class . joins ( join_source ) 
4958      _ct . scope_with_order ( s ) 
5059    end 
5160
@@ -72,14 +81,23 @@ def root
7281      end 
7382
7483      def  leaves 
75-         s  =  joins ( <<-SQL . squish ) 
76-           INNER JOIN ( 
77-             SELECT ancestor_id 
78-             FROM #{ _ct . quoted_hierarchy_table_name }  
79-             GROUP BY ancestor_id 
80-             HAVING MAX(#{ _ct . quoted_hierarchy_table_name }  
81-           ) #{ _ct . t_alias_keyword } #{ _ct . quoted_table_name } #{ primary_key }  
82-         SQL 
84+         hierarchy_table  =  hierarchy_class . arel_table 
85+         model_table  =  arel_table 
86+ 
87+         # Build the subquery for leaves (nodes with no children) 
88+         leaves_subquery  =  hierarchy_table 
89+                           . project ( hierarchy_table [ :ancestor_id ] ) 
90+                           . group ( hierarchy_table [ :ancestor_id ] ) 
91+                           . having ( hierarchy_table [ :generations ] . maximum . eq ( 0 ) ) 
92+                           . as ( 'leaves' ) 
93+ 
94+         # Build the join 
95+         join_source  =  model_table 
96+                       . join ( leaves_subquery ) 
97+                       . on ( model_table [ primary_key ] . eq ( leaves_subquery [ :ancestor_id ] ) ) 
98+                       . join_sources 
99+ 
100+         s  =  joins ( join_source ) 
83101        _ct . scope_with_order ( s . readonly ( false ) ) 
84102      end 
85103
@@ -123,22 +141,38 @@ def lowest_common_ancestor(*descendants)
123141      end 
124142
125143      def  find_all_by_generation ( generation_level ) 
126-         s  =  joins ( <<-SQL . squish ) 
127-           INNER JOIN ( 
128-             SELECT #{ primary_key }  
129-             FROM #{ _ct . quoted_table_name }  
130-             WHERE #{ _ct . quoted_parent_column_name }  
131-           ) #{ _ct . t_alias_keyword }  
132-           INNER JOIN ( 
133-             SELECT ancestor_id, descendant_id 
134-             FROM #{ _ct . quoted_hierarchy_table_name }  
135-             GROUP BY ancestor_id, descendant_id 
136-             HAVING MAX(generations) = #{ generation_level . to_i }  
137-           ) #{ _ct . t_alias_keyword }  
138-             #{ _ct . quoted_table_name } #{ primary_key }  
139-             AND roots.root_id = descendants.ancestor_id 
140-           ) 
141-         SQL 
144+         hierarchy_table  =  hierarchy_class . arel_table 
145+         model_table  =  arel_table 
146+ 
147+         # Build the roots subquery 
148+         roots_subquery  =  model_table 
149+                          . project ( model_table [ primary_key ] . as ( 'root_id' ) ) 
150+                          . where ( model_table [ _ct . parent_column_sym ] . eq ( nil ) ) 
151+                          . as ( 'roots' ) 
152+ 
153+         # Build the descendants subquery 
154+         descendants_subquery  =  hierarchy_table 
155+                                . project ( 
156+                                  hierarchy_table [ :ancestor_id ] , 
157+                                  hierarchy_table [ :descendant_id ] 
158+                                ) 
159+                                . group ( hierarchy_table [ :ancestor_id ] ,  hierarchy_table [ :descendant_id ] ) 
160+                                . having ( hierarchy_table [ :generations ] . maximum . eq ( generation_level . to_i ) ) 
161+                                . as ( 'descendants' ) 
162+ 
163+         # Build the joins 
164+         join_roots  =  model_table 
165+                      . join ( roots_subquery ) 
166+                      . on ( Arel . sql ( '1 = 1' ) ) 
167+ 
168+         join_descendants  =  join_roots 
169+                            . join ( descendants_subquery ) 
170+                            . on ( 
171+                              model_table [ primary_key ] . eq ( descendants_subquery [ :descendant_id ] ) 
172+                              . and ( roots_subquery [ :root_id ] . eq ( descendants_subquery [ :ancestor_id ] ) ) 
173+                            ) 
174+ 
175+         s  =  joins ( join_descendants . join_sources ) 
142176        _ct . scope_with_order ( s ) 
143177      end 
144178
@@ -151,6 +185,7 @@ def find_by_path(path, attributes = {}, parent_id = nil)
151185
152186        scope  =  where ( path . pop ) 
153187        last_joined_table  =  _ct . table_name 
188+ 
154189        path . reverse . each_with_index  do  |ea ,  idx |
155190          next_joined_table  =  "p#{ idx }  
156191          scope  =  scope . joins ( <<-SQL . squish ) 
@@ -161,6 +196,7 @@ def find_by_path(path, attributes = {}, parent_id = nil)
161196          scope  =  _ct . scoped_attributes ( scope ,  ea ,  next_joined_table ) 
162197          last_joined_table  =  next_joined_table 
163198        end 
199+ 
164200        scope . where ( "#{ last_joined_table } #{ _ct . parent_column_name }   =>  parent_id ) . readonly ( false ) . first 
165201      end 
166202
0 commit comments