activerecord-hierarchical_query 0.0.8 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|