rasti-db 4.1.1 → 4.2.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/.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
|