activerecord-hierarchical_query 0.0.2 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +59 -30
- data/lib/active_record/hierarchical_query.rb +11 -11
- data/lib/active_record/hierarchical_query/cte/columns.rb +2 -15
- data/lib/active_record/hierarchical_query/cte/cycle_detector.rb +54 -0
- data/lib/active_record/hierarchical_query/cte/non_recursive_term.rb +32 -11
- data/lib/active_record/hierarchical_query/cte/query_builder.rb +82 -0
- data/lib/active_record/hierarchical_query/cte/recursive_term.rb +27 -13
- data/lib/active_record/hierarchical_query/cte/union_term.rb +10 -6
- data/lib/active_record/hierarchical_query/join_builder.rb +31 -10
- data/lib/active_record/hierarchical_query/orderings.rb +113 -0
- data/lib/active_record/hierarchical_query/{builder.rb → query.rb} +66 -36
- data/lib/active_record/hierarchical_query/version.rb +1 -1
- data/lib/arel/nodes/postgresql.rb +26 -6
- data/spec/active_record/hierarchical_query_spec.rb +56 -32
- data/spec/database.yml +1 -9
- data/spec/schema.rb +2 -2
- data/spec/spec_helper.rb +2 -4
- data/spec/support/models.rb +4 -4
- metadata +28 -33
- data/lib/active_record/hierarchical_query/adapters.rb +0 -34
- data/lib/active_record/hierarchical_query/adapters/abstract.rb +0 -41
- data/lib/active_record/hierarchical_query/adapters/postgresql.rb +0 -19
- data/lib/active_record/hierarchical_query/cte/query.rb +0 -65
- data/lib/active_record/hierarchical_query/visitors/orderings.rb +0 -87
- data/lib/active_record/hierarchical_query/visitors/postgresql/cycle_detector.rb +0 -49
- data/lib/active_record/hierarchical_query/visitors/postgresql/orderings.rb +0 -70
- data/lib/active_record/hierarchical_query/visitors/visitor.rb +0 -17
@@ -3,29 +3,43 @@ module ActiveRecord
|
|
3
3
|
module CTE
|
4
4
|
class RecursiveTerm
|
5
5
|
# @return [ActiveRecord::HierarchicalQuery::CTE::Query]
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :builder
|
7
7
|
|
8
|
-
delegate :
|
9
|
-
:join_conditions,
|
10
|
-
:adapter,
|
11
|
-
:to => :query
|
8
|
+
delegate :query, to: :builder
|
12
9
|
|
13
|
-
# @param [ActiveRecord::HierarchicalQuery::CTE::
|
14
|
-
def initialize(
|
15
|
-
@
|
10
|
+
# @param [ActiveRecord::HierarchicalQuery::CTE::QueryBuilder] builder
|
11
|
+
def initialize(builder)
|
12
|
+
@builder = builder
|
13
|
+
end
|
14
|
+
|
15
|
+
def bind_values
|
16
|
+
scope.bind_values
|
16
17
|
end
|
17
18
|
|
18
19
|
def arel
|
19
|
-
arel = scope.
|
20
|
-
.
|
21
|
-
.join(recursive_table).on(join_conditions)
|
20
|
+
arel = scope.arel
|
21
|
+
.join(query.recursive_table).on(query.join_conditions)
|
22
22
|
|
23
|
-
|
23
|
+
builder.cycle_detector.apply_to_recursive(arel)
|
24
24
|
end
|
25
25
|
|
26
26
|
private
|
27
27
|
def scope
|
28
|
-
query.
|
28
|
+
@scope ||= query.child_scope_value.select(columns)
|
29
|
+
end
|
30
|
+
|
31
|
+
def columns
|
32
|
+
columns = builder.columns.to_a
|
33
|
+
columns << ordering if query.orderings.any?
|
34
|
+
columns
|
35
|
+
end
|
36
|
+
|
37
|
+
def ordering
|
38
|
+
column_name = query.ordering_column_name
|
39
|
+
left = query.recursive_table[column_name]
|
40
|
+
right = query.orderings.row_number_expression
|
41
|
+
|
42
|
+
Arel::Nodes::ArrayConcat.new(left, right)
|
29
43
|
end
|
30
44
|
end
|
31
45
|
end
|
@@ -5,22 +5,26 @@ module ActiveRecord
|
|
5
5
|
module HierarchicalQuery
|
6
6
|
module CTE
|
7
7
|
class UnionTerm
|
8
|
-
# @param [ActiveRecord::HierarchicalQuery::CTE::
|
9
|
-
def initialize(
|
10
|
-
@
|
8
|
+
# @param [ActiveRecord::HierarchicalQuery::CTE::QueryBuilder] builder
|
9
|
+
def initialize(builder)
|
10
|
+
@builder = builder
|
11
|
+
end
|
12
|
+
|
13
|
+
def bind_values
|
14
|
+
non_recursive_term.bind_values + recursive_term.bind_values
|
11
15
|
end
|
12
16
|
|
13
17
|
def arel
|
14
|
-
non_recursive_term.union(:all, recursive_term)
|
18
|
+
non_recursive_term.arel.union(:all, recursive_term.arel)
|
15
19
|
end
|
16
20
|
|
17
21
|
private
|
18
22
|
def recursive_term
|
19
|
-
RecursiveTerm.new(@
|
23
|
+
@rt ||= RecursiveTerm.new(@builder)
|
20
24
|
end
|
21
25
|
|
22
26
|
def non_recursive_term
|
23
|
-
NonRecursiveTerm.new(@
|
27
|
+
@nrt ||= NonRecursiveTerm.new(@builder)
|
24
28
|
end
|
25
29
|
end
|
26
30
|
end
|
@@ -1,21 +1,26 @@
|
|
1
|
+
require 'active_record/hierarchical_query/cte/query_builder'
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module HierarchicalQuery
|
3
5
|
class JoinBuilder
|
4
|
-
|
5
|
-
|
6
|
-
# @param [ActiveRecord::HierarchicalQuery::CTE::Query] query
|
6
|
+
# @param [ActiveRecord::HierarchicalQuery::Query] query
|
7
7
|
# @param [ActiveRecord::Relation] join_to
|
8
8
|
# @param [#to_s] subquery_alias
|
9
9
|
def initialize(query, join_to, subquery_alias)
|
10
10
|
@query = query
|
11
|
+
@builder = CTE::QueryBuilder.new(query)
|
11
12
|
@relation = join_to
|
12
13
|
@alias = Arel::Table.new(subquery_alias, ActiveRecord::Base)
|
13
14
|
end
|
14
15
|
|
15
16
|
def build
|
16
17
|
relation = @relation.joins(inner_join.to_sql)
|
18
|
+
# copy bound variables from inner subquery
|
19
|
+
relation.bind_values += bind_values
|
20
|
+
# add ordering by "__order_column"
|
21
|
+
relation.order_values += order_columns if ordered?
|
17
22
|
|
18
|
-
|
23
|
+
relation
|
19
24
|
end
|
20
25
|
|
21
26
|
private
|
@@ -23,6 +28,22 @@ module ActiveRecord
|
|
23
28
|
Arel::Nodes::InnerJoin.new(aliased_subquery, constraint)
|
24
29
|
end
|
25
30
|
|
31
|
+
def aliased_subquery
|
32
|
+
Arel::Nodes::As.new(subquery, @alias)
|
33
|
+
end
|
34
|
+
|
35
|
+
def subquery
|
36
|
+
Arel::Nodes::Grouping.new(cte_arel.ast)
|
37
|
+
end
|
38
|
+
|
39
|
+
def cte_arel
|
40
|
+
@cte_arel ||= @builder.build_arel
|
41
|
+
end
|
42
|
+
|
43
|
+
def constraint
|
44
|
+
Arel::Nodes::On.new(primary_key.eq(foreign_key))
|
45
|
+
end
|
46
|
+
|
26
47
|
def primary_key
|
27
48
|
@relation.table[@relation.klass.primary_key]
|
28
49
|
end
|
@@ -31,16 +52,16 @@ module ActiveRecord
|
|
31
52
|
@alias[@query.klass.primary_key]
|
32
53
|
end
|
33
54
|
|
34
|
-
def
|
35
|
-
|
55
|
+
def bind_values
|
56
|
+
@builder.bind_values
|
36
57
|
end
|
37
58
|
|
38
|
-
def
|
39
|
-
|
59
|
+
def ordered?
|
60
|
+
@query.orderings.any?
|
40
61
|
end
|
41
62
|
|
42
|
-
def
|
43
|
-
|
63
|
+
def order_columns
|
64
|
+
[@query.recursive_table[@query.ordering_column_name].asc]
|
44
65
|
end
|
45
66
|
end
|
46
67
|
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module HierarchicalQuery
|
3
|
+
class Orderings
|
4
|
+
NATURAL_SORT_TYPES = Set[
|
5
|
+
:integer, :float, :decimal,
|
6
|
+
:datetime, :timestamp, :time, :date,
|
7
|
+
:boolean, :itet, :cidr, :ltree
|
8
|
+
]
|
9
|
+
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
attr_reader :order_values, :table
|
13
|
+
|
14
|
+
# @param [Array] order_values
|
15
|
+
# @param [Arel::Table] table
|
16
|
+
def initialize(order_values, table)
|
17
|
+
@order_values, @table = order_values, table
|
18
|
+
@values = nil # cached orderings
|
19
|
+
end
|
20
|
+
|
21
|
+
def each(&block)
|
22
|
+
return @values.each(&block) if @values
|
23
|
+
return enum_for(__method__) unless block_given?
|
24
|
+
|
25
|
+
@values = []
|
26
|
+
|
27
|
+
order_values.each do |value|
|
28
|
+
Array.wrap(as_orderings(value)).each do |ordering|
|
29
|
+
@values << ordering
|
30
|
+
|
31
|
+
yield ordering
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
@values
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns order expression to be inserted into SELECT clauses of both
|
39
|
+
# non-recursive and recursive terms.
|
40
|
+
#
|
41
|
+
# @return [Arel::Nodes::Node] order expression
|
42
|
+
def row_number_expression
|
43
|
+
if raw_ordering?
|
44
|
+
order_attribute
|
45
|
+
else
|
46
|
+
Arel.sql("ROW_NUMBER() OVER (ORDER BY #{map(&:to_sql).join(', ')})")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def as_orderings(value)
|
52
|
+
case value
|
53
|
+
when Arel::Nodes::Ordering
|
54
|
+
value
|
55
|
+
|
56
|
+
when Arel::Nodes::Node, Arel::Attributes::Attribute
|
57
|
+
value.asc
|
58
|
+
|
59
|
+
when Symbol
|
60
|
+
table[value].asc
|
61
|
+
|
62
|
+
when Hash
|
63
|
+
value.map { |field, dir| table[field].send(dir) }
|
64
|
+
|
65
|
+
when String
|
66
|
+
value.split(',').map do |expr|
|
67
|
+
string_as_ordering(expr)
|
68
|
+
end
|
69
|
+
|
70
|
+
else
|
71
|
+
raise 'Unknown expression in ORDER BY SIBLINGS clause'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def string_as_ordering(expr)
|
76
|
+
expr.strip!
|
77
|
+
|
78
|
+
if expr.gsub!(/\bdesc\z/i, '')
|
79
|
+
Arel.sql(expr).desc
|
80
|
+
else
|
81
|
+
expr.gsub!(/\basc\z/i, '')
|
82
|
+
Arel.sql(expr).asc
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def raw_ordering?
|
87
|
+
ordered_by_attribute? &&
|
88
|
+
(column = order_column) &&
|
89
|
+
NATURAL_SORT_TYPES.include?(column.type)
|
90
|
+
end
|
91
|
+
|
92
|
+
def ordered_by_attribute?
|
93
|
+
one? && first.ascending? && order_attribute.is_a?(Arel::Attributes::Attribute)
|
94
|
+
end
|
95
|
+
|
96
|
+
def order_attribute
|
97
|
+
first.expr
|
98
|
+
end
|
99
|
+
|
100
|
+
def order_column
|
101
|
+
table = order_attribute.relation
|
102
|
+
|
103
|
+
if table.engine == ActiveRecord::Base
|
104
|
+
columns = table.engine.connection_pool.columns_hash[table.name]
|
105
|
+
else
|
106
|
+
columns = table.engine.columns_hash
|
107
|
+
end
|
108
|
+
|
109
|
+
columns[order_attribute.name.to_s]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -2,12 +2,16 @@
|
|
2
2
|
|
3
3
|
require 'active_support/core_ext/array/extract_options'
|
4
4
|
|
5
|
-
require 'active_record/hierarchical_query/
|
5
|
+
require 'active_record/hierarchical_query/orderings'
|
6
6
|
require 'active_record/hierarchical_query/join_builder'
|
7
|
+
require 'arel/nodes/postgresql'
|
7
8
|
|
8
9
|
module ActiveRecord
|
9
10
|
module HierarchicalQuery
|
10
|
-
class
|
11
|
+
class Query
|
12
|
+
# @api private
|
13
|
+
ORDERING_COLUMN_NAME = '__order_column'.freeze
|
14
|
+
|
11
15
|
# @api private
|
12
16
|
attr_reader :klass,
|
13
17
|
:start_with_value,
|
@@ -19,15 +23,15 @@ module ActiveRecord
|
|
19
23
|
:nocycle_value
|
20
24
|
|
21
25
|
# @api private
|
22
|
-
CHILD_SCOPE_METHODS = :where, :joins, :group, :having
|
26
|
+
CHILD_SCOPE_METHODS = :where, :joins, :group, :having, :bind
|
23
27
|
|
24
28
|
def initialize(klass)
|
25
29
|
@klass = klass
|
26
|
-
@query = CTE::Query.new(self)
|
27
30
|
|
28
|
-
|
31
|
+
# start with :all
|
32
|
+
@start_with_value = klass.__send__(HierarchicalQuery::DELEGATOR_SCOPE)
|
29
33
|
@connect_by_value = nil
|
30
|
-
@child_scope_value = klass
|
34
|
+
@child_scope_value = klass.__send__(HierarchicalQuery::DELEGATOR_SCOPE)
|
31
35
|
@limit_value = nil
|
32
36
|
@offset_value = nil
|
33
37
|
@nocycle_value = false
|
@@ -38,43 +42,49 @@ module ActiveRecord
|
|
38
42
|
#
|
39
43
|
# @example When scope given
|
40
44
|
# MyModel.join_recursive do |hierarchy|
|
41
|
-
# hierarchy.start_with(MyModel.where(:
|
42
|
-
# .connect_by(:
|
45
|
+
# hierarchy.start_with(MyModel.where(parent_id: nil))
|
46
|
+
# .connect_by(id: :parent_id)
|
43
47
|
# end
|
44
48
|
#
|
45
49
|
# @example When Hash given
|
46
50
|
# MyModel.join_recursive do |hierarchy|
|
47
|
-
# hierarchy.start_with(:
|
48
|
-
# .connect_by(:
|
51
|
+
# hierarchy.start_with(parent_id: nil)
|
52
|
+
# .connect_by(id: :parent_id)
|
49
53
|
# end
|
50
54
|
#
|
55
|
+
# @example When String given
|
56
|
+
# MyModel.join_recursive do |hierarchy|
|
57
|
+
# hierararchy.start_with('parent_id = ?', 1)
|
58
|
+
# .connect_by(id: :parent_id)
|
59
|
+
# end
|
60
|
+
#
|
51
61
|
# @example When block given
|
52
62
|
# MyModel.join_recursive do |hierarchy|
|
53
|
-
# hierarchy.start_with { |root| root.where(:
|
54
|
-
# .connect_by(:
|
63
|
+
# hierarchy.start_with { |root| root.where(parent_id: nil) }
|
64
|
+
# .connect_by(id: :parent_id)
|
55
65
|
# end
|
56
66
|
#
|
57
67
|
# @example When block with arity=0 given
|
58
68
|
# MyModel.join_recursive do |hierarchy|
|
59
|
-
# hierarchy.start_with { where(:
|
60
|
-
# .connect_by(:
|
69
|
+
# hierarchy.start_with { where(parent_id: nil) }
|
70
|
+
# .connect_by(id: :parent_id)
|
61
71
|
# end
|
62
72
|
#
|
63
73
|
# @example Specify columns for root relation (PostgreSQL-specific)
|
64
74
|
# MyModel.join_recursive do |hierarchy|
|
65
75
|
# hierarchy.start_with { select('ARRAY[id] AS _path') }
|
66
|
-
# .connect_by(:
|
67
|
-
# .select('_path || id', :
|
76
|
+
# .connect_by(id: :parent_id)
|
77
|
+
# .select('_path || id', start_with: false) # `start_with: false` tells not to include this expression into START WITH clause
|
68
78
|
# end
|
69
79
|
#
|
70
|
-
# @param [ActiveRecord::Relation, Hash, nil] scope root scope (optional).
|
71
|
-
# @return [ActiveRecord::HierarchicalQuery::
|
72
|
-
def start_with(scope = nil, &block)
|
80
|
+
# @param [ActiveRecord::Relation, Hash, String, nil] scope root scope (optional).
|
81
|
+
# @return [ActiveRecord::HierarchicalQuery::Query] self
|
82
|
+
def start_with(scope = nil, *arguments, &block)
|
73
83
|
raise ArgumentError, 'START WITH: scope or block expected, none given' unless scope || block
|
74
84
|
|
75
85
|
case scope
|
76
|
-
when Hash
|
77
|
-
@start_with_value = klass.where(scope)
|
86
|
+
when Hash, String
|
87
|
+
@start_with_value = klass.where(scope, *arguments)
|
78
88
|
|
79
89
|
when ActiveRecord::Relation
|
80
90
|
@start_with_value = scope
|
@@ -103,7 +113,7 @@ module ActiveRecord
|
|
103
113
|
# @example Specify relationship with Hash (traverse descendants)
|
104
114
|
# MyModel.join_recursive do |hierarchy|
|
105
115
|
# # join child rows with condition `parent.id = child.parent_id`
|
106
|
-
# hierarchy.connect_by(:
|
116
|
+
# hierarchy.connect_by(id: :parent_id)
|
107
117
|
# end
|
108
118
|
#
|
109
119
|
# @example Specify relationship with block (traverse descendants)
|
@@ -117,7 +127,7 @@ module ActiveRecord
|
|
117
127
|
# @yieldparam [Arel::Table] parent parent rows table instance.
|
118
128
|
# @yieldparam [Arel::Table] child child rows table instance.
|
119
129
|
# @yieldreturn [Arel::Nodes::Node] relationship condition expressed as Arel node.
|
120
|
-
# @return [ActiveRecord::HierarchicalQuery::
|
130
|
+
# @return [ActiveRecord::HierarchicalQuery::Query] self
|
121
131
|
def connect_by(conditions = nil, &block)
|
122
132
|
# convert hash to block which returns Arel node
|
123
133
|
if conditions
|
@@ -136,7 +146,7 @@ module ActiveRecord
|
|
136
146
|
#
|
137
147
|
# @param [Array<Symbol, String, Arel::Attributes::Attribute, Arel::Nodes::Node>] columns
|
138
148
|
# @option columns [true, false] :start_with include given columns to START WITH clause (true by default)
|
139
|
-
# @return [ActiveRecord::HierarchicalQuery::
|
149
|
+
# @return [ActiveRecord::HierarchicalQuery::Query] self
|
140
150
|
def select(*columns)
|
141
151
|
options = columns.extract_options!
|
142
152
|
|
@@ -166,6 +176,7 @@ module ActiveRecord
|
|
166
176
|
# @!method joins(*tables)
|
167
177
|
# @!method group(*values)
|
168
178
|
# @!method having(*conditions)
|
179
|
+
# @!method bind(value)
|
169
180
|
CHILD_SCOPE_METHODS.each do |method|
|
170
181
|
define_method(method) do |*args|
|
171
182
|
@child_scope_value = @child_scope_value.public_send(method, *args)
|
@@ -177,7 +188,7 @@ module ActiveRecord
|
|
177
188
|
# Specifies a limit for the number of records to retrieve.
|
178
189
|
#
|
179
190
|
# @param [Fixnum] value
|
180
|
-
# @return [ActiveRecord::HierarchicalQuery::
|
191
|
+
# @return [ActiveRecord::HierarchicalQuery::Query] self
|
181
192
|
def limit(value)
|
182
193
|
@limit_value = value
|
183
194
|
|
@@ -187,7 +198,7 @@ module ActiveRecord
|
|
187
198
|
# Specifies the number of rows to skip before returning row
|
188
199
|
#
|
189
200
|
# @param [Fixnum] value
|
190
|
-
# @return [ActiveRecord::HierarchicalQuery::
|
201
|
+
# @return [ActiveRecord::HierarchicalQuery::Query] self
|
191
202
|
def offset(value)
|
192
203
|
@offset_value = value
|
193
204
|
|
@@ -198,18 +209,18 @@ module ActiveRecord
|
|
198
209
|
#
|
199
210
|
# @example
|
200
211
|
# MyModel.join_recursive do |hierarchy|
|
201
|
-
# hierarchy.connect_by(:
|
212
|
+
# hierarchy.connect_by(id: :parent_id)
|
202
213
|
# .order_siblings(:name)
|
203
214
|
# end
|
204
215
|
#
|
205
216
|
# @example
|
206
217
|
# MyModel.join_recursive do |hierarchy|
|
207
|
-
# hierarchy.connect_by(:
|
218
|
+
# hierarchy.connect_by(id: :parent_id)
|
208
219
|
# .order_siblings('name DESC, created_at ASC')
|
209
220
|
# end
|
210
221
|
#
|
211
222
|
# @param [<Symbol, String, Arel::Nodes::Node, Arel::Attributes::Attribute>] columns
|
212
|
-
# @return [ActiveRecord::HierarchicalQuery::
|
223
|
+
# @return [ActiveRecord::HierarchicalQuery::Query] self
|
213
224
|
def order_siblings(*columns)
|
214
225
|
@order_values += columns
|
215
226
|
|
@@ -221,7 +232,7 @@ module ActiveRecord
|
|
221
232
|
# endless loops if your tree could contain cycles.
|
222
233
|
#
|
223
234
|
# @param [true, false] value
|
224
|
-
# @return [ActiveRecord::HierarchicalQuery::
|
235
|
+
# @return [ActiveRecord::HierarchicalQuery::Query] self
|
225
236
|
def nocycle(value = true)
|
226
237
|
@nocycle_value = value
|
227
238
|
self
|
@@ -232,25 +243,26 @@ module ActiveRecord
|
|
232
243
|
#
|
233
244
|
# @example
|
234
245
|
# MyModel.join_recursive do |hierarchy|
|
235
|
-
# hierarchy.connect_by(:
|
236
|
-
# .start_with(:
|
246
|
+
# hierarchy.connect_by(id: :parent_id)
|
247
|
+
# .start_with(parent_id: nil) { select(:depth) }
|
237
248
|
# .select(hierarchy.table[:depth])
|
238
249
|
# .where(hierarchy.prior[:depth].lteq 1)
|
239
250
|
# end
|
240
251
|
#
|
241
252
|
# @return [Arel::Table]
|
242
253
|
def prior
|
243
|
-
@
|
254
|
+
@recursive_table ||= Arel::Table.new("#{table.name}__recursive")
|
244
255
|
end
|
245
256
|
alias_method :previous, :prior
|
257
|
+
alias_method :recursive_table, :prior
|
246
258
|
|
247
259
|
# Returns object representing child rows table,
|
248
260
|
# so it could be used in complex WHEREs.
|
249
261
|
#
|
250
262
|
# @example
|
251
263
|
# MyModel.join_recursive do |hierarchy|
|
252
|
-
# hierarchy.connect_by(:
|
253
|
-
# .start_with(:
|
264
|
+
# hierarchy.connect_by(id: :parent_id)
|
265
|
+
# .start_with(parent_id: nil) { select(:depth) }
|
254
266
|
# .select(hierarchy.table[:depth])
|
255
267
|
# .where(hierarchy.prior[:depth].lteq 1)
|
256
268
|
# end
|
@@ -258,19 +270,37 @@ module ActiveRecord
|
|
258
270
|
@klass.arel_table
|
259
271
|
end
|
260
272
|
|
273
|
+
# @return [Arel::Nodes::Node]
|
274
|
+
# @api private
|
275
|
+
def join_conditions
|
276
|
+
connect_by_value.call(recursive_table, table)
|
277
|
+
end
|
278
|
+
|
279
|
+
# @return [ActiveRecord::HierarchicalQuery::Orderings]
|
280
|
+
# @api private
|
281
|
+
def orderings
|
282
|
+
@orderings ||= Orderings.new(order_values, table)
|
283
|
+
end
|
284
|
+
|
285
|
+
# @api private
|
286
|
+
def ordering_column_name
|
287
|
+
ORDERING_COLUMN_NAME
|
288
|
+
end
|
289
|
+
|
261
290
|
# Builds recursive query and joins it to given +relation+.
|
262
291
|
#
|
263
292
|
# @api private
|
264
293
|
# @param [ActiveRecord::Relation] relation
|
265
294
|
# @param [Hash] join_options
|
266
295
|
# @option join_options [#to_s] :as joined table alias
|
296
|
+
# @api private
|
267
297
|
def join_to(relation, join_options = {})
|
268
298
|
raise 'Recursive query requires CONNECT BY clause, please use #connect_by method' unless
|
269
299
|
connect_by_value
|
270
300
|
|
271
301
|
table_alias = join_options.fetch(:as, "#{table.name}__recursive")
|
272
302
|
|
273
|
-
JoinBuilder.new(
|
303
|
+
JoinBuilder.new(self, relation, table_alias).build
|
274
304
|
end
|
275
305
|
|
276
306
|
private
|