rasti-db 2.0.1 → 2.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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -3
  3. data/lib/rasti/db/collection.rb +10 -1
  4. data/lib/rasti/db/computed_attribute.rb +22 -0
  5. data/lib/rasti/db/nql/nodes/attribute.rb +37 -0
  6. data/lib/rasti/db/nql/nodes/binary_node.rb +4 -0
  7. data/lib/rasti/db/nql/nodes/comparisons/base.rb +5 -1
  8. data/lib/rasti/db/nql/nodes/comparisons/equal.rb +2 -2
  9. data/lib/rasti/db/nql/nodes/comparisons/greater_than.rb +2 -2
  10. data/lib/rasti/db/nql/nodes/comparisons/greater_than_or_equal.rb +2 -2
  11. data/lib/rasti/db/nql/nodes/comparisons/include.rb +2 -2
  12. data/lib/rasti/db/nql/nodes/comparisons/less_than.rb +2 -2
  13. data/lib/rasti/db/nql/nodes/comparisons/less_than_or_equal.rb +2 -2
  14. data/lib/rasti/db/nql/nodes/comparisons/like.rb +2 -2
  15. data/lib/rasti/db/nql/nodes/comparisons/not_equal.rb +2 -2
  16. data/lib/rasti/db/nql/nodes/comparisons/not_include.rb +2 -2
  17. data/lib/rasti/db/nql/nodes/conjunction.rb +2 -2
  18. data/lib/rasti/db/nql/nodes/disjunction.rb +2 -2
  19. data/lib/rasti/db/nql/nodes/parenthesis_sentence.rb +6 -2
  20. data/lib/rasti/db/nql/nodes/sentence.rb +6 -2
  21. data/lib/rasti/db/nql/syntax.rb +33 -33
  22. data/lib/rasti/db/nql/syntax.treetop +12 -12
  23. data/lib/rasti/db/query.rb +41 -7
  24. data/lib/rasti/db/version.rb +1 -1
  25. data/spec/computed_attribute_spec.rb +32 -0
  26. data/spec/minitest_helper.rb +30 -3
  27. data/spec/model_spec.rb +1 -1
  28. data/spec/nql/computed_attributes_spec.rb +29 -0
  29. data/spec/nql/filter_condition_spec.rb +4 -2
  30. data/spec/nql/syntax_parser_spec.rb +12 -5
  31. data/spec/query_spec.rb +146 -34
  32. metadata +8 -3
  33. data/lib/rasti/db/nql/nodes/field.rb +0 -23
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b41f6198798c9bee6da1f94742378639bb3b900f3bda6471f791b42a71a24c7
4
- data.tar.gz: f470e1f1ef5673546e54e6c144e16ebba577b0f848b8fd59c8822489808a7cbf
3
+ metadata.gz: 3166fff22e51aca6cd07d0994d1bf6170438e0bc9aed41e90370a83920a0d9ff
4
+ data.tar.gz: 73e64013ec42740653058f0d9ff6c37ee45de41be0312f2296e4926fb7bff5b4
5
5
  SHA512:
6
- metadata.gz: f5e836d580a42656efe1d78ee2e193449edd1adf45dbccd0a4b4c9c4d6f263a0d92e844a142abb908a683a8dcad4865d5926aec10b5a0260c0240425dcd063dd
7
- data.tar.gz: 71148abafe56a6fe60613f2ebfa38138df2aaa656bdbe7116a834d41f3920fb49b97bfffac3235a520a6ffbb293184310d2d7fcefa552178d1cb767b94ce8e4d
6
+ metadata.gz: 55c6fc7f5b3ef1d904f252e6d54c595b5d4cb72f1fbdf67f3e577c9cc095bd838f0963f34f611f6b7069971f7ead1d96d7dd20864dbcfea8ed542c5a3008dfdc
7
+ data.tar.gz: 3b6c0136ba9823832cf329b3efe78406e69ea92d6d90f89950652628de740ea675a8b7d7152e530838f269b9b32140fa7f80750bcc2b41af70c9efc2b275cd78
data/README.md CHANGED
@@ -93,7 +93,7 @@ User = Rasti::DB::Model[:id, :name, :posts, :comments, :person]
93
93
  Post = Rasti::DB::Model[:id, :title, :body, :user_id, :user, :comments, :categories]
94
94
  Comment = Rasti::DB::Model[:id, :text, :user_id, :user, :post_id, :post]
95
95
  Category = Rasti::DB::Model[:id, :name, :posts]
96
- Person = Rasti::DB::Model[:document_number, :first_name, :last_name, :birth_date, :user_id, :user]
96
+ Person = Rasti::DB::Model[:document_number, :first_name, :last_name, :full_name, :birth_date, :user_id, :user]
97
97
  Language = Rasti::DB::Model[:id, :name, :people]
98
98
  ```
99
99
 
@@ -111,10 +111,10 @@ class Posts < Rasti::DB::Collection
111
111
  many_to_many :categories
112
112
  one_to_many :comments
113
113
 
114
- query :created_by do |user_id|
114
+ query :created_by do |user_id|
115
115
  where user_id: user_id
116
116
  end
117
-
117
+
118
118
  query :entitled, -> (title) { where title: title }
119
119
 
120
120
  query :commented_by do |user_id|
@@ -144,6 +144,10 @@ class People < Rasti::DB::Collection
144
144
 
145
145
  many_to_one :user
146
146
  many_to_many :languages
147
+
148
+ computed_attribute :full_name do
149
+ Rasti::DB::ComputedAttribute.new Sequel.join([:first_name, ' ', :last_name])
150
+ end
147
151
  end
148
152
 
149
153
  class Languages < Rasti::DB::Collection
@@ -210,6 +214,10 @@ posts.all_attributes # => [Post, ...]
210
214
  posts.graph('user.person').select_graph_attributes(user: [:id], 'user.person': [:last_name, :user_id]) # => [Post, ...]
211
215
  posts.graph('user.person').exclude_graph_attributes(user: [:name], 'user.person': [:first_name, :last_name]) # => [Post, ...]
212
216
  posts.graph('user.person').all_graph_attributes('user.person') # => [Post, ...]
217
+
218
+ posts.each { |post| do_something post } # Iterate posts loading all at first
219
+ posts.each(batch_size: 1000) { |post| do_something post } # Iterate posts loading in batches
220
+ posts.each_batch(size: 1000) { |posts| do_something posts } # Iterate batches of posts
213
221
  ```
214
222
  ### Natural Query Language
215
223
 
@@ -14,7 +14,7 @@ module Rasti
14
14
  end
15
15
 
16
16
  def collection_attributes
17
- @collection_attributes ||= model.attributes - relations.keys
17
+ @collection_attributes ||= model.attributes - relations.keys - computed_attributes.keys
18
18
  end
19
19
 
20
20
  def primary_key
@@ -47,6 +47,10 @@ module Rasti
47
47
  @queries ||= Hash::Indifferent.new
48
48
  end
49
49
 
50
+ def computed_attributes
51
+ @computed_attributes ||= Hash::Indifferent.new
52
+ end
53
+
50
54
  private
51
55
 
52
56
  def set_collection_name(collection_name)
@@ -91,6 +95,11 @@ module Rasti
91
95
  end
92
96
  end
93
97
 
98
+ def computed_attribute(name, &block)
99
+ raise "Computed Attribute #{name} already exists" if computed_attributes.key? name
100
+ computed_attributes[name] = block.call
101
+ end
102
+
94
103
  end
95
104
 
96
105
  def initialize(environment)
@@ -0,0 +1,22 @@
1
+ module Rasti
2
+ module DB
3
+ class ComputedAttribute
4
+
5
+ attr_reader :identifier
6
+
7
+ def initialize(identifier, &join)
8
+ @identifier = identifier
9
+ @join = join
10
+ end
11
+
12
+ def apply_join(dataset)
13
+ join ? join.call(dataset) : dataset
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :join
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ module Rasti
2
+ module DB
3
+ module NQL
4
+ module Nodes
5
+ class Attribute < Treetop::Runtime::SyntaxNode
6
+
7
+ def identifier(collection_class)
8
+ if computed? collection_class
9
+ collection_class.computed_attributes[column].identifier
10
+ else
11
+ tables.empty? ? Sequel[column] : Sequel[tables.join('__').to_sym][column]
12
+ end
13
+ end
14
+
15
+ def tables
16
+ _tables.elements.map{ |e| e.table.text_value }
17
+ end
18
+
19
+ def column
20
+ _column.text_value.to_sym
21
+ end
22
+
23
+ def computed_attributes(collection_class)
24
+ computed?(collection_class) ? [column] : []
25
+ end
26
+
27
+ private
28
+
29
+ def computed?(collection_class)
30
+ collection_class.computed_attributes.key? column
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -8,6 +8,10 @@ module Rasti
8
8
  values.flat_map(&:dependency_tables)
9
9
  end
10
10
 
11
+ def computed_attributes(collection_class)
12
+ left.computed_attributes(collection_class) | right.computed_attributes(collection_class)
13
+ end
14
+
11
15
  def values
12
16
  @values ||= values_for(left) + values_for(right)
13
17
  end
@@ -6,7 +6,11 @@ module Rasti
6
6
  class Base < Treetop::Runtime::SyntaxNode
7
7
 
8
8
  def dependency_tables
9
- field.tables.empty? ? [] : [field.tables.join('.')]
9
+ attribute.tables.empty? ? [] : [attribute.tables.join('.')]
10
+ end
11
+
12
+ def computed_attributes(collection_class)
13
+ attribute.computed_attributes(collection_class)
10
14
  end
11
15
 
12
16
  end
@@ -5,8 +5,8 @@ module Rasti
5
5
  module Comparisons
6
6
  class Equal < Base
7
7
 
8
- def filter_condition
9
- { field.identifier => argument.value }
8
+ def filter_condition(collection_class)
9
+ { attribute.identifier(collection_class) => argument.value }
10
10
  end
11
11
 
12
12
  end
@@ -5,8 +5,8 @@ module Rasti
5
5
  module Comparisons
6
6
  class GreaterThan < Base
7
7
 
8
- def filter_condition
9
- field.identifier > argument.value
8
+ def filter_condition(collection_class)
9
+ attribute.identifier(collection_class) > argument.value
10
10
  end
11
11
 
12
12
  end
@@ -5,8 +5,8 @@ module Rasti
5
5
  module Comparisons
6
6
  class GreaterThanOrEqual < Base
7
7
 
8
- def filter_condition
9
- field.identifier >= argument.value
8
+ def filter_condition(collection_class)
9
+ attribute.identifier(collection_class) >= argument.value
10
10
  end
11
11
 
12
12
  end
@@ -5,8 +5,8 @@ module Rasti
5
5
  module Comparisons
6
6
  class Include < Base
7
7
 
8
- def filter_condition
9
- Sequel.ilike(field.identifier, "%#{argument.value}%")
8
+ def filter_condition(collection_class)
9
+ Sequel.ilike(attribute.identifier(collection_class), "%#{argument.value}%")
10
10
  end
11
11
 
12
12
  end
@@ -5,8 +5,8 @@ module Rasti
5
5
  module Comparisons
6
6
  class LessThan < Base
7
7
 
8
- def filter_condition
9
- field.identifier < argument.value
8
+ def filter_condition(collection_class)
9
+ attribute.identifier(collection_class) < argument.value
10
10
  end
11
11
 
12
12
  end
@@ -5,8 +5,8 @@ module Rasti
5
5
  module Comparisons
6
6
  class LessThanOrEqual < Base
7
7
 
8
- def filter_condition
9
- field.identifier <= argument.value
8
+ def filter_condition(collection_class)
9
+ attribute.identifier(collection_class) <= argument.value
10
10
  end
11
11
 
12
12
  end
@@ -5,8 +5,8 @@ module Rasti
5
5
  module Comparisons
6
6
  class Like < Base
7
7
 
8
- def filter_condition
9
- Sequel.ilike(field.identifier, argument.value)
8
+ def filter_condition(collection_class)
9
+ Sequel.ilike(attribute.identifier(collection_class), argument.value)
10
10
  end
11
11
 
12
12
  end
@@ -5,8 +5,8 @@ module Rasti
5
5
  module Comparisons
6
6
  class NotEqual < Base
7
7
 
8
- def filter_condition
9
- Sequel.negate(field.identifier => argument.value)
8
+ def filter_condition(collection_class)
9
+ Sequel.negate(attribute.identifier(collection_class) => argument.value)
10
10
  end
11
11
 
12
12
  end
@@ -5,8 +5,8 @@ module Rasti
5
5
  module Comparisons
6
6
  class NotInclude < Base
7
7
 
8
- def filter_condition
9
- ~ Sequel.ilike(field.identifier, "%#{argument.value}%")
8
+ def filter_condition(collection_class)
9
+ ~ Sequel.ilike(attribute.identifier(collection_class), "%#{argument.value}%")
10
10
  end
11
11
 
12
12
  end
@@ -4,8 +4,8 @@ module Rasti
4
4
  module Nodes
5
5
  class Conjunction < BinaryNode
6
6
 
7
- def filter_condition
8
- Sequel.&(*values.map(&:filter_condition))
7
+ def filter_condition(collection_class)
8
+ Sequel.&(*values.map { |value| value.filter_condition(collection_class) } )
9
9
  end
10
10
 
11
11
  end
@@ -4,8 +4,8 @@ module Rasti
4
4
  module Nodes
5
5
  class Disjunction < BinaryNode
6
6
 
7
- def filter_condition
8
- Sequel.|(*values.map(&:filter_condition))
7
+ def filter_condition(collection_class)
8
+ Sequel.|(*values.map { |value| value.filter_condition(collection_class) } )
9
9
  end
10
10
 
11
11
  end
@@ -8,8 +8,12 @@ module Rasti
8
8
  sentence.dependency_tables
9
9
  end
10
10
 
11
- def filter_condition
12
- sentence.filter_condition
11
+ def computed_attributes(collection_class)
12
+ sentence.computed_attributes(collection_class)
13
+ end
14
+
15
+ def filter_condition(collection_class)
16
+ sentence.filter_condition collection_class
13
17
  end
14
18
 
15
19
  end
@@ -8,8 +8,12 @@ module Rasti
8
8
  proposition.dependency_tables
9
9
  end
10
10
 
11
- def filter_condition
12
- proposition.filter_condition
11
+ def computed_attributes(collection_class)
12
+ proposition.computed_attributes collection_class
13
+ end
14
+
15
+ def filter_condition(collection_class)
16
+ proposition.filter_condition collection_class
13
17
  end
14
18
 
15
19
  end
@@ -433,14 +433,14 @@ module Rasti
433
433
  r0
434
434
  end
435
435
 
436
- module Field0
436
+ module Attribute0
437
437
  def table
438
438
  elements[0]
439
439
  end
440
440
 
441
441
  end
442
442
 
443
- module Field1
443
+ module Attribute1
444
444
  def _tables
445
445
  elements[0]
446
446
  end
@@ -450,10 +450,10 @@ module Rasti
450
450
  end
451
451
  end
452
452
 
453
- def _nt_field
453
+ def _nt_attribute
454
454
  start_index = index
455
- if node_cache[:field].has_key?(index)
456
- cached = node_cache[:field][index]
455
+ if node_cache[:attribute].has_key?(index)
456
+ cached = node_cache[:attribute][index]
457
457
  if cached
458
458
  cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
459
459
  @index = cached.interval.end
@@ -465,7 +465,7 @@ module Rasti
465
465
  s1, i1 = [], index
466
466
  loop do
467
467
  i2, s2 = index, []
468
- r3 = _nt_field_name
468
+ r3 = _nt_attribute_name
469
469
  s2 << r3
470
470
  if r3
471
471
  if has_terminal?('.', false, index)
@@ -479,7 +479,7 @@ module Rasti
479
479
  end
480
480
  if s2.last
481
481
  r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
482
- r2.extend(Field0)
482
+ r2.extend(Attribute0)
483
483
  else
484
484
  @index = i2
485
485
  r2 = nil
@@ -493,24 +493,24 @@ module Rasti
493
493
  r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
494
494
  s0 << r1
495
495
  if r1
496
- r5 = _nt_field_name
496
+ r5 = _nt_attribute_name
497
497
  s0 << r5
498
498
  end
499
499
  if s0.last
500
- r0 = instantiate_node(Nodes::Field,input, i0...index, s0)
501
- r0.extend(Field1)
500
+ r0 = instantiate_node(Nodes::Attribute,input, i0...index, s0)
501
+ r0.extend(Attribute1)
502
502
  else
503
503
  @index = i0
504
504
  r0 = nil
505
505
  end
506
506
 
507
- node_cache[:field][start_index] = r0
507
+ node_cache[:attribute][start_index] = r0
508
508
 
509
509
  r0
510
510
  end
511
511
 
512
512
  module ComparisonInclude0
513
- def field
513
+ def attribute
514
514
  elements[0]
515
515
  end
516
516
 
@@ -535,7 +535,7 @@ module Rasti
535
535
  end
536
536
 
537
537
  i0, s0 = index, []
538
- r1 = _nt_field
538
+ r1 = _nt_attribute
539
539
  s0 << r1
540
540
  if r1
541
541
  s2, i2 = [], index
@@ -591,7 +591,7 @@ module Rasti
591
591
  end
592
592
 
593
593
  module ComparisonNotInclude0
594
- def field
594
+ def attribute
595
595
  elements[0]
596
596
  end
597
597
 
@@ -616,7 +616,7 @@ module Rasti
616
616
  end
617
617
 
618
618
  i0, s0 = index, []
619
- r1 = _nt_field
619
+ r1 = _nt_attribute
620
620
  s0 << r1
621
621
  if r1
622
622
  s2, i2 = [], index
@@ -672,7 +672,7 @@ module Rasti
672
672
  end
673
673
 
674
674
  module ComparisonLike0
675
- def field
675
+ def attribute
676
676
  elements[0]
677
677
  end
678
678
 
@@ -697,7 +697,7 @@ module Rasti
697
697
  end
698
698
 
699
699
  i0, s0 = index, []
700
- r1 = _nt_field
700
+ r1 = _nt_attribute
701
701
  s0 << r1
702
702
  if r1
703
703
  s2, i2 = [], index
@@ -753,7 +753,7 @@ module Rasti
753
753
  end
754
754
 
755
755
  module ComparisonGreaterThan0
756
- def field
756
+ def attribute
757
757
  elements[0]
758
758
  end
759
759
 
@@ -778,7 +778,7 @@ module Rasti
778
778
  end
779
779
 
780
780
  i0, s0 = index, []
781
- r1 = _nt_field
781
+ r1 = _nt_attribute
782
782
  s0 << r1
783
783
  if r1
784
784
  s2, i2 = [], index
@@ -834,7 +834,7 @@ module Rasti
834
834
  end
835
835
 
836
836
  module ComparisonGreaterThanOrEqual0
837
- def field
837
+ def attribute
838
838
  elements[0]
839
839
  end
840
840
 
@@ -859,7 +859,7 @@ module Rasti
859
859
  end
860
860
 
861
861
  i0, s0 = index, []
862
- r1 = _nt_field
862
+ r1 = _nt_attribute
863
863
  s0 << r1
864
864
  if r1
865
865
  s2, i2 = [], index
@@ -915,7 +915,7 @@ module Rasti
915
915
  end
916
916
 
917
917
  module ComparisonLessThan0
918
- def field
918
+ def attribute
919
919
  elements[0]
920
920
  end
921
921
 
@@ -940,7 +940,7 @@ module Rasti
940
940
  end
941
941
 
942
942
  i0, s0 = index, []
943
- r1 = _nt_field
943
+ r1 = _nt_attribute
944
944
  s0 << r1
945
945
  if r1
946
946
  s2, i2 = [], index
@@ -996,7 +996,7 @@ module Rasti
996
996
  end
997
997
 
998
998
  module ComparisonLessThanOrEqual0
999
- def field
999
+ def attribute
1000
1000
  elements[0]
1001
1001
  end
1002
1002
 
@@ -1021,7 +1021,7 @@ module Rasti
1021
1021
  end
1022
1022
 
1023
1023
  i0, s0 = index, []
1024
- r1 = _nt_field
1024
+ r1 = _nt_attribute
1025
1025
  s0 << r1
1026
1026
  if r1
1027
1027
  s2, i2 = [], index
@@ -1077,7 +1077,7 @@ module Rasti
1077
1077
  end
1078
1078
 
1079
1079
  module ComparisonNotEqual0
1080
- def field
1080
+ def attribute
1081
1081
  elements[0]
1082
1082
  end
1083
1083
 
@@ -1102,7 +1102,7 @@ module Rasti
1102
1102
  end
1103
1103
 
1104
1104
  i0, s0 = index, []
1105
- r1 = _nt_field
1105
+ r1 = _nt_attribute
1106
1106
  s0 << r1
1107
1107
  if r1
1108
1108
  s2, i2 = [], index
@@ -1158,7 +1158,7 @@ module Rasti
1158
1158
  end
1159
1159
 
1160
1160
  module ComparisonEqual0
1161
- def field
1161
+ def attribute
1162
1162
  elements[0]
1163
1163
  end
1164
1164
 
@@ -1183,7 +1183,7 @@ module Rasti
1183
1183
  end
1184
1184
 
1185
1185
  i0, s0 = index, []
1186
- r1 = _nt_field
1186
+ r1 = _nt_attribute
1187
1187
  s0 << r1
1188
1188
  if r1
1189
1189
  s2, i2 = [], index
@@ -1311,10 +1311,10 @@ module Rasti
1311
1311
  r0
1312
1312
  end
1313
1313
 
1314
- def _nt_field_name
1314
+ def _nt_attribute_name
1315
1315
  start_index = index
1316
- if node_cache[:field_name].has_key?(index)
1317
- cached = node_cache[:field_name][index]
1316
+ if node_cache[:attribute_name].has_key?(index)
1317
+ cached = node_cache[:attribute_name][index]
1318
1318
  if cached
1319
1319
  cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
1320
1320
  @index = cached.interval.end
@@ -1343,7 +1343,7 @@ module Rasti
1343
1343
  r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
1344
1344
  end
1345
1345
 
1346
- node_cache[:field_name][start_index] = r0
1346
+ node_cache[:attribute_name][start_index] = r0
1347
1347
 
1348
1348
  r0
1349
1349
  end