rasti-db 4.1.1 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +1 -1
- data/lib/rasti/db/query.rb +5 -1
- data/lib/rasti/db/relations/graph.rb +23 -10
- data/lib/rasti/db/relations/many_to_many.rb +14 -8
- data/lib/rasti/db/relations/many_to_one.rb +8 -4
- data/lib/rasti/db/relations/one_to_many.rb +7 -4
- data/lib/rasti/db/version.rb +1 -1
- data/spec/minitest_helper.rb +26 -6
- data/spec/query_spec.rb +58 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa10d65e8a9fbd3c311384e8806addd5c04b803405444717d6904282dbf9207c
|
4
|
+
data.tar.gz: 24f1e8801906634288e62ef7337da1179392e75203b08ae6f034b5f25256228e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04a6c1deb6afa47fae66203bec39d81926ac5e6435844593c321279423d6a8a5651178fc5769e58868f88286ae819ecfd7aa77785b2e6df82cd63096d12ee34f
|
7
|
+
data.tar.gz: 28ec23866d539f20163bff9449586060d1f943d022f10f72d2d4b4ff5f3fc8b9db97acbefa2482fcc262d0e51625bad603ea3861fbea7f47fe36382ce9da5821
|
data/.github/workflows/ci.yml
CHANGED
@@ -13,7 +13,7 @@ jobs:
|
|
13
13
|
runs-on: ubuntu-latest
|
14
14
|
strategy:
|
15
15
|
matrix:
|
16
|
-
ruby-version: ['2.
|
16
|
+
ruby-version: ['2.3', '2.4', '2.5', '2.6', '2.7', '3.0', 'jruby-9.2.9.0']
|
17
17
|
|
18
18
|
steps:
|
19
19
|
- uses: actions/checkout@v3
|
data/lib/rasti/db/query.rb
CHANGED
@@ -53,6 +53,10 @@ module Rasti
|
|
53
53
|
build_query relations_graph: relations_graph.merge(excluded_attributes: excluded_attributes)
|
54
54
|
end
|
55
55
|
|
56
|
+
def graph_queries(queries)
|
57
|
+
build_query relations_graph: relations_graph.merge(queries: queries)
|
58
|
+
end
|
59
|
+
|
56
60
|
def all_graph_attributes(*relations)
|
57
61
|
build_query relations_graph: relations_graph.with_all_attributes_for(relations)
|
58
62
|
end
|
@@ -215,4 +219,4 @@ module Rasti
|
|
215
219
|
|
216
220
|
end
|
217
221
|
end
|
218
|
-
end
|
222
|
+
end
|
@@ -3,20 +3,22 @@ module Rasti
|
|
3
3
|
module Relations
|
4
4
|
class Graph
|
5
5
|
|
6
|
-
def initialize(environment, collection_class, relations=[], selected_attributes={}, excluded_attributes={})
|
6
|
+
def initialize(environment, collection_class, relations=[], selected_attributes={}, excluded_attributes={}, queries={})
|
7
7
|
@environment = environment
|
8
8
|
@collection_class = collection_class
|
9
9
|
@graph = build_graph relations,
|
10
10
|
Hash::Indifferent.new(selected_attributes),
|
11
|
-
Hash::Indifferent.new(excluded_attributes)
|
11
|
+
Hash::Indifferent.new(excluded_attributes),
|
12
|
+
Hash::Indifferent.new(queries)
|
12
13
|
end
|
13
14
|
|
14
|
-
def merge(relations:[], selected_attributes:{}, excluded_attributes:{})
|
15
|
+
def merge(relations:[], selected_attributes:{}, excluded_attributes:{}, queries: {})
|
15
16
|
Graph.new environment,
|
16
17
|
collection_class,
|
17
18
|
(flat_relations | relations),
|
18
19
|
flat_selected_attributes.merge(selected_attributes),
|
19
|
-
flat_excluded_attributes.merge(excluded_attributes)
|
20
|
+
flat_excluded_attributes.merge(excluded_attributes),
|
21
|
+
flat_queries.merge(queries)
|
20
22
|
end
|
21
23
|
|
22
24
|
def with_all_attributes_for(relations)
|
@@ -30,16 +32,17 @@ module Rasti
|
|
30
32
|
query.graph(*flat_relations)
|
31
33
|
.select_graph_attributes(flat_selected_attributes)
|
32
34
|
.exclude_graph_attributes(flat_excluded_attributes)
|
35
|
+
.graph_queries(flat_queries)
|
33
36
|
end
|
34
37
|
|
35
38
|
def fetch_graph(rows)
|
36
39
|
return if rows.empty?
|
37
|
-
|
38
40
|
graph.roots.each do |node|
|
39
41
|
relation_of(node).fetch_graph environment,
|
40
42
|
rows,
|
41
43
|
node[:selected_attributes],
|
42
|
-
node[:excluded_attributes]
|
44
|
+
node[:excluded_attributes],
|
45
|
+
node[:queries] ,
|
43
46
|
subgraph_of(node)
|
44
47
|
end
|
45
48
|
end
|
@@ -76,33 +79,43 @@ module Rasti
|
|
76
79
|
end
|
77
80
|
end
|
78
81
|
|
82
|
+
def flat_queries
|
83
|
+
graph.each_with_object(Hash::Indifferent.new) do |node, hash|
|
84
|
+
hash[node.id] = node[:queries]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
79
88
|
def subgraph_of(node)
|
80
89
|
relations = []
|
81
90
|
selected = Hash::Indifferent.new
|
82
91
|
excluded = Hash::Indifferent.new
|
92
|
+
queries = Hash::Indifferent.new
|
83
93
|
|
84
94
|
node.descendants.each do |descendant|
|
85
95
|
id = descendant.id[node[:name].length+1..-1]
|
86
96
|
relations << id
|
87
97
|
selected[id] = descendant[:selected_attributes]
|
88
98
|
excluded[id] = descendant[:excluded_attributes]
|
99
|
+
queries[id] = descendant[:queries]
|
89
100
|
end
|
90
101
|
|
91
102
|
Graph.new environment,
|
92
103
|
relation_of(node).target_collection_class,
|
93
104
|
relations,
|
94
105
|
selected,
|
95
|
-
excluded
|
106
|
+
excluded,
|
107
|
+
queries
|
96
108
|
end
|
97
109
|
|
98
|
-
def build_graph(relations, selected_attributes, excluded_attributes)
|
110
|
+
def build_graph(relations, selected_attributes, excluded_attributes, queries)
|
99
111
|
HierarchicalGraph.new.tap do |graph|
|
100
112
|
flatten(relations).each do |relation|
|
101
113
|
sections = relation.split('.')
|
102
114
|
|
103
115
|
graph.add_node relation, name: sections.last.to_sym,
|
104
116
|
selected_attributes: selected_attributes[relation],
|
105
|
-
excluded_attributes: excluded_attributes[relation]
|
117
|
+
excluded_attributes: excluded_attributes[relation],
|
118
|
+
queries: queries[relation]
|
106
119
|
|
107
120
|
if sections.count > 1
|
108
121
|
parent_id = sections[0..-2].join('.')
|
@@ -126,4 +139,4 @@ module Rasti
|
|
126
139
|
end
|
127
140
|
end
|
128
141
|
end
|
129
|
-
end
|
142
|
+
end
|
@@ -19,9 +19,9 @@ module Rasti
|
|
19
19
|
@relation_data_source_name ||= options[:relation_data_source_name] || source_collection_class.data_source_name
|
20
20
|
end
|
21
21
|
|
22
|
-
def fetch_graph(environment, rows, selected_attributes=nil, excluded_attributes=nil, relations_graph=nil)
|
22
|
+
def fetch_graph(environment, rows, selected_attributes=nil, excluded_attributes=nil, queries=nil, relations_graph=nil)
|
23
23
|
pks = rows.map { |row| row[source_collection_class.primary_key] }
|
24
|
-
|
24
|
+
|
25
25
|
if target_collection_class.data_source_name == relation_data_source_name
|
26
26
|
target_data_source = environment.data_source_of target_collection_class
|
27
27
|
|
@@ -30,10 +30,14 @@ module Rasti
|
|
30
30
|
.where(Sequel[relation_collection_name][source_foreign_key] => pks)
|
31
31
|
.select_all(target_collection_class.collection_name)
|
32
32
|
|
33
|
-
selected_attributes ||= target_collection_class.collection_attributes - excluded_attributes
|
34
|
-
dataset = dataset.select(*selected_attributes.map { |a| Sequel[target_collection_class.collection_name][a] })
|
33
|
+
selected_attributes ||= target_collection_class.collection_attributes - excluded_attributes unless excluded_attributes.nil?
|
34
|
+
dataset = dataset.select(*selected_attributes.map { |a| Sequel[target_collection_class.collection_name][a] }) unless selected_attributes.nil?
|
35
|
+
|
36
|
+
query = Query.new collection_class: target_collection_class, dataset: dataset, environment: environment
|
37
|
+
query = queries.inject(query) { |new_query, sub_query| new_query.send(sub_query) } unless queries.nil?
|
38
|
+
|
39
|
+
join_rows = query.send(:dataset).select_append(Sequel[relation_collection_name][source_foreign_key].as(:source_foreign_key)).all
|
35
40
|
|
36
|
-
join_rows = dataset.select_append(Sequel[relation_collection_name][source_foreign_key].as(:source_foreign_key)).all
|
37
41
|
else
|
38
42
|
relation_data_source = environment.data_source relation_data_source_name
|
39
43
|
|
@@ -42,8 +46,10 @@ module Rasti
|
|
42
46
|
.select_hash_groups(target_foreign_key, source_foreign_key)
|
43
47
|
|
44
48
|
query = target_collection_class.new environment
|
45
|
-
query = query.exclude_attributes(*excluded_attributes)
|
46
|
-
query = query.select_attributes(*selected_attributes)
|
49
|
+
query = query.exclude_attributes(*excluded_attributes) unless excluded_attributes.nil?
|
50
|
+
query = query.select_attributes(*selected_attributes) unless selected_attributes.nil?
|
51
|
+
|
52
|
+
query = queries.inject(query) { |new_query, sub_query| new_query.send(sub_query) } unless queries.nil?
|
47
53
|
|
48
54
|
join_rows = query.where(target_collection_class.primary_key => relation_index.keys).raw.flat_map do |row|
|
49
55
|
relation_index[row[target_collection_class.primary_key]].map do |source_primary_key|
|
@@ -110,4 +116,4 @@ module Rasti
|
|
110
116
|
end
|
111
117
|
end
|
112
118
|
end
|
113
|
-
end
|
119
|
+
end
|
@@ -7,14 +7,18 @@ module Rasti
|
|
7
7
|
@foreign_key ||= options[:foreign_key] || target_collection_class.foreign_key
|
8
8
|
end
|
9
9
|
|
10
|
-
def fetch_graph(environment, rows, selected_attributes=nil, excluded_attributes=nil, relations_graph=nil)
|
10
|
+
def fetch_graph(environment, rows, selected_attributes=nil, excluded_attributes=nil, queries=nil, relations_graph=nil)
|
11
11
|
fks = rows.map { |row| row[foreign_key] }.uniq
|
12
12
|
|
13
13
|
target_collection = target_collection_class.new environment
|
14
14
|
|
15
15
|
query = target_collection.where(source_collection_class.primary_key => fks)
|
16
|
-
|
17
|
-
query = query.
|
16
|
+
|
17
|
+
query = query.exclude_attributes(*excluded_attributes) unless excluded_attributes.nil?
|
18
|
+
query = query.select_attributes(*selected_attributes) unless selected_attributes.nil?
|
19
|
+
|
20
|
+
query = queries.inject(query) { |new_query, sub_query| new_query.send(sub_query) } unless queries.nil?
|
21
|
+
|
18
22
|
query = relations_graph.apply_to query if relations_graph
|
19
23
|
|
20
24
|
relation_rows = query.each_with_object({}) do |row, hash|
|
@@ -47,4 +51,4 @@ module Rasti
|
|
47
51
|
end
|
48
52
|
end
|
49
53
|
end
|
50
|
-
end
|
54
|
+
end
|
@@ -7,14 +7,17 @@ module Rasti
|
|
7
7
|
@foreign_key ||= options[:foreign_key] || source_collection_class.foreign_key
|
8
8
|
end
|
9
9
|
|
10
|
-
def fetch_graph(environment, rows, selected_attributes=nil, excluded_attributes=nil, relations_graph=nil)
|
10
|
+
def fetch_graph(environment, rows, selected_attributes=nil, excluded_attributes=nil, queries=nil, relations_graph=nil)
|
11
11
|
pks = rows.map { |row| row[source_collection_class.primary_key] }.uniq
|
12
12
|
|
13
13
|
target_collection = target_collection_class.new environment
|
14
14
|
|
15
15
|
query = target_collection.where(foreign_key => pks)
|
16
|
-
query = query.exclude_attributes(*excluded_attributes)
|
17
|
-
query = query.select_attributes(*selected_attributes)
|
16
|
+
query = query.exclude_attributes(*excluded_attributes) unless excluded_attributes.nil?
|
17
|
+
query = query.select_attributes(*selected_attributes) unless selected_attributes.nil?
|
18
|
+
|
19
|
+
query = queries.inject(query) { |new_query, sub_query| new_query.send(sub_query) } unless queries.nil?
|
20
|
+
|
18
21
|
query = relations_graph.apply_to query if relations_graph
|
19
22
|
|
20
23
|
relation_rows = query.group_by(&foreign_key)
|
@@ -62,4 +65,4 @@ module Rasti
|
|
62
65
|
end
|
63
66
|
end
|
64
67
|
end
|
65
|
-
end
|
68
|
+
end
|
data/lib/rasti/db/version.rb
CHANGED
data/spec/minitest_helper.rb
CHANGED
@@ -20,10 +20,11 @@ Comment = Rasti::DB::Model[:id, :text, :user_id, :user, :post_id, :post, :tags]
|
|
20
20
|
Category = Rasti::DB::Model[:id, :name, :posts]
|
21
21
|
Person = Rasti::DB::Model[:document_number, :first_name, :last_name, :birth_date, :user_id, :user, :languages, :full_name]
|
22
22
|
Language = Rasti::DB::Model[:id, :name, :people, :countries]
|
23
|
-
Country = Rasti::DB::Model[:id, :name, :language_id]
|
23
|
+
Country = Rasti::DB::Model[:id, :name, :population, :language_id]
|
24
24
|
|
25
25
|
|
26
26
|
class Users < Rasti::DB::Collection
|
27
|
+
|
27
28
|
one_to_many :posts
|
28
29
|
one_to_many :comments
|
29
30
|
one_to_one :person
|
@@ -42,6 +43,7 @@ class Users < Rasti::DB::Collection
|
|
42
43
|
end
|
43
44
|
|
44
45
|
class Posts < Rasti::DB::Collection
|
46
|
+
|
45
47
|
many_to_one :user
|
46
48
|
many_to_one :language
|
47
49
|
many_to_many :categories
|
@@ -62,6 +64,18 @@ class Posts < Rasti::DB::Collection
|
|
62
64
|
end
|
63
65
|
end
|
64
66
|
|
67
|
+
query :only_title do
|
68
|
+
chainable do
|
69
|
+
dataset.select(:title).select_append(:id, :user_id)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
query :append_body do
|
74
|
+
chainable do
|
75
|
+
dataset.select_append(:body)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
65
79
|
computed_attribute :notice do
|
66
80
|
Rasti::DB::ComputedAttribute.new Sequel.join([:title, ': ', :body])
|
67
81
|
end
|
@@ -73,6 +87,7 @@ class Posts < Rasti::DB::Collection
|
|
73
87
|
end
|
74
88
|
|
75
89
|
class Comments < Rasti::DB::Collection
|
90
|
+
|
76
91
|
many_to_one :user
|
77
92
|
many_to_one :post
|
78
93
|
|
@@ -82,13 +97,17 @@ class Comments < Rasti::DB::Collection
|
|
82
97
|
.select_all(:posts)
|
83
98
|
.map { |row| Post.new row }
|
84
99
|
end
|
100
|
+
|
85
101
|
end
|
86
102
|
|
87
103
|
class Categories < Rasti::DB::Collection
|
104
|
+
|
88
105
|
many_to_many :posts
|
106
|
+
|
89
107
|
end
|
90
108
|
|
91
109
|
class People < Rasti::DB::Collection
|
110
|
+
|
92
111
|
set_collection_name :people
|
93
112
|
set_primary_key :document_number
|
94
113
|
set_foreign_key :document_number
|
@@ -100,18 +119,23 @@ class People < Rasti::DB::Collection
|
|
100
119
|
computed_attribute :full_name do |db|
|
101
120
|
Rasti::DB::ComputedAttribute.new Sequel.join([:first_name, ' ', :last_name])
|
102
121
|
end
|
122
|
+
|
103
123
|
end
|
104
124
|
|
105
125
|
class Languages < Rasti::DB::Collection
|
126
|
+
|
106
127
|
set_data_source_name :custom
|
107
128
|
|
108
129
|
many_to_many :people, collection: People, relation_data_source_name: :default
|
109
130
|
one_to_many :posts
|
110
131
|
one_to_many :countries
|
132
|
+
|
111
133
|
end
|
112
134
|
|
113
135
|
class Countries < Rasti::DB::Collection
|
136
|
+
|
114
137
|
many_to_one :language
|
138
|
+
|
115
139
|
end
|
116
140
|
|
117
141
|
|
@@ -140,7 +164,6 @@ class Minitest::Spec
|
|
140
164
|
|
141
165
|
let :db do
|
142
166
|
Sequel.connect(driver).tap do |db|
|
143
|
-
|
144
167
|
db.create_table :users do
|
145
168
|
primary_key :id
|
146
169
|
String :name, null: false, unique: true
|
@@ -193,19 +216,16 @@ class Minitest::Spec
|
|
193
216
|
Integer :population, null: false
|
194
217
|
Integer :language_id, null: false, index: true
|
195
218
|
end
|
196
|
-
|
197
219
|
end
|
198
220
|
end
|
199
221
|
|
200
222
|
let :custom_db do
|
201
223
|
Sequel.connect(driver).tap do |db|
|
202
|
-
|
203
224
|
db.create_table :languages do
|
204
225
|
primary_key :id
|
205
226
|
String :name, null: false, unique: true
|
206
227
|
end
|
207
|
-
|
208
228
|
end
|
209
229
|
end
|
210
230
|
|
211
|
-
end
|
231
|
+
end
|
data/spec/query_spec.rb
CHANGED
@@ -48,6 +48,8 @@ describe 'Query' do
|
|
48
48
|
|
49
49
|
let(:countries_query) { Rasti::DB::Query.new collection_class: Countries, dataset: db[:countries], environment: environment }
|
50
50
|
|
51
|
+
let(:categories_query) { Rasti::DB::Query.new collection_class: Categories, dataset: db[:categories], environment: environment }
|
52
|
+
|
51
53
|
it 'Count' do
|
52
54
|
users_query.count.must_equal 10
|
53
55
|
end
|
@@ -146,6 +148,59 @@ describe 'Query' do
|
|
146
148
|
.must_equal [post]
|
147
149
|
end
|
148
150
|
|
151
|
+
it 'Graph with query many to one' do
|
152
|
+
comments_query.graph('post')
|
153
|
+
.graph_queries('post' => [:only_title])
|
154
|
+
.where(id: 1)
|
155
|
+
.all
|
156
|
+
.must_equal [
|
157
|
+
Comment.new(id: 1, text: 'Comment 1', user_id: 5, post_id: 1, post: Post.new(id: 1, user_id: 2, title: 'Sample post'), tags: [])
|
158
|
+
]
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'Graph with query one to many' do
|
162
|
+
users_query.graph('posts')
|
163
|
+
.graph_queries('posts' => [:only_title])
|
164
|
+
.where(id: 1)
|
165
|
+
.all
|
166
|
+
.must_equal [
|
167
|
+
User.new(id: 1, name: 'User 1', posts: [Post.new(user_id: 1, id: 2, title: 'Another post')])
|
168
|
+
]
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'Graph with query many to many' do
|
172
|
+
categories_query.graph('posts')
|
173
|
+
.graph_queries('posts' => [:only_title])
|
174
|
+
.where(id: 1)
|
175
|
+
.all
|
176
|
+
.must_equal [
|
177
|
+
Category.new(id: 1, name: 'Category 1', posts: [Post.new(user_id: 2, id: 1, title: 'Sample post')])
|
178
|
+
]
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'Graph with n queries' do
|
182
|
+
comments_query.graph('post')
|
183
|
+
.graph_queries('post' => [:only_title, :append_body])
|
184
|
+
.where(id: 1)
|
185
|
+
.all
|
186
|
+
.must_equal [
|
187
|
+
Comment.new(id: 1, text: 'Comment 1', user_id: 5, post_id: 1, post: Post.new(id: 1, user_id: 2, title: 'Sample post', body: '...'), tags: [])
|
188
|
+
]
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'Graph query missing must raise error' do
|
193
|
+
proc { posts_query.graph('comments.user')
|
194
|
+
.graph_queries('comments.user' => [:with_not_exists_query])
|
195
|
+
.all }.must_raise NoMethodError
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'Graph with query params must raise error' do
|
199
|
+
proc { users_query.graph('posts')
|
200
|
+
.graph_queries('posts' => [:created_by])
|
201
|
+
.all }.must_raise ArgumentError
|
202
|
+
end
|
203
|
+
|
149
204
|
describe 'Select computed attributes' do
|
150
205
|
it 'With join' do
|
151
206
|
db[:comments].insert post_id: 1, user_id: 5, text: 'Comment 4'
|
@@ -179,12 +234,12 @@ describe 'Query' do
|
|
179
234
|
language_id: 1
|
180
235
|
|
181
236
|
[countries_query.detect(id: 1), countries_query.where(id: 1).order(:name).last, countries_query.all.first].each do |country|
|
182
|
-
country.must_equal Country.new(language_id: 1, id: 1, name: 'Argentina')
|
237
|
+
country.must_equal Country.new(language_id: 1, id: 1, name: 'Argentina', population: 40000000)
|
183
238
|
end
|
184
239
|
end
|
185
240
|
|
186
241
|
it 'Graph nested' do
|
187
|
-
languages_query.graph(:countries).first.countries.must_equal [Country.new(language_id: 1, id: 1, name: 'Argentina')]
|
242
|
+
languages_query.graph(:countries).first.countries.must_equal [Country.new(language_id: 1, id: 1, name: 'Argentina', population: 40000000)]
|
188
243
|
end
|
189
244
|
|
190
245
|
end
|
@@ -576,4 +631,4 @@ describe 'Query' do
|
|
576
631
|
|
577
632
|
end
|
578
633
|
|
579
|
-
end
|
634
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rasti-db
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabriel Naiman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|