activerecord-hierarchical_query 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -1
- data/lib/active_record/hierarchical_query/cte/query_builder.rb +2 -2
- data/lib/active_record/hierarchical_query/cte/union_term.rb +3 -7
- data/lib/active_record/hierarchical_query/join_builder.rb +17 -8
- data/lib/active_record/hierarchical_query/version.rb +1 -1
- data/spec/active_record/hierarchical_query_spec.rb +22 -0
- data/spec/schema.rb +6 -1
- data/spec/support/models.rb +6 -1
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c51bb399108f367e30e8bde8ea46171451212228
|
4
|
+
data.tar.gz: 385d34aa8caa4777f50e0a2c8a7b9d84c0c30dd1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3bad1f4d790f159e86c1870184e43afc7b3c196394b8330d52b8cd25a582ba098b609cf2193213017838fb426f2cdebf22c53ce8b372bb211cb272c95bd7332
|
7
|
+
data.tar.gz: 02b43b6cfb15595885d4d326b6ddd6cced8c34fc7d8dff3c7c1aa11eca5119852276b7d3238f78f13f6634bc9fd62b8e5eaf72505a30aff1f201d4c50d60b731
|
data/README.md
CHANGED
@@ -246,7 +246,7 @@ end
|
|
246
246
|
|
247
247
|
## DISTINCT
|
248
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
|
249
|
+
to `SELECT DISTINCT` CTE values, add a query option for `distinct`:
|
250
250
|
```ruby
|
251
251
|
Category.join_recursive do |query|
|
252
252
|
query.connect_by(id: :parent_id)
|
@@ -255,6 +255,14 @@ Category.join_recursive do |query|
|
|
255
255
|
end
|
256
256
|
```
|
257
257
|
|
258
|
+
If you want to join CTE terms by `UNION DISTINCT`, pass an option to `join_recursive`:
|
259
|
+
```ruby
|
260
|
+
Category.join_recursive(union_type: :distinct) do |query|
|
261
|
+
query.connect_by(id: :parent_id)
|
262
|
+
.start_with(id: node_1.id)
|
263
|
+
end
|
264
|
+
```
|
265
|
+
|
258
266
|
## Generated SQL queries
|
259
267
|
|
260
268
|
Under the hood this extensions builds `INNER JOIN` to recursive subquery.
|
@@ -56,11 +56,11 @@ module ActiveRecord
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def union_term
|
59
|
-
@union_term ||= UnionTerm.new(self)
|
59
|
+
@union_term ||= UnionTerm.new(self, @options)
|
60
60
|
end
|
61
61
|
|
62
62
|
def build_select
|
63
|
-
if @query.distinct_value
|
63
|
+
if @query.distinct_value
|
64
64
|
@arel.project(recursive_table[Arel.star]).distinct
|
65
65
|
else
|
66
66
|
@arel.project(recursive_table[Arel.star])
|
@@ -6,8 +6,9 @@ module ActiveRecord
|
|
6
6
|
module CTE
|
7
7
|
class UnionTerm
|
8
8
|
# @param [ActiveRecord::HierarchicalQuery::CTE::QueryBuilder] builder
|
9
|
-
def initialize(builder)
|
9
|
+
def initialize(builder, options = {})
|
10
10
|
@builder = builder
|
11
|
+
@union_type = options.fetch(:union_type, :all)
|
11
12
|
end
|
12
13
|
|
13
14
|
def bind_values
|
@@ -19,12 +20,7 @@ module ActiveRecord
|
|
19
20
|
end
|
20
21
|
|
21
22
|
private
|
22
|
-
|
23
|
-
def union_type
|
24
|
-
return @builder.options[:union_type] if @builder.options[:union_type].present?
|
25
|
-
|
26
|
-
:all
|
27
|
-
end
|
23
|
+
attr_reader :union_type
|
28
24
|
|
29
25
|
def recursive_term
|
30
26
|
@rt ||= RecursiveTerm.new(@builder)
|
@@ -6,6 +6,7 @@ module ActiveRecord
|
|
6
6
|
# @param [ActiveRecord::HierarchicalQuery::Query] query
|
7
7
|
# @param [ActiveRecord::Relation] join_to
|
8
8
|
# @param [#to_s] subquery_alias
|
9
|
+
# @param [Hash] options (:outer_join_hierarchical, :union_type)
|
9
10
|
def initialize(query, join_to, subquery_alias, options = {})
|
10
11
|
@query = query
|
11
12
|
@builder = CTE::QueryBuilder.new(query, options: options)
|
@@ -15,13 +16,7 @@ module ActiveRecord
|
|
15
16
|
end
|
16
17
|
|
17
18
|
def build
|
18
|
-
|
19
|
-
# default option when flag is not specified is to include only entries participating
|
20
|
-
# in a hierarchy
|
21
|
-
join_sql = @options[:outer_join_hierarchical] == true ? outer_join.to_sql : inner_join.to_sql
|
22
|
-
|
23
|
-
relation = @relation.joins(join_sql)
|
24
|
-
|
19
|
+
relation = @relation.joins(joined_arel_node)
|
25
20
|
# copy bound variables from inner subquery (remove duplicates)
|
26
21
|
relation.bind_values |= bind_values
|
27
22
|
# add ordering by "__order_column"
|
@@ -31,6 +26,10 @@ module ActiveRecord
|
|
31
26
|
end
|
32
27
|
|
33
28
|
private
|
29
|
+
def joined_arel_node
|
30
|
+
@options[:outer_join_hierarchical] == true ? outer_join : inner_join
|
31
|
+
end
|
32
|
+
|
34
33
|
def inner_join
|
35
34
|
Arel::Nodes::InnerJoin.new(aliased_subquery, constraint)
|
36
35
|
end
|
@@ -40,7 +39,7 @@ module ActiveRecord
|
|
40
39
|
end
|
41
40
|
|
42
41
|
def aliased_subquery
|
43
|
-
|
42
|
+
SubqueryAlias.new(subquery, @alias)
|
44
43
|
end
|
45
44
|
|
46
45
|
def subquery
|
@@ -78,6 +77,16 @@ module ActiveRecord
|
|
78
77
|
def order_columns
|
79
78
|
[@query.recursive_table[@query.ordering_column_name].asc]
|
80
79
|
end
|
80
|
+
|
81
|
+
# This node is required to support joins to aliased Arel nodes
|
82
|
+
class SubqueryAlias < Arel::Nodes::As
|
83
|
+
attr_reader :table_name
|
84
|
+
|
85
|
+
def initialize(subquery, alias_node)
|
86
|
+
super
|
87
|
+
@table_name = alias_node.name
|
88
|
+
end
|
89
|
+
end
|
81
90
|
end
|
82
91
|
end
|
83
92
|
end
|
@@ -306,4 +306,26 @@ describe ActiveRecord::HierarchicalQuery do
|
|
306
306
|
end
|
307
307
|
end
|
308
308
|
end
|
309
|
+
|
310
|
+
describe 'Testing bind variables' do
|
311
|
+
let!(:article) { Article.create!(category: child_2, title: 'Alpha') }
|
312
|
+
|
313
|
+
let(:subquery) do
|
314
|
+
klass.join_recursive do |query|
|
315
|
+
query.
|
316
|
+
start_with(parent_id: child_1.id).
|
317
|
+
connect_by(id: :parent_id)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
let(:outer_query) do
|
322
|
+
Article.where(category_id: subquery, title: 'Alpha')
|
323
|
+
end
|
324
|
+
|
325
|
+
subject(:result) { outer_query.to_a }
|
326
|
+
|
327
|
+
it 'returns result without throwing an error' do
|
328
|
+
expect(result).to include(article)
|
329
|
+
end
|
330
|
+
end
|
309
331
|
end
|
data/spec/schema.rb
CHANGED
data/spec/support/models.rb
CHANGED
@@ -16,6 +16,7 @@ class Category < ActiveRecord::Base
|
|
16
16
|
|
17
17
|
belongs_to :parent, class_name: 'Category'
|
18
18
|
has_many :children, class_name: 'Category'
|
19
|
+
has_many :articles
|
19
20
|
|
20
21
|
before_save :generate_name, unless: :name?
|
21
22
|
before_save :count_depth
|
@@ -36,4 +37,8 @@ class Category < ActiveRecord::Base
|
|
36
37
|
def ancestors
|
37
38
|
parent ? parent.ancestors + [parent] : []
|
38
39
|
end
|
39
|
-
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Article < ActiveRecord::Base
|
43
|
+
belongs_to :category
|
44
|
+
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.1.
|
4
|
+
version: 0.1.2
|
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-08-
|
11
|
+
date: 2016-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -17,6 +17,9 @@ dependencies:
|
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 3.1.0
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 5.0.0
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -24,6 +27,9 @@ dependencies:
|
|
24
27
|
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: 3.1.0
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 5.0.0
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: bundler
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -152,4 +158,3 @@ test_files:
|
|
152
158
|
- spec/schema.rb
|
153
159
|
- spec/spec_helper.rb
|
154
160
|
- spec/support/models.rb
|
155
|
-
has_rdoc:
|