activerecord-hierarchical_query 0.0.8 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -1
- data/lib/active_record/hierarchical_query/cte/query_builder.rb +6 -2
- data/lib/active_record/hierarchical_query/join_builder.rb +5 -4
- data/lib/active_record/hierarchical_query/query.rb +11 -1
- data/lib/active_record/hierarchical_query/version.rb +1 -1
- data/spec/active_record/hierarchical_query_spec.rb +57 -8
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 35f6d79b615d8ca821c2258b9287f10044ed997c
|
4
|
+
data.tar.gz: 4dc56871743b3a2d9bd2dc920d1195894dfc8aa5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7cd10f34571215ba462f81ebd552667a432f38bfd7f84eb4cc25c05dcc006623c0b7f4e0f4f24211f1bd2874ce4458bee0bec1a43db61109cd7227105606c4fc
|
7
|
+
data.tar.gz: 2936034c375afb978a9c5b5a8f7c69cc4cd36690315affa3c1e3fde2433b84fbf20eb3a07cac37461f672ea5f753702c4045cf1566c1da6df3cdcce393fe8252
|
data/README.md
CHANGED
@@ -244,6 +244,17 @@ Category.join_recursive do |query|
|
|
244
244
|
end
|
245
245
|
```
|
246
246
|
|
247
|
+
## DISTINCT
|
248
|
+
By default, the union term in the Common Table Expression uses a `UNION ALL`. If you want
|
249
|
+
to `SELECT DISTINCT` CTE values, add a query option for `distinct`
|
250
|
+
```ruby
|
251
|
+
Category.join_recursive do |query|
|
252
|
+
query.connect_by(id: :parent_id)
|
253
|
+
.start_with(id: node_1.id)
|
254
|
+
.distinct
|
255
|
+
end
|
256
|
+
```
|
257
|
+
|
247
258
|
## Generated SQL queries
|
248
259
|
|
249
260
|
Under the hood this extensions builds `INNER JOIN` to recursive subquery.
|
@@ -297,7 +308,7 @@ ORDER BY "categories__recursive"."__order_column" ASC
|
|
297
308
|
|
298
309
|
If you want to use a `LEFT OUTER JOIN` instead of an `INNER JOIN`, add a query option for `outer_join_hierarchical`. This option allows the query to return non-hierarchical entries:
|
299
310
|
```ruby
|
300
|
-
.join_recursive(outer_join_hierarchical: true)
|
311
|
+
.join_recursive(outer_join_hierarchical: true)
|
301
312
|
```
|
302
313
|
|
303
314
|
If, when joining the recursive view to the main table, you want to change the foreign_key on the recursive view from the primary key of the main table to another column:
|
@@ -58,7 +58,11 @@ module ActiveRecord
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def build_select
|
61
|
-
@
|
61
|
+
if @query.distinct_value == true
|
62
|
+
@arel.project(recursive_table[Arel.star]).distinct
|
63
|
+
else
|
64
|
+
@arel.project(recursive_table[Arel.star])
|
65
|
+
end
|
62
66
|
end
|
63
67
|
|
64
68
|
def build_limits
|
@@ -79,4 +83,4 @@ module ActiveRecord
|
|
79
83
|
end
|
80
84
|
end
|
81
85
|
end
|
82
|
-
end
|
86
|
+
end
|
@@ -6,19 +6,20 @@ module ActiveRecord
|
|
6
6
|
# @param [ActiveRecord::HierarchicalQuery::Query] query
|
7
7
|
# @param [ActiveRecord::Relation] join_to
|
8
8
|
# @param [#to_s] subquery_alias
|
9
|
-
def initialize(query, join_to, subquery_alias,
|
9
|
+
def initialize(query, join_to, subquery_alias, options = {})
|
10
10
|
@query = query
|
11
11
|
@builder = CTE::QueryBuilder.new(query)
|
12
12
|
@relation = join_to
|
13
13
|
@alias = Arel::Table.new(subquery_alias, ActiveRecord::Base)
|
14
|
-
@
|
14
|
+
@options = options
|
15
15
|
end
|
16
16
|
|
17
17
|
def build
|
18
18
|
# outer joins to include non-hierarchical entries if specified
|
19
19
|
# default option when flag is not specified is to include only entries participating
|
20
20
|
# in a hierarchy
|
21
|
-
join_sql = @
|
21
|
+
join_sql = @options[:outer_join_hierarchical] == true ? outer_join.to_sql : inner_join.to_sql
|
22
|
+
|
22
23
|
relation = @relation.joins(join_sql)
|
23
24
|
|
24
25
|
# copy bound variables from inner subquery (remove duplicates)
|
@@ -59,7 +60,7 @@ module ActiveRecord
|
|
59
60
|
end
|
60
61
|
|
61
62
|
def custom_foreign_key
|
62
|
-
@
|
63
|
+
@options[:foreign_key]
|
63
64
|
end
|
64
65
|
|
65
66
|
def foreign_key
|
@@ -20,7 +20,8 @@ module ActiveRecord
|
|
20
20
|
:limit_value,
|
21
21
|
:offset_value,
|
22
22
|
:order_values,
|
23
|
-
:nocycle_value
|
23
|
+
:nocycle_value,
|
24
|
+
:distinct_value
|
24
25
|
|
25
26
|
# @api private
|
26
27
|
CHILD_SCOPE_METHODS = :where, :joins, :group, :having, :bind
|
@@ -36,6 +37,7 @@ module ActiveRecord
|
|
36
37
|
@offset_value = nil
|
37
38
|
@nocycle_value = false
|
38
39
|
@order_values = []
|
40
|
+
@distinct_value = false
|
39
41
|
end
|
40
42
|
|
41
43
|
# Specify root scope of the hierarchy.
|
@@ -270,6 +272,14 @@ module ActiveRecord
|
|
270
272
|
@klass.arel_table
|
271
273
|
end
|
272
274
|
|
275
|
+
# Turn on select distinct option in the CTE.
|
276
|
+
#
|
277
|
+
# @return [ActiveRecord::HierarchicalQuery::Query] self
|
278
|
+
def distinct
|
279
|
+
@distinct_value = true
|
280
|
+
self
|
281
|
+
end
|
282
|
+
|
273
283
|
# @return [Arel::Nodes::Node]
|
274
284
|
# @api private
|
275
285
|
def join_conditions
|
@@ -214,14 +214,6 @@ describe ActiveRecord::HierarchicalQuery do
|
|
214
214
|
end
|
215
215
|
end
|
216
216
|
|
217
|
-
describe ':outer_join_hierarchical' do
|
218
|
-
it 'build an outer join' do
|
219
|
-
expect(
|
220
|
-
klass.join_recursive(outer_join_hierarchical: true) { connect_by(id: :parent_id) }.to_sql
|
221
|
-
).to match /LEFT OUTER JOIN/
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
217
|
describe ':foreign_key' do
|
226
218
|
it 'uses described foreign_key when joining table to recursive view' do
|
227
219
|
expect(
|
@@ -229,5 +221,62 @@ describe ActiveRecord::HierarchicalQuery do
|
|
229
221
|
).to match /categories\"\.\"id\" = \"categories__recursive\"\.\"some_column/
|
230
222
|
end
|
231
223
|
end
|
224
|
+
|
225
|
+
describe ':outer_join_hierarchical' do
|
226
|
+
subject { klass.join_recursive(outer_join_hierarchical: value) { connect_by(id: :parent_id) }.to_sql }
|
227
|
+
|
228
|
+
let(:value) { true }
|
229
|
+
let(:inner_join) {
|
230
|
+
/INNER JOIN \(WITH RECURSIVE \"categories__recursive\"/
|
231
|
+
}
|
232
|
+
|
233
|
+
it 'builds an outer join' do
|
234
|
+
expect(subject).to match /LEFT OUTER JOIN \(WITH RECURSIVE \"categories__recursive\"/
|
235
|
+
end
|
236
|
+
|
237
|
+
context 'value is false' do
|
238
|
+
let(:value) { false }
|
239
|
+
|
240
|
+
it 'builds an inner join' do
|
241
|
+
expect(subject).to match inner_join
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
context 'value is a string' do
|
246
|
+
let(:value) { 'foo' }
|
247
|
+
|
248
|
+
it 'builds an inner join' do
|
249
|
+
expect(subject).to match inner_join
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
context 'key is absent' do
|
254
|
+
subject { klass.join_recursive { connect_by(id: :parent_id) }.to_sql }
|
255
|
+
|
256
|
+
it 'builds an inner join' do
|
257
|
+
expect(subject).to match inner_join
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
describe ':distinct query method' do
|
264
|
+
subject { klass.join_recursive { connect_by(id: :parent_id).distinct }.to_sql }
|
265
|
+
|
266
|
+
let(:select) {
|
267
|
+
/SELECT \"categories__recursive\"/
|
268
|
+
}
|
269
|
+
|
270
|
+
it 'selects using a distinct option after joining table to recursive view' do
|
271
|
+
expect(subject).to match /SELECT DISTINCT \"categories__recursive\"/
|
272
|
+
end
|
273
|
+
|
274
|
+
context 'distinct method is absent' do
|
275
|
+
subject { klass.join_recursive { connect_by(id: :parent_id) }.to_sql }
|
276
|
+
|
277
|
+
it 'selects without using a distinct' do
|
278
|
+
expect(subject).to match select
|
279
|
+
end
|
280
|
+
end
|
232
281
|
end
|
233
282
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-hierarchical_query
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexei Mikhailov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-07-
|
11
|
+
date: 2016-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -141,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
141
|
version: '0'
|
142
142
|
requirements: []
|
143
143
|
rubyforge_project:
|
144
|
-
rubygems_version: 2.4.
|
144
|
+
rubygems_version: 2.4.3
|
145
145
|
signing_key:
|
146
146
|
specification_version: 4
|
147
147
|
summary: Recursively traverse trees using a single SQL query
|
@@ -152,3 +152,4 @@ test_files:
|
|
152
152
|
- spec/schema.rb
|
153
153
|
- spec/spec_helper.rb
|
154
154
|
- spec/support/models.rb
|
155
|
+
has_rdoc:
|