rasti-db 1.4.0 → 2.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -3
  3. data/README.md +88 -24
  4. data/lib/rasti/db.rb +2 -1
  5. data/lib/rasti/db/collection.rb +79 -46
  6. data/lib/rasti/db/computed_attribute.rb +22 -0
  7. data/lib/rasti/db/data_source.rb +18 -0
  8. data/lib/rasti/db/environment.rb +32 -0
  9. data/lib/rasti/db/nql/nodes/attribute.rb +37 -0
  10. data/lib/rasti/db/nql/nodes/binary_node.rb +4 -0
  11. data/lib/rasti/db/nql/nodes/comparisons/base.rb +5 -1
  12. data/lib/rasti/db/nql/nodes/comparisons/equal.rb +2 -2
  13. data/lib/rasti/db/nql/nodes/comparisons/greater_than.rb +2 -2
  14. data/lib/rasti/db/nql/nodes/comparisons/greater_than_or_equal.rb +2 -2
  15. data/lib/rasti/db/nql/nodes/comparisons/include.rb +2 -2
  16. data/lib/rasti/db/nql/nodes/comparisons/less_than.rb +2 -2
  17. data/lib/rasti/db/nql/nodes/comparisons/less_than_or_equal.rb +2 -2
  18. data/lib/rasti/db/nql/nodes/comparisons/like.rb +2 -2
  19. data/lib/rasti/db/nql/nodes/comparisons/not_equal.rb +2 -2
  20. data/lib/rasti/db/nql/nodes/comparisons/not_include.rb +2 -2
  21. data/lib/rasti/db/nql/nodes/conjunction.rb +2 -2
  22. data/lib/rasti/db/nql/nodes/disjunction.rb +2 -2
  23. data/lib/rasti/db/nql/nodes/parenthesis_sentence.rb +6 -2
  24. data/lib/rasti/db/nql/nodes/sentence.rb +6 -2
  25. data/lib/rasti/db/nql/syntax.rb +33 -33
  26. data/lib/rasti/db/nql/syntax.treetop +12 -12
  27. data/lib/rasti/db/query.rb +107 -43
  28. data/lib/rasti/db/relations/base.rb +22 -8
  29. data/lib/rasti/db/relations/graph.rb +129 -0
  30. data/lib/rasti/db/relations/many_to_many.rb +58 -24
  31. data/lib/rasti/db/relations/many_to_one.rb +17 -12
  32. data/lib/rasti/db/relations/one_to_many.rb +27 -16
  33. data/lib/rasti/db/version.rb +1 -1
  34. data/rasti-db.gemspec +3 -7
  35. data/spec/collection_spec.rb +223 -52
  36. data/spec/computed_attribute_spec.rb +32 -0
  37. data/spec/minitest_helper.rb +76 -15
  38. data/spec/model_spec.rb +4 -2
  39. data/spec/nql/computed_attributes_spec.rb +29 -0
  40. data/spec/nql/filter_condition_spec.rb +4 -2
  41. data/spec/nql/syntax_parser_spec.rb +12 -5
  42. data/spec/query_spec.rb +319 -85
  43. data/spec/relations_spec.rb +27 -7
  44. metadata +41 -7
  45. data/lib/rasti/db/helpers.rb +0 -16
  46. data/lib/rasti/db/nql/nodes/field.rb +0 -23
  47. data/lib/rasti/db/relations/graph_builder.rb +0 -60
@@ -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,18 @@
1
+ module Rasti
2
+ module DB
3
+ class DataSource
4
+
5
+ attr_reader :db, :schema
6
+
7
+ def initialize(db, schema=nil)
8
+ @db = db
9
+ @schema = schema ? schema.to_sym : nil
10
+ end
11
+
12
+ def qualify(collection_name)
13
+ schema ? Sequel[schema][collection_name] : Sequel[collection_name]
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ module Rasti
2
+ module DB
3
+ class Environment
4
+
5
+ def initialize(data_sources)
6
+ @data_sources = data_sources
7
+ end
8
+
9
+ def data_source(name)
10
+ raise "Undefined data source #{name}" unless data_sources.key? name
11
+ data_sources[name]
12
+ end
13
+
14
+ def data_source_of(collection_class)
15
+ data_source collection_class.data_source_name
16
+ end
17
+
18
+ def qualify(data_source_name, collection_name)
19
+ data_source(data_source_name).qualify collection_name
20
+ end
21
+
22
+ def qualify_collection(collection_class)
23
+ data_source_of(collection_class).qualify collection_class.collection_name
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :data_sources
29
+
30
+ end
31
+ end
32
+ 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