rasti-db 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed5332eda8adfde54d7465d8f3f631d9b542f54d17c78644703d2f9054cb1bca
4
- data.tar.gz: 5452a867c32c4083f10149c3496093e731d6f8d64a6b13fafd57ebcc0d7e38c8
3
+ metadata.gz: b801c94dd51c4ee11f624cb542d784a42e445622e0090c42cb9d47322b87e855
4
+ data.tar.gz: 518c58daf408e477f80cdac52837a0e2d3256938352c888a73e369f3eca5b7a2
5
5
  SHA512:
6
- metadata.gz: 31a039adaeb64c98fa7f5c38b2155a009f78e2a824d9c8d4e9969bd2d9ff7599afc899d5b10c3864694a22540061cf0f439f681792f2f89e9eb9da030aa7c28e
7
- data.tar.gz: 393c7b91108ff8ab2c8effed2016b4ea912747baf65194c05614a01ef9d4d4447d41f350e996f0bdf8b14b9de102ced09bd023ce9cef590306068c28a8ae051c
6
+ metadata.gz: ae1452c9b77c6d50f9aa8b05b85f9970c801369e7e3a67a7a88436762fbacea4191390e114be066458d7bc2dc563f834cda93a364185354c74267db86798a889
7
+ data.tar.gz: 2707b44943ea63f17cd0f613c4fa08bdfc0663e8fde17f604c8c6ce9ba8f1fff23b4fa1641dfe42f77810a55fda6794491f5e8d8cb756c4fc19adb1cbb8cba76
data/.travis.yml CHANGED
@@ -1,7 +1,6 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
- - 2.0
5
4
  - 2.1
6
5
  - 2.2
7
6
  - 2.3
@@ -9,8 +8,7 @@ rvm:
9
8
  - 2.5
10
9
  - 2.6
11
10
  - 2.7
12
- - jruby-9.1.7.0
13
- - jruby-9.1.16.0
11
+ - jruby-9.2.9.0
14
12
  - ruby-head
15
13
  - jruby-head
16
14
 
data/README.md CHANGED
@@ -4,7 +4,6 @@
4
4
  [![Build Status](https://travis-ci.org/gabynaiman/rasti-db.svg?branch=master)](https://travis-ci.org/gabynaiman/rasti-db)
5
5
  [![Coverage Status](https://coveralls.io/repos/github/gabynaiman/rasti-db/badge.svg?branch=master)](https://coveralls.io/github/gabynaiman/rasti-db?branch=master)
6
6
  [![Code Climate](https://codeclimate.com/github/gabynaiman/rasti-db.svg)](https://codeclimate.com/github/gabynaiman/rasti-db)
7
- [![Dependency Status](https://gemnasium.com/gabynaiman/rasti-db.svg)](https://gemnasium.com/gabynaiman/rasti-db)
8
7
 
9
8
  Database collections and relations
10
9
 
@@ -108,7 +107,7 @@ class Posts < Rasti::DB::Collection
108
107
  chainable do
109
108
  dataset.join(with_schema(:comments), post_id: :id)
110
109
  .where(with_schema(:comments, :user_id) => user_id)
111
- .select_all(with_schema(:posts))
110
+ .select_all(:posts)
112
111
  .distinct
113
112
  end
114
113
  end
@@ -162,17 +161,49 @@ end
162
161
  posts.all # => [Post, ...]
163
162
  posts.first # => Post
164
163
  posts.count # => 1
164
+
165
165
  posts.where(id: [1,2]) # => [Post, ...]
166
166
  posts.where{id > 1}.limit(10).offset(20) } # => [Post, ...]
167
- posts.graph(:user, :categories, 'comments.user') # => [Post(User, [Categories, ...], [Comments(User)]), ...]
168
- posts.created_by(1) # => [Post, ...]
169
- posts.created_by(1).entitled('...').commented_by(2) # => [Post, ...]
170
- posts.with_categories([1,2]) # => [Post, ...]
167
+
171
168
  posts.where(id: [1,2]).raw # => [{id:1, ...}, {id:2, ...}]
172
169
  posts.where(id: [1,2]).primary_keys # => [1,2]
173
170
  posts.where(id: [1,2]).pluck(:id) # => [1,2]
174
171
  posts.where(id: [1,2]).pluck(:id, :title) # => [[1, ...], [2, ...]]
172
+
173
+ posts.created_by(1) # => [Post, ...]
174
+ posts.created_by(1).entitled('...').commented_by(2) # => [Post, ...]
175
+ posts.with_categories([1,2]) # => [Post, ...]
176
+
177
+ posts.graph(:user, :categories, 'comments.user') # => [Post(User, [Categories, ...], [Comments(User)]), ...]
178
+
175
179
  posts.join(:user).where(name: 'User 4') # => [Post, ...]
180
+
181
+ posts.select_attributes(:id, :title) # => [Post, ...]
182
+ posts.exclude_attributes(:id, :title) # => [Post, ...]
183
+ posts.all_attributes # => [Post, ...]
184
+
185
+ posts.graph('user.person').select_graph_attributes(user: [:id], 'user.person': [:last_name, :user_id]) # => [Post, ...]
186
+ posts.graph('user.person').exclude_graph_attributes(user: [:name], 'user.person': [:first_name, :last_name]) # => [Post, ...]
187
+ posts.graph('user.person').all_graph_attributes('user.person') # => [Post, ...]
188
+ ```
189
+ ### Natural Query Language
190
+
191
+ ```ruby
192
+ posts.nql('id = 1') # => Equal
193
+ posts.nql('id != 1') # => Not equal
194
+ posts.nql('title: My post') # => Include
195
+ posts.nql('title !: My post') # => Not include
196
+ posts.nql('title ~ My post') # => Insensitive like
197
+ posts.nql('id > 1') # => Greater
198
+ posts.nql('id >= 1') # => Greater or equal
199
+ posts.nql('id < 10') # => Less
200
+ posts.nql('id <= 10') # => Less or equal
201
+
202
+ posts.nql('id = 1 | id = 2') # => Or
203
+ posts.nql('id > 1 & title: "My post"') # => And
204
+ posts.nql('(id > 3 & id < 10) | title: "My post"') # => Precedence
205
+
206
+ posts.nql('comments.user.person.document_number = 7') # => Nested
176
207
  ```
177
208
 
178
209
  ## Development
@@ -2,9 +2,8 @@ module Rasti
2
2
  module DB
3
3
  class Collection
4
4
 
5
- QUERY_METHODS = (Query::DATASET_CHAINED_METHODS + [:graph, :join, :count, :all, :each, :first, :pluck, :primary_keys, :any?, :empty?, :raw, :nql]).freeze
5
+ QUERY_METHODS = Query.public_instance_methods - Object.public_instance_methods
6
6
 
7
- include Enumerable
8
7
  include Helpers::WithSchema
9
8
 
10
9
  class << self
@@ -16,8 +15,8 @@ module Rasti
16
15
  @collection_name ||= underscore(demodulize(name)).to_sym
17
16
  end
18
17
 
19
- def collection_fields
20
- @collection_fields ||= model.attributes - relations.keys
18
+ def collection_attributes
19
+ @collection_attributes ||= model.attributes - relations.keys
21
20
  end
22
21
 
23
22
  def primary_key
@@ -80,7 +79,7 @@ module Rasti
80
79
  queries[name] = lambda || block
81
80
 
82
81
  define_method name do |*args|
83
- query.instance_exec(*args, &self.class.queries[name])
82
+ default_query.instance_exec(*args, &self.class.queries.fetch(name))
84
83
  end
85
84
  end
86
85
 
@@ -93,6 +92,12 @@ module Rasti
93
92
  @schema = schema ? schema.to_sym : nil
94
93
  end
95
94
 
95
+ QUERY_METHODS.each do |method|
96
+ define_method method do |*args, &block|
97
+ default_query.public_send method, *args, &block
98
+ end
99
+ end
100
+
96
101
  def dataset
97
102
  db[qualified_collection_name]
98
103
  end
@@ -172,12 +177,6 @@ module Rasti
172
177
  where(self.class.primary_key => primary_key).graph(*relations).first
173
178
  end
174
179
 
175
- QUERY_METHODS.each do |method|
176
- define_method method do |*args, &block|
177
- query.public_send method, *args, &block
178
- end
179
- end
180
-
181
180
  def exists?(filter=nil, &block)
182
181
  build_query(filter, &block).any?
183
182
  end
@@ -199,16 +198,19 @@ module Rasti
199
198
  schema.nil? ? Sequel[self.class.collection_name] : Sequel[schema][self.class.collection_name]
200
199
  end
201
200
 
202
- def query
203
- Query.new self.class, dataset, [], schema
201
+ def default_query
202
+ Query.new collection_class: self.class,
203
+ dataset: dataset.select_all(self.class.collection_name),
204
+ schema: schema
204
205
  end
205
206
 
206
207
  def build_query(filter=nil, &block)
207
208
  raise ArgumentError, 'must specify filter hash or block' if filter.nil? && block.nil?
209
+
208
210
  if filter
209
- query.where filter
211
+ default_query.where filter
210
212
  else
211
- block.arity == 0 ? query.instance_eval(&block) : block.call(query)
213
+ block.arity == 0 ? default_query.instance_eval(&block) : block.call(default_query)
212
214
  end
213
215
  end
214
216
 
@@ -7,19 +7,25 @@ module Rasti
7
7
  include Enumerable
8
8
  include Helpers::WithSchema
9
9
 
10
- def initialize(collection_class, dataset, relations=[], schema=nil)
10
+ def initialize(collection_class:, dataset:, relations_graph:nil, schema:nil)
11
11
  @collection_class = collection_class
12
12
  @dataset = dataset
13
- @relations = relations
13
+ @relations_graph = relations_graph || Relations::Graph.new(dataset.db, schema, collection_class)
14
14
  @schema = schema
15
15
  end
16
16
 
17
+ DATASET_CHAINED_METHODS.each do |method|
18
+ define_method method do |*args, &block|
19
+ build_query dataset: dataset.public_send(method, *args, &block)
20
+ end
21
+ end
22
+
17
23
  def raw
18
24
  dataset.all
19
25
  end
20
26
 
21
27
  def pluck(*attributes)
22
- ds = dataset.select(*attributes.map { |attr| Sequel[collection_class.collection_name][attr] })
28
+ ds = dataset.select(*attributes.map { |a| Sequel[collection_class.collection_name][a] })
23
29
  attributes.count == 1 ? ds.map { |r| r[attributes.first] } : ds.map(&:values)
24
30
  end
25
31
 
@@ -27,6 +33,31 @@ module Rasti
27
33
  pluck collection_class.primary_key
28
34
  end
29
35
 
36
+ def select_attributes(*attributes)
37
+ build_query dataset: dataset.select(*attributes.map { |a| Sequel[collection_class.collection_name][a] })
38
+ end
39
+
40
+ def exclude_attributes(*excluded_attributes)
41
+ attributes = collection_class.collection_attributes - excluded_attributes
42
+ select_attributes(*attributes)
43
+ end
44
+
45
+ def all_attributes
46
+ build_query dataset: dataset.select_all(collection_class.collection_name)
47
+ end
48
+
49
+ def select_graph_attributes(selected_attributes)
50
+ build_query relations_graph: relations_graph.merge(selected_attributes: selected_attributes)
51
+ end
52
+
53
+ def exclude_graph_attributes(excluded_attributes)
54
+ build_query relations_graph: relations_graph.merge(excluded_attributes: excluded_attributes)
55
+ end
56
+
57
+ def all_graph_attributes(*relations)
58
+ build_query relations_graph: relations_graph.with_all_attributes_for(relations)
59
+ end
60
+
30
61
  def all
31
62
  with_graph(dataset.all).map do |row|
32
63
  collection_class.model.new row
@@ -38,27 +69,18 @@ module Rasti
38
69
  all.each(&block)
39
70
  end
40
71
 
41
- DATASET_CHAINED_METHODS.each do |method|
42
- define_method method do |*args, &block|
43
- Query.new collection_class,
44
- dataset.public_send(method, *args, &block),
45
- relations,
46
- schema
47
- end
48
- end
49
-
50
- def graph(*rels)
51
- Query.new collection_class,
52
- dataset,
53
- (relations | rels),
54
- schema
72
+ def graph(*relations)
73
+ build_query relations_graph: relations_graph.merge(relations: relations)
55
74
  end
56
75
 
57
- def join(*rels)
58
- Query.new collection_class,
59
- Relations::GraphBuilder.joins_to(dataset, rels, collection_class, schema),
60
- relations,
61
- schema
76
+ def join(*relations)
77
+ graph = Relations::Graph.new dataset.db, schema, collection_class, relations
78
+
79
+ ds = graph.add_joins(dataset)
80
+ .distinct
81
+ .select_all(collection_class.collection_name)
82
+
83
+ build_query dataset: ds
62
84
  end
63
85
 
64
86
  def count
@@ -105,25 +127,41 @@ module Rasti
105
127
 
106
128
  private
107
129
 
130
+ attr_reader :collection_class, :dataset, :relations_graph, :schema
131
+
132
+ def build_query(**args)
133
+ current_args = {
134
+ collection_class: collection_class,
135
+ dataset: dataset,
136
+ relations_graph: relations_graph,
137
+ schema: schema
138
+ }
139
+
140
+ Query.new(**current_args.merge(args))
141
+ end
142
+
108
143
  def chainable(&block)
109
- ds = instance_eval(&block)
110
- Query.new collection_class, ds, relations, schema
144
+ build_query dataset: instance_eval(&block)
111
145
  end
112
146
 
113
147
  def with_related(relation_name, primary_keys)
114
148
  ds = collection_class.relations[relation_name].apply_filter dataset, schema, primary_keys
115
- Query.new collection_class, ds, relations, schema
149
+ build_query dataset: ds
116
150
  end
117
151
 
118
152
  def with_graph(data)
119
153
  rows = data.is_a?(Array) ? data : [data]
120
- Relations::GraphBuilder.graph_to rows, relations, collection_class, dataset.db, schema
154
+ relations_graph.fetch_graph rows
121
155
  data
122
156
  end
123
157
 
158
+ def nql_parser
159
+ NQL::SyntaxParser.new
160
+ end
161
+
124
162
  def method_missing(method, *args, &block)
125
- if collection_class.queries.key?(method)
126
- instance_exec(*args, &collection_class.queries[method])
163
+ if collection_class.queries.key? method
164
+ instance_exec(*args, &collection_class.queries.fetch(method))
127
165
  else
128
166
  super
129
167
  end
@@ -133,12 +171,6 @@ module Rasti
133
171
  collection_class.queries.key?(method) || super
134
172
  end
135
173
 
136
- def nql_parser
137
- NQL::SyntaxParser.new
138
- end
139
-
140
- attr_reader :collection_class, :dataset, :relations, :schema
141
-
142
174
  end
143
175
  end
144
176
  end
@@ -0,0 +1,135 @@
1
+ module Rasti
2
+ module DB
3
+ module Relations
4
+ class Graph
5
+
6
+ def initialize(db, schema, collection_class, relations=[], selected_attributes={}, excluded_attributes={})
7
+ @db = db
8
+ @schema = schema
9
+ @collection_class = collection_class
10
+ @graph = build_graph relations,
11
+ Hash::Indifferent.new(selected_attributes),
12
+ Hash::Indifferent.new(excluded_attributes)
13
+ end
14
+
15
+ def merge(relations:[], selected_attributes:{}, excluded_attributes:{})
16
+ Graph.new db,
17
+ schema,
18
+ collection_class,
19
+ (flat_relations | relations),
20
+ flat_selected_attributes.merge(selected_attributes),
21
+ flat_excluded_attributes.merge(excluded_attributes)
22
+ end
23
+
24
+ def with_all_attributes_for(relations)
25
+ relations_with_all_attributes = relations.map { |r| [r, nil] }.to_h
26
+
27
+ merge selected_attributes: relations_with_all_attributes,
28
+ excluded_attributes: relations_with_all_attributes
29
+ end
30
+
31
+ def apply_to(query)
32
+ query.graph(*flat_relations)
33
+ .select_graph_attributes(flat_selected_attributes)
34
+ .exclude_graph_attributes(flat_excluded_attributes)
35
+ end
36
+
37
+ def fetch_graph(rows)
38
+ return if rows.empty?
39
+
40
+ graph.roots.each do |node|
41
+ relation_of(node).fetch_graph rows,
42
+ db,
43
+ schema,
44
+ node[:selected_attributes],
45
+ node[:excluded_attributes] ,
46
+ subgraph_of(node)
47
+ end
48
+ end
49
+
50
+ def add_joins(dataset, prefix=nil)
51
+ graph.roots.each do |node|
52
+ relation = relation_of node
53
+ dataset = relation.add_join dataset, schema, prefix
54
+ dataset = subgraph_of(node).add_joins dataset, relation.join_relation_name(prefix)
55
+ end
56
+
57
+ dataset
58
+ end
59
+
60
+ private
61
+
62
+ attr_reader :db, :schema, :collection_class, :graph
63
+
64
+ def relation_of(node)
65
+ collection_class.relations.fetch(node[:name])
66
+ end
67
+
68
+ def flat_relations
69
+ graph.map(&:id)
70
+ end
71
+
72
+ def flat_selected_attributes
73
+ graph.each_with_object(Hash::Indifferent.new) do |node, hash|
74
+ hash[node.id] = node[:selected_attributes]
75
+ end
76
+ end
77
+
78
+ def flat_excluded_attributes
79
+ graph.each_with_object(Hash::Indifferent.new) do |node, hash|
80
+ hash[node.id] = node[:excluded_attributes]
81
+ end
82
+ end
83
+
84
+ def subgraph_of(node)
85
+ relations = []
86
+ selected = Hash::Indifferent.new
87
+ excluded = Hash::Indifferent.new
88
+
89
+ node.descendants.each do |descendant|
90
+ id = descendant.id[node[:name].length+1..-1]
91
+ relations << id
92
+ selected[id] = descendant[:selected_attributes]
93
+ excluded[id] = descendant[:excluded_attributes]
94
+ end
95
+
96
+ Graph.new db,
97
+ schema,
98
+ relation_of(node).target_collection_class,
99
+ relations,
100
+ selected,
101
+ excluded
102
+ end
103
+
104
+ def build_graph(relations, selected_attributes, excluded_attributes)
105
+ HierarchicalGraph.new.tap do |graph|
106
+ flatten(relations).each do |relation|
107
+ sections = relation.split('.')
108
+
109
+ graph.add_node relation, name: sections.last.to_sym,
110
+ selected_attributes: selected_attributes[relation],
111
+ excluded_attributes: excluded_attributes[relation]
112
+
113
+ if sections.count > 1
114
+ parent_id = sections[0..-2].join('.')
115
+ graph.add_relation parent_id: parent_id,
116
+ child_id: relation
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ def flatten(relations)
123
+ relations.flat_map do |relation|
124
+ parents = []
125
+ relation.to_s.split('.').map do |section|
126
+ parents << section
127
+ parents.compact.join('.')
128
+ end
129
+ end.uniq.sort
130
+ end
131
+
132
+ end
133
+ end
134
+ end
135
+ end
@@ -19,7 +19,7 @@ module Rasti
19
19
  schema.nil? ? Sequel[relation_collection_name] : Sequel[schema][relation_collection_name]
20
20
  end
21
21
 
22
- def graph_to(rows, db, schema=nil, relations=[])
22
+ def fetch_graph(rows, db, schema=nil, selected_attributes=nil, excluded_attributes=nil, relations_graph=nil)
23
23
  pks = rows.map { |row| row[source_collection_class.primary_key] }
24
24
 
25
25
  target_collection = target_collection_class.new db, schema
@@ -29,11 +29,11 @@ module Rasti
29
29
  join_rows = target_collection.dataset
30
30
  .join(relation_name, target_foreign_key => target_collection_class.primary_key)
31
31
  .where(Sequel[relation_name][source_foreign_key] => pks)
32
- .select_all(qualified_target_collection_name(schema))
32
+ .select_all(target_collection_class.collection_name)
33
33
  .select_append(Sequel[relation_name][source_foreign_key].as(:source_foreign_key))
34
34
  .all
35
35
 
36
- GraphBuilder.graph_to join_rows, relations, target_collection_class, db, schema
36
+ relations_graph.fetch_graph join_rows if relations_graph
37
37
 
38
38
  relation_rows = join_rows.each_with_object(Hash.new { |h,k| h[k] = [] }) do |row, hash|
39
39
  attributes = row.select { |attr,_| target_collection_class.model.attributes.include? attr }
@@ -45,7 +45,7 @@ module Rasti
45
45
  end
46
46
  end
47
47
 
48
- def join_to(dataset, schema=nil, prefix=nil)
48
+ def add_join(dataset, schema=nil, prefix=nil)
49
49
  many_to_many_relation_alias = with_prefix prefix, "#{relation_collection_name}_#{SecureRandom.base64}"
50
50
 
51
51
  qualified_relation_source = prefix ? Sequel[prefix] : qualified_source_collection_name(schema)
@@ -69,7 +69,7 @@ module Rasti
69
69
 
70
70
  dataset.join(relation_name, source_foreign_key => target_collection_class.primary_key)
71
71
  .where(Sequel[relation_name][target_foreign_key] => primary_keys)
72
- .select_all(qualified_source_collection_name(schema))
72
+ .select_all(source_collection_class.collection_name)
73
73
  .distinct
74
74
  end
75
75
 
@@ -7,23 +7,26 @@ module Rasti
7
7
  @foreign_key ||= options[:foreign_key] || target_collection_class.foreign_key
8
8
  end
9
9
 
10
- def graph_to(rows, db, schema=nil, relations=[])
10
+ def fetch_graph(rows, db, schema=nil, selected_attributes=nil, excluded_attributes=nil, relations_graph=nil)
11
11
  fks = rows.map { |row| row[foreign_key] }.uniq
12
12
 
13
13
  target_collection = target_collection_class.new db, schema
14
14
 
15
- relation_rows = target_collection.where(source_collection_class.primary_key => fks)
16
- .graph(*relations)
17
- .each_with_object({}) do |row, hash|
18
- hash[row.public_send(source_collection_class.primary_key)] = row
19
- end
15
+ query = target_collection.where(source_collection_class.primary_key => fks)
16
+ query = query.exclude_attributes(*excluded_attributes) if excluded_attributes
17
+ query = query.select_attributes(*selected_attributes) if selected_attributes
18
+ query = relations_graph.apply_to query if relations_graph
19
+
20
+ relation_rows = query.each_with_object({}) do |row, hash|
21
+ hash[row.public_send(source_collection_class.primary_key)] = row
22
+ end
20
23
 
21
24
  rows.each do |row|
22
25
  row[name] = relation_rows[row[foreign_key]]
23
26
  end
24
27
  end
25
28
 
26
- def join_to(dataset, schema=nil, prefix=nil)
29
+ def add_join(dataset, schema=nil, prefix=nil)
27
30
  relation_alias = join_relation_name prefix
28
31
 
29
32
  qualified_relation_source = prefix ? Sequel[prefix] : qualified_source_collection_name(schema)
@@ -7,21 +7,24 @@ module Rasti
7
7
  @foreign_key ||= options[:foreign_key] || source_collection_class.foreign_key
8
8
  end
9
9
 
10
- def graph_to(rows, db, schema=nil, relations=[])
10
+ def fetch_graph(rows, db, schema=nil, selected_attributes=nil, excluded_attributes=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 db, schema
14
14
 
15
- relation_rows = target_collection.where(foreign_key => pks)
16
- .graph(*relations)
17
- .group_by { |r| r.public_send(foreign_key) }
15
+ query = target_collection.where(foreign_key => pks)
16
+ query = query.exclude_attributes(*excluded_attributes) if excluded_attributes
17
+ query = query.select_attributes(*selected_attributes) if selected_attributes
18
+ query = relations_graph.apply_to query if relations_graph
19
+
20
+ relation_rows = query.group_by(&foreign_key)
18
21
 
19
22
  rows.each do |row|
20
23
  row[name] = build_graph_result relation_rows.fetch(row[source_collection_class.primary_key], [])
21
24
  end
22
25
  end
23
26
 
24
- def join_to(dataset, schema=nil, prefix=nil)
27
+ def add_join(dataset, schema=nil, prefix=nil)
25
28
  relation_alias = join_relation_name prefix
26
29
 
27
30
  qualified_relation_source = prefix ? Sequel[prefix] : qualified_source_collection_name(schema)
@@ -1,5 +1,5 @@
1
1
  module Rasti
2
2
  module DB
3
- VERSION = '1.4.0'
3
+ VERSION = '1.5.0'
4
4
  end
5
5
  end
data/lib/rasti/db.rb CHANGED
@@ -3,7 +3,9 @@ require 'consty'
3
3
  require 'time'
4
4
  require 'timing'
5
5
  require 'treetop'
6
+ require 'hierarchical_graph'
6
7
  require 'class_config'
8
+ require 'hash_ext'
7
9
  require 'multi_require'
8
10
 
9
11
  module Rasti
data/rasti-db.gemspec CHANGED
@@ -24,8 +24,10 @@ Gem::Specification.new do |spec|
24
24
  spec.add_runtime_dependency 'timing', '~> 0.1', '>= 0.1.3'
25
25
  spec.add_runtime_dependency 'class_config', '~> 0.0', '>= 0.0.2'
26
26
  spec.add_runtime_dependency 'multi_require', '~> 1.0'
27
+ spec.add_runtime_dependency 'hierarchical_graph', '~> 1.0'
28
+ spec.add_runtime_dependency 'hash_ext', '~> 0.5'
27
29
 
28
- spec.add_development_dependency 'rake', '~> 11.0'
30
+ spec.add_development_dependency 'rake', '~> 12.3'
29
31
  spec.add_development_dependency 'minitest', '~> 5.0', '< 5.11'
30
32
  spec.add_development_dependency 'minitest-colorin', '~> 0.1'
31
33
  spec.add_development_dependency 'minitest-line', '~> 0.6'
@@ -38,10 +40,4 @@ Gem::Specification.new do |spec|
38
40
  else
39
41
  spec.add_development_dependency 'sqlite3', '~> 1.3'
40
42
  end
41
-
42
- if RUBY_VERSION < '2'
43
- spec.add_development_dependency 'term-ansicolor', '~> 1.3.0'
44
- spec.add_development_dependency 'tins', '~> 1.6.0'
45
- spec.add_development_dependency 'json', '~> 1.8'
46
- end
47
43
  end
@@ -6,7 +6,7 @@ describe 'Collection' do
6
6
 
7
7
  it 'Implicit' do
8
8
  Users.collection_name.must_equal :users
9
- Users.collection_fields.must_equal [:id, :name]
9
+ Users.collection_attributes.must_equal [:id, :name]
10
10
  Users.model.must_equal User
11
11
  Users.primary_key.must_equal :id
12
12
  Users.foreign_key.must_equal :user_id
@@ -14,7 +14,7 @@ describe 'Collection' do
14
14
 
15
15
  it 'Explicit' do
16
16
  People.collection_name.must_equal :people
17
- People.collection_fields.must_equal [:document_number, :first_name, :last_name, :birth_date, :user_id]
17
+ People.collection_attributes.must_equal [:document_number, :first_name, :last_name, :birth_date, :user_id]
18
18
  People.model.must_equal Person
19
19
  People.primary_key.must_equal :document_number
20
20
  People.foreign_key.must_equal :document_number
@@ -244,6 +244,24 @@ describe 'Collection' do
244
244
  users.find_graph(user_id, :posts).must_equal User.new id: user_id, name: 'User 1', posts: posts.all
245
245
  end
246
246
 
247
+ it 'Select attributes' do
248
+ id = db[:users].insert name: 'User 1'
249
+
250
+ users.select_attributes(:id).all.must_equal [User.new(id: id)]
251
+ end
252
+
253
+ it 'Exclude attributes' do
254
+ db[:users].insert name: 'User 1'
255
+
256
+ users.exclude_attributes(:id).all.must_equal [User.new(name: 'User 1')]
257
+ end
258
+
259
+ it 'All attributes' do
260
+ id = db[:users].insert name: 'User 1'
261
+
262
+ users.select_attributes(:id).all_attributes.all.must_equal [User.new(id: id, name: 'User 1')]
263
+ end
264
+
247
265
  it 'Count' do
248
266
  1.upto(10) { |i| db[:users].insert name: "User #{i}" }
249
267
 
@@ -430,21 +448,21 @@ describe 'Collection' do
430
448
  stubs = Proc.new do |sql|
431
449
  case sql
432
450
 
433
- when 'SELECT * FROM custom_schema.users',
434
- 'SELECT * FROM custom_schema.users WHERE (id IN (2, 1))'
451
+ when 'SELECT users.* FROM custom_schema.users',
452
+ 'SELECT users.* FROM custom_schema.users WHERE (id IN (2, 1))'
435
453
  [
436
454
  {id: 1},
437
455
  {id: 2}
438
456
  ]
439
457
 
440
- when 'SELECT * FROM custom_schema.posts',
441
- 'SELECT * FROM custom_schema.posts WHERE (user_id IN (1, 2))'
458
+ when 'SELECT posts.* FROM custom_schema.posts',
459
+ 'SELECT posts.* FROM custom_schema.posts WHERE (user_id IN (1, 2))'
442
460
  [
443
461
  {id: 3, user_id: 1},
444
462
  {id: 4, user_id: 2}
445
463
  ]
446
464
 
447
- when 'SELECT * FROM custom_schema.comments WHERE (post_id IN (3, 4))'
465
+ when 'SELECT comments.* FROM custom_schema.comments WHERE (post_id IN (3, 4))'
448
466
  [
449
467
  {id: 5, user_id: 2, post_id: 3},
450
468
  {id: 6, user_id: 1, post_id: 3},
@@ -506,33 +524,34 @@ describe 'Collection' do
506
524
 
507
525
  it 'Chained query' do
508
526
  stub_users.where(id: [1,2]).limit(1).order(:name).all
509
- stub_db.sqls.must_equal ['SELECT * FROM custom_schema.users WHERE (id IN (1, 2)) ORDER BY name LIMIT 1']
527
+ stub_db.sqls.must_equal ['SELECT users.* FROM custom_schema.users WHERE (id IN (1, 2)) ORDER BY name LIMIT 1']
510
528
  end
511
529
 
512
530
  it 'Graph' do
513
531
  stub_posts.graph(:user, :categories, 'comments.user.posts.categories').all
532
+
514
533
  stub_db.sqls.must_equal [
515
- 'SELECT * FROM custom_schema.posts',
516
- 'SELECT * FROM custom_schema.users WHERE (id IN (1, 2))',
517
- 'SELECT custom_schema.categories.*, custom_schema.categories_posts.post_id AS source_foreign_key FROM custom_schema.categories INNER JOIN custom_schema.categories_posts ON (custom_schema.categories_posts.category_id = custom_schema.categories.id) WHERE (custom_schema.categories_posts.post_id IN (3, 4))',
518
- 'SELECT * FROM custom_schema.comments WHERE (post_id IN (3, 4))',
519
- 'SELECT * FROM custom_schema.users WHERE (id IN (2, 1))',
520
- 'SELECT * FROM custom_schema.posts WHERE (user_id IN (1, 2))',
521
- 'SELECT custom_schema.categories.*, custom_schema.categories_posts.post_id AS source_foreign_key FROM custom_schema.categories INNER JOIN custom_schema.categories_posts ON (custom_schema.categories_posts.category_id = custom_schema.categories.id) WHERE (custom_schema.categories_posts.post_id IN (3, 4))'
534
+ 'SELECT posts.* FROM custom_schema.posts',
535
+ 'SELECT categories.*, custom_schema.categories_posts.post_id AS source_foreign_key FROM custom_schema.categories INNER JOIN custom_schema.categories_posts ON (custom_schema.categories_posts.category_id = custom_schema.categories.id) WHERE (custom_schema.categories_posts.post_id IN (3, 4))',
536
+ 'SELECT comments.* FROM custom_schema.comments WHERE (post_id IN (3, 4))',
537
+ 'SELECT users.* FROM custom_schema.users WHERE (id IN (2, 1))',
538
+ 'SELECT posts.* FROM custom_schema.posts WHERE (user_id IN (1, 2))',
539
+ 'SELECT categories.*, custom_schema.categories_posts.post_id AS source_foreign_key FROM custom_schema.categories INNER JOIN custom_schema.categories_posts ON (custom_schema.categories_posts.category_id = custom_schema.categories.id) WHERE (custom_schema.categories_posts.post_id IN (3, 4))',
540
+ 'SELECT users.* FROM custom_schema.users WHERE (id IN (1, 2))'
522
541
  ]
523
542
  end
524
543
 
525
544
  it 'Named query' do
526
545
  stub_posts.commented_by(1).all
527
546
  stub_db.sqls.must_equal [
528
- 'SELECT DISTINCT custom_schema.posts.* FROM custom_schema.posts INNER JOIN custom_schema.comments ON (custom_schema.comments.post_id = custom_schema.posts.id) WHERE (custom_schema.comments.user_id = 1)'
547
+ 'SELECT DISTINCT posts.* FROM custom_schema.posts INNER JOIN custom_schema.comments ON (custom_schema.comments.post_id = custom_schema.posts.id) WHERE (custom_schema.comments.user_id = 1)'
529
548
  ]
530
549
  end
531
550
 
532
551
  it 'Custom query' do
533
552
  stub_comments.posts_commented_by(2)
534
553
  stub_db.sqls.must_equal [
535
- 'SELECT custom_schema.posts.* FROM custom_schema.comments INNER JOIN custom_schema.posts ON (custom_schema.posts.id = custom_schema.comments.post_id) WHERE (comments.user_id = 2)'
554
+ 'SELECT posts.* FROM custom_schema.comments INNER JOIN custom_schema.posts ON (custom_schema.posts.id = custom_schema.comments.post_id) WHERE (comments.user_id = 2)'
536
555
  ]
537
556
  end
538
557
 
@@ -2,6 +2,7 @@ require 'coverage_helper'
2
2
  require 'rasti-db'
3
3
  require 'minitest/autorun'
4
4
  require 'minitest/colorin'
5
+ require 'minitest/line/describe_track'
5
6
  require 'pry-nav'
6
7
  require 'logger'
7
8
  require 'sequel/extensions/pg_hstore'
@@ -40,7 +41,7 @@ class Posts < Rasti::DB::Collection
40
41
  chainable do
41
42
  dataset.join(with_schema(:comments), post_id: :id)
42
43
  .where(with_schema(:comments, :user_id) => user_id)
43
- .select_all(with_schema(:posts))
44
+ .select_all(:posts)
44
45
  .distinct
45
46
  end
46
47
  end
@@ -53,7 +54,7 @@ class Comments < Rasti::DB::Collection
53
54
  def posts_commented_by(user_id)
54
55
  dataset.where(Sequel[:comments][:user_id] => user_id)
55
56
  .join(with_schema(:posts), id: :post_id)
56
- .select_all(with_schema(:posts))
57
+ .select_all(:posts)
57
58
  .map { |row| Post.new row }
58
59
  end
59
60
  end
data/spec/query_spec.rb CHANGED
@@ -3,18 +3,38 @@ require 'minitest_helper'
3
3
  describe 'Query' do
4
4
 
5
5
  before do
6
- 1.upto(10) { |i| db[:users].insert name: "User #{i}" }
6
+ 1.upto(10) do |i|
7
+ db[:users].insert name: "User #{i}"
8
+
9
+ db[:people].insert user_id: i,
10
+ document_number: i,
11
+ first_name: "Name #{i}",
12
+ last_name: "Last Name #{i}",
13
+ birth_date: Time.now
14
+ end
7
15
 
8
16
  db[:posts].insert user_id: 2, title: 'Sample post', body: '...'
9
17
  db[:posts].insert user_id: 1, title: 'Another post', body: '...'
10
18
  db[:posts].insert user_id: 4, title: 'Best post', body: '...'
11
- end
12
19
 
13
- let(:users_query) { Rasti::DB::Query.new Users, db[:users] }
20
+ 1.upto(3) { |i| db[:categories].insert name: "Category #{i}" }
21
+
22
+ db[:comments].insert post_id: 1, user_id: 5, text: 'Comment 1'
23
+ db[:comments].insert post_id: 1, user_id: 7, text: 'Comment 2'
24
+ db[:comments].insert post_id: 2, user_id: 2, text: 'Comment 3'
25
+
26
+ db[:categories_posts].insert post_id: 1, category_id: 1
27
+ db[:categories_posts].insert post_id: 1, category_id: 2
28
+ db[:categories_posts].insert post_id: 2, category_id: 2
29
+ db[:categories_posts].insert post_id: 2, category_id: 3
30
+ db[:categories_posts].insert post_id: 3, category_id: 3
31
+ end
14
32
 
15
- let(:posts_query) { Rasti::DB::Query.new Posts, db[:posts] }
33
+ let(:users_query) { Rasti::DB::Query.new collection_class: Users, dataset: db[:users] }
34
+
35
+ let(:posts_query) { Rasti::DB::Query.new collection_class: Posts, dataset: db[:posts] }
16
36
 
17
- let(:comments_query) { Rasti::DB::Query.new Comments, db[:comments] }
37
+ let(:comments_query) { Rasti::DB::Query.new collection_class: Comments, dataset: db[:comments] }
18
38
 
19
39
  it 'Count' do
20
40
  users_query.count.must_equal 10
@@ -37,6 +57,61 @@ describe 'Query' do
37
57
  users_query.primary_keys.must_equal db[:users].map { |u| u[:id] }
38
58
  end
39
59
 
60
+ it 'Select attributes' do
61
+ posts_query.select_attributes(:id, :user_id).all.must_equal db[:posts].select(:id, :user_id).map { |r| Post.new r }
62
+ end
63
+
64
+ it 'Exclude attributes' do
65
+ posts_query.exclude_attributes(:body).all.must_equal db[:posts].select(:id, :user_id, :title).map { |r| Post.new r }
66
+ end
67
+
68
+ it 'All attributes' do
69
+ posts_query.exclude_attributes(:body).all_attributes.all.must_equal db[:posts].map { |r| Post.new r }
70
+ end
71
+
72
+ it 'Select graph attributes' do
73
+ person = Person.new db[:people].where(document_number: 2).select(:first_name, :last_name, :user_id).first
74
+
75
+ user = User.new db[:users].where(id: 2).select(:id).first.merge(person: person)
76
+
77
+ post = Post.new db[:posts].where(id: 1).first.merge(user: user)
78
+
79
+ posts_query.where(id: 1)
80
+ .graph('user.person')
81
+ .select_graph_attributes(user: [:id], 'user.person' => [:first_name, :last_name, :user_id])
82
+ .all
83
+ .must_equal [post]
84
+ end
85
+
86
+ it 'Exclude graph attributes' do
87
+ person = Person.new db[:people].where(document_number: 2).select(:document_number, :last_name, :user_id).first
88
+
89
+ user = User.new db[:users].where(id: 2).select(:id).first.merge(person: person)
90
+
91
+ post = Post.new db[:posts].where(id: 1).first.merge(user: user)
92
+
93
+ posts_query.where(id: 1)
94
+ .graph('user.person')
95
+ .exclude_graph_attributes(user: [:name], 'user.person' => [:first_name, :birth_date])
96
+ .all
97
+ .must_equal [post]
98
+ end
99
+
100
+ it 'All graph attributes' do
101
+ person = Person.new db[:people].where(document_number: 2).first
102
+
103
+ user = User.new db[:users].where(id: 2).select(:id).first.merge(person: person)
104
+
105
+ post = Post.new db[:posts].where(id: 1).first.merge(user: user)
106
+
107
+ posts_query.where(id: 1)
108
+ .graph('user.person')
109
+ .exclude_graph_attributes(user: [:name], 'user.person' => [:birth_date, :first_name, :last_name])
110
+ .all_graph_attributes('user.person')
111
+ .all
112
+ .must_equal [post]
113
+ end
114
+
40
115
  it 'Map' do
41
116
  users_query.map(&:name).must_equal db[:users].map(:name)
42
117
  end
@@ -96,12 +171,14 @@ describe 'Query' do
96
171
  users_query.graph(:posts).where(id: 1).first.must_equal User.new(id: 1, name: 'User 1', posts: [Post.new(id: 2, user_id: 1, title: 'Another post', body: '...')])
97
172
  end
98
173
 
99
- it 'Empty?' do
174
+ it 'Any?' do
100
175
  users_query.empty?.must_equal false
101
176
  users_query.any?.must_equal true
102
177
  end
103
178
 
104
- it 'Any?' do
179
+ it 'Empty?' do
180
+ db[:comments].truncate
181
+
105
182
  comments_query.empty?.must_equal true
106
183
  comments_query.any?.must_equal false
107
184
  end
@@ -126,28 +203,6 @@ describe 'Query' do
126
203
 
127
204
  describe 'Join' do
128
205
 
129
- before do
130
- 1.upto(10) do |i|
131
- db[:people].insert user_id: i,
132
- document_number: i,
133
- first_name: "Name #{i}",
134
- last_name: "Last Name #{i}",
135
- birth_date: Time.now
136
- end
137
-
138
- 1.upto(3) { |i| db[:categories].insert name: "Category #{i}" }
139
-
140
- db[:comments].insert post_id: 1, user_id: 5, text: 'Comment 1'
141
- db[:comments].insert post_id: 1, user_id: 7, text: 'Comment 2'
142
- db[:comments].insert post_id: 2, user_id: 2, text: 'Comment 3'
143
-
144
- db[:categories_posts].insert post_id: 1, category_id: 1
145
- db[:categories_posts].insert post_id: 1, category_id: 2
146
- db[:categories_posts].insert post_id: 2, category_id: 2
147
- db[:categories_posts].insert post_id: 2, category_id: 3
148
- db[:categories_posts].insert post_id: 3, category_id: 3
149
- end
150
-
151
206
  it 'One to Many' do
152
207
  users_query.join(:posts).where(title: 'Sample post').all.must_equal [User.new(id: 2, name: 'User 2')]
153
208
  end
@@ -179,55 +234,37 @@ describe 'Query' do
179
234
 
180
235
  describe 'NQL' do
181
236
 
182
- before do
183
- 1.upto(10) do |i|
184
- db[:people].insert user_id: i,
185
- document_number: i,
186
- first_name: "Name #{i}",
187
- last_name: "Last Name #{i}",
188
- birth_date: Time.now
189
- end
190
-
191
- 1.upto(3) { |i| db[:categories].insert name: "Category #{i}" }
192
-
193
- db[:comments].insert post_id: 1, user_id: 5, text: 'Comment 1'
194
- db[:comments].insert post_id: 1, user_id: 7, text: 'Comment 2'
195
- db[:comments].insert post_id: 2, user_id: 2, text: 'Comment 3'
196
-
197
- db[:categories_posts].insert post_id: 1, category_id: 1
198
- db[:categories_posts].insert post_id: 1, category_id: 2
199
- db[:categories_posts].insert post_id: 2, category_id: 2
200
- db[:categories_posts].insert post_id: 2, category_id: 3
201
- db[:categories_posts].insert post_id: 3, category_id: 3
202
- end
203
-
204
237
  it 'Invalid expression' do
205
238
  error = proc { posts_query.nql('a + b') }.must_raise Rasti::DB::NQL::InvalidExpressionError
206
239
  error.message.must_equal 'Invalid filter expression: a + b'
207
240
  end
208
241
 
209
242
  it 'Filter to self table' do
210
- people_query = Rasti::DB::Query.new People, db[:people]
211
-
212
- people_query.nql('user_id > 7')
213
- .map(&:user_id)
214
- .sort
215
- .must_equal [8, 9, 10]
243
+ posts_query.nql('user_id > 1')
244
+ .pluck(:user_id)
245
+ .sort
246
+ .must_equal [2, 4]
216
247
  end
217
248
 
218
249
  it 'Filter to join table' do
219
250
  posts_query.nql('categories.name = Category 2')
220
- .map(&:id)
251
+ .pluck(:id)
221
252
  .sort
222
253
  .must_equal [1, 2]
223
254
  end
224
255
 
225
256
  it 'Filter to 2nd order relation' do
226
257
  posts_query.nql('comments.user.person.document_number = 7')
227
- .map(&:id)
258
+ .pluck(:id)
228
259
  .must_equal [1]
229
260
  end
230
261
 
262
+ it 'Filter combined' do
263
+ posts_query.nql('(categories.id = 1 | categories.id = 3) & comments.user.person.document_number = 2')
264
+ .pluck(:id)
265
+ .must_equal [2]
266
+ end
267
+
231
268
  end
232
269
 
233
270
  end
@@ -37,7 +37,7 @@ describe 'Relations' do
37
37
  1.upto(2) { |i| db[:posts].insert user_id: user_id, title: "Post #{i}", body: '...' }
38
38
  rows = db[:users].all
39
39
 
40
- Users.relations[:posts].graph_to rows, db
40
+ Users.relations[:posts].fetch_graph rows, db
41
41
 
42
42
  rows[0][:posts].must_equal posts.where(user_id: user_id).all
43
43
  end
@@ -79,7 +79,7 @@ describe 'Relations' do
79
79
  db[:posts].insert user_id: user_id, title: 'Post 1', body: '...'
80
80
  rows = db[:posts].all
81
81
 
82
- Posts.relations[:user].graph_to rows, db
82
+ Posts.relations[:user].fetch_graph rows, db
83
83
 
84
84
  rows[0][:user].must_equal users.first
85
85
  end
@@ -136,7 +136,7 @@ describe 'Relations' do
136
136
 
137
137
  rows = db[:posts].all
138
138
 
139
- Posts.relations[:categories].graph_to rows, db
139
+ Posts.relations[:categories].fetch_graph rows, db
140
140
 
141
141
  rows[0][:categories].must_equal categories.where(id: [1,2]).all
142
142
  rows[1][:categories].must_equal categories.where(id: [3,4]).all
@@ -186,7 +186,7 @@ describe 'Relations' do
186
186
 
187
187
  rows = db[:users].all
188
188
 
189
- Users.relations[:person].graph_to rows, db
189
+ Users.relations[:person].fetch_graph rows, db
190
190
 
191
191
  2.times do |i|
192
192
  rows[i][:person].must_equal people.find("document_#{i}")
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: 1.4.0
4
+ version: 1.5.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: 2020-01-08 00:00:00.000000000 Z
11
+ date: 2020-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -112,20 +112,48 @@ dependencies:
112
112
  - - "~>"
113
113
  - !ruby/object:Gem::Version
114
114
  version: '1.0'
115
+ - !ruby/object:Gem::Dependency
116
+ name: hierarchical_graph
117
+ requirement: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - "~>"
120
+ - !ruby/object:Gem::Version
121
+ version: '1.0'
122
+ type: :runtime
123
+ prerelease: false
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - "~>"
127
+ - !ruby/object:Gem::Version
128
+ version: '1.0'
129
+ - !ruby/object:Gem::Dependency
130
+ name: hash_ext
131
+ requirement: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - "~>"
134
+ - !ruby/object:Gem::Version
135
+ version: '0.5'
136
+ type: :runtime
137
+ prerelease: false
138
+ version_requirements: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: '0.5'
115
143
  - !ruby/object:Gem::Dependency
116
144
  name: rake
117
145
  requirement: !ruby/object:Gem::Requirement
118
146
  requirements:
119
147
  - - "~>"
120
148
  - !ruby/object:Gem::Version
121
- version: '11.0'
149
+ version: '12.3'
122
150
  type: :development
123
151
  prerelease: false
124
152
  version_requirements: !ruby/object:Gem::Requirement
125
153
  requirements:
126
154
  - - "~>"
127
155
  - !ruby/object:Gem::Version
128
- version: '11.0'
156
+ version: '12.3'
129
157
  - !ruby/object:Gem::Dependency
130
158
  name: minitest
131
159
  requirement: !ruby/object:Gem::Requirement
@@ -279,7 +307,7 @@ files:
279
307
  - lib/rasti/db/nql/syntax.treetop
280
308
  - lib/rasti/db/query.rb
281
309
  - lib/rasti/db/relations/base.rb
282
- - lib/rasti/db/relations/graph_builder.rb
310
+ - lib/rasti/db/relations/graph.rb
283
311
  - lib/rasti/db/relations/many_to_many.rb
284
312
  - lib/rasti/db/relations/many_to_one.rb
285
313
  - lib/rasti/db/relations/one_to_many.rb
@@ -1,60 +0,0 @@
1
- module Rasti
2
- module DB
3
- module Relations
4
- class GraphBuilder
5
- class << self
6
-
7
- def graph_to(rows, relations, collection_class, db, schema=nil)
8
- return if rows.empty?
9
-
10
- parse(relations).each do |relation_name, nested_relations|
11
- relation = get_relation collection_class, relation_name
12
- relation.graph_to rows, db, schema, nested_relations
13
- end
14
- end
15
-
16
- def joins_to(dataset, relations, collection_class, schema=nil)
17
- ds = recursive_joins dataset, recursive_parse(relations), collection_class, schema
18
- qualified_collection_name = schema ? Sequel[schema][collection_class.collection_name] : Sequel[collection_class.collection_name]
19
- ds.distinct.select_all(qualified_collection_name)
20
- end
21
-
22
- private
23
-
24
- def get_relation(collection_class, relation_name)
25
- raise "Undefined relation #{relation_name} for #{collection_class}" unless collection_class.relations.key? relation_name
26
- collection_class.relations[relation_name]
27
- end
28
-
29
- def parse(relations)
30
- relations.each_with_object({}) do |relation, hash|
31
- tail = relation.to_s.split '.'
32
- head = tail.shift.to_sym
33
- hash[head] ||= []
34
- hash[head] << tail.join('.') unless tail.empty?
35
- end
36
- end
37
-
38
- def recursive_parse(relations)
39
- parse(relations).each_with_object({}) do |(key, value), hash|
40
- hash[key] = recursive_parse value
41
- end
42
- end
43
-
44
- def recursive_joins(dataset, joins, collection_class, schema, prefix=nil)
45
- joins.each do |relation_name, nested_joins|
46
- relation = get_relation collection_class, relation_name
47
-
48
- dataset = relation.join_to dataset, schema, prefix
49
-
50
- dataset = recursive_joins dataset, nested_joins, relation.target_collection_class, schema, relation.join_relation_name(prefix) unless nested_joins.empty?
51
- end
52
-
53
- dataset
54
- end
55
-
56
- end
57
- end
58
- end
59
- end
60
- end