rasti-db 2.0.0 → 2.3.1
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/README.md +11 -3
- data/lib/rasti/db.rb +11 -1
- data/lib/rasti/db/collection.rb +10 -1
- data/lib/rasti/db/computed_attribute.rb +22 -0
- data/lib/rasti/db/nql/filter_condition_strategies/base.rb +17 -0
- data/lib/rasti/db/nql/filter_condition_strategies/postgres.rb +21 -0
- data/lib/rasti/db/nql/filter_condition_strategies/sqlite.rb +21 -0
- data/lib/rasti/db/nql/filter_condition_strategies/types/generic.rb +49 -0
- data/lib/rasti/db/nql/filter_condition_strategies/types/pg_array.rb +32 -0
- data/lib/rasti/db/nql/filter_condition_strategies/types/sqlite_array.rb +34 -0
- data/lib/rasti/db/nql/filter_condition_strategies/unsupported_type_comparison.rb +22 -0
- data/lib/rasti/db/nql/nodes/array_content.rb +21 -0
- data/lib/rasti/db/nql/nodes/attribute.rb +37 -0
- data/lib/rasti/db/nql/nodes/binary_node.rb +4 -0
- data/lib/rasti/db/nql/nodes/comparisons/base.rb +15 -1
- data/lib/rasti/db/nql/nodes/comparisons/equal.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/greater_than.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/greater_than_or_equal.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/include.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/less_than.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/less_than_or_equal.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/like.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/not_equal.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/not_include.rb +0 -4
- data/lib/rasti/db/nql/nodes/conjunction.rb +2 -2
- data/lib/rasti/db/nql/nodes/constants/array.rb +17 -0
- data/lib/rasti/db/nql/nodes/constants/base.rb +17 -0
- data/lib/rasti/db/nql/nodes/constants/false.rb +1 -1
- data/lib/rasti/db/nql/nodes/constants/float.rb +1 -1
- data/lib/rasti/db/nql/nodes/constants/integer.rb +1 -1
- data/lib/rasti/db/nql/nodes/constants/literal_string.rb +1 -1
- data/lib/rasti/db/nql/nodes/constants/string.rb +1 -1
- data/lib/rasti/db/nql/nodes/constants/time.rb +1 -1
- data/lib/rasti/db/nql/nodes/constants/true.rb +1 -1
- data/lib/rasti/db/nql/nodes/disjunction.rb +2 -2
- data/lib/rasti/db/nql/nodes/parenthesis_sentence.rb +6 -2
- data/lib/rasti/db/nql/nodes/sentence.rb +6 -2
- data/lib/rasti/db/nql/syntax.rb +262 -44
- data/lib/rasti/db/nql/syntax.treetop +27 -14
- data/lib/rasti/db/query.rb +42 -14
- data/lib/rasti/db/type_converters/postgres.rb +32 -36
- data/lib/rasti/db/type_converters/postgres_types/array.rb +11 -9
- data/lib/rasti/db/type_converters/postgres_types/hstore.rb +10 -9
- data/lib/rasti/db/type_converters/postgres_types/json.rb +17 -14
- data/lib/rasti/db/type_converters/postgres_types/jsonb.rb +17 -14
- data/lib/rasti/db/type_converters/sqlite.rb +62 -0
- data/lib/rasti/db/type_converters/sqlite_types/array.rb +34 -0
- data/lib/rasti/db/version.rb +1 -1
- data/rasti-db.gemspec +1 -0
- data/spec/collection_spec.rb +19 -11
- data/spec/computed_attribute_spec.rb +32 -0
- data/spec/minitest_helper.rb +32 -5
- data/spec/model_spec.rb +1 -1
- data/spec/nql/computed_attributes_spec.rb +29 -0
- data/spec/nql/filter_condition_spec.rb +23 -4
- data/spec/nql/filter_condition_strategies_spec.rb +112 -0
- data/spec/nql/syntax_parser_spec.rb +36 -5
- data/spec/query_spec.rb +254 -39
- data/spec/type_converters/sqlite_spec.rb +66 -0
- metadata +38 -3
- data/lib/rasti/db/nql/nodes/field.rb +0 -23
@@ -42,44 +42,49 @@ module Rasti
|
|
42
42
|
comparison_equal
|
43
43
|
end
|
44
44
|
|
45
|
-
rule
|
46
|
-
_tables:(table:
|
45
|
+
rule attribute
|
46
|
+
_tables:(table:attribute_name '.')* _column:attribute_name <Nodes::Attribute>
|
47
47
|
end
|
48
48
|
|
49
49
|
rule comparison_include
|
50
|
-
|
50
|
+
attribute:attribute space* comparator:':' space* argument:argument <Nodes::Comparisons::Include>
|
51
51
|
end
|
52
52
|
|
53
53
|
rule comparison_not_include
|
54
|
-
|
54
|
+
attribute:attribute space* comparator:'!:' space* argument:argument <Nodes::Comparisons::NotInclude>
|
55
55
|
end
|
56
56
|
|
57
57
|
rule comparison_like
|
58
|
-
|
58
|
+
attribute:attribute space* comparator:'~' space* argument:argument <Nodes::Comparisons::Like>
|
59
59
|
end
|
60
60
|
|
61
61
|
rule comparison_greater_than
|
62
|
-
|
62
|
+
attribute:attribute space* comparator:'>' space* argument:argument <Nodes::Comparisons::GreaterThan>
|
63
63
|
end
|
64
64
|
|
65
65
|
rule comparison_greater_than_or_equal
|
66
|
-
|
66
|
+
attribute:attribute space* comparator:'>=' space* argument:argument <Nodes::Comparisons::GreaterThanOrEqual>
|
67
67
|
end
|
68
68
|
|
69
69
|
rule comparison_less_than
|
70
|
-
|
70
|
+
attribute:attribute space* comparator:'<' space* argument:argument <Nodes::Comparisons::LessThan>
|
71
71
|
end
|
72
72
|
|
73
73
|
rule comparison_less_than_or_equal
|
74
|
-
|
74
|
+
attribute:attribute space* comparator:'<=' space* argument:argument <Nodes::Comparisons::LessThanOrEqual>
|
75
75
|
end
|
76
76
|
|
77
77
|
rule comparison_not_equal
|
78
|
-
|
78
|
+
attribute:attribute space* comparator:'!=' space* argument:argument <Nodes::Comparisons::NotEqual>
|
79
79
|
end
|
80
80
|
|
81
81
|
rule comparison_equal
|
82
|
-
|
82
|
+
attribute:attribute space* comparator:'=' space* argument:argument <Nodes::Comparisons::Equal>
|
83
|
+
end
|
84
|
+
|
85
|
+
rule argument
|
86
|
+
array /
|
87
|
+
basic
|
83
88
|
end
|
84
89
|
|
85
90
|
rule basic
|
@@ -91,11 +96,19 @@ module Rasti
|
|
91
96
|
string
|
92
97
|
end
|
93
98
|
|
99
|
+
rule array
|
100
|
+
open:'[' space* contents:(array_content / basic) space* close:']' <Nodes::Constants::Array>
|
101
|
+
end
|
102
|
+
|
103
|
+
rule array_content
|
104
|
+
left:basic space* ',' space* right:(array_content / basic) <Nodes::ArrayContent>
|
105
|
+
end
|
106
|
+
|
94
107
|
rule space
|
95
108
|
[\s\t\n]
|
96
109
|
end
|
97
110
|
|
98
|
-
rule
|
111
|
+
rule attribute_name
|
99
112
|
[a-z_]+
|
100
113
|
end
|
101
114
|
|
@@ -130,7 +143,7 @@ module Rasti
|
|
130
143
|
end
|
131
144
|
|
132
145
|
rule valid_character
|
133
|
-
[0-9a-zA-Z
|
146
|
+
[0-9a-zA-ZÁÀÄÂÃÅĀĂǍáàäâãåāăǎÉÈËÊĒĔĖĚéèëêēĕėěÍÌÏÎĨĬǏíìïîĩĭǐÓÒÖÔÕŌŎŐǑóòöôõōŏőǒÚÙÜÛŨŪŬŮŰǓúùüûũūŭůűǔÑñçÇ%@#+\--Z\\^_'?!$*/\s]
|
134
147
|
end
|
135
148
|
|
136
149
|
rule boolean
|
@@ -159,7 +172,7 @@ module Rasti
|
|
159
172
|
end
|
160
173
|
|
161
174
|
rule reserved_character
|
162
|
-
[&|.()
|
175
|
+
[&|.():!=<>~,\]\[]
|
163
176
|
end
|
164
177
|
|
165
178
|
end
|
data/lib/rasti/db/query.rb
CHANGED
@@ -9,7 +9,7 @@ module Rasti
|
|
9
9
|
def initialize(environment:, collection_class:, dataset:, relations_graph:nil)
|
10
10
|
@environment = environment
|
11
11
|
@collection_class = collection_class
|
12
|
-
@dataset = dataset
|
12
|
+
@dataset = dataset.qualify collection_class.collection_name
|
13
13
|
@relations_graph = relations_graph || Relations::Graph.new(environment, collection_class)
|
14
14
|
end
|
15
15
|
|
@@ -57,15 +57,36 @@ module Rasti
|
|
57
57
|
build_query relations_graph: relations_graph.with_all_attributes_for(relations)
|
58
58
|
end
|
59
59
|
|
60
|
+
def select_computed_attributes(*computed_attributes)
|
61
|
+
ds = computed_attributes.inject(dataset) do |ds, name|
|
62
|
+
computed_attribute = collection_class.computed_attributes[name]
|
63
|
+
computed_attribute.apply_join(ds).select_append(computed_attribute.identifier.as(name))
|
64
|
+
end
|
65
|
+
build_query dataset: ds
|
66
|
+
end
|
67
|
+
|
60
68
|
def all
|
61
|
-
with_graph(dataset.all).map do |row|
|
69
|
+
with_graph(dataset.all).map do |row|
|
62
70
|
collection_class.model.new row
|
63
71
|
end
|
64
72
|
end
|
65
73
|
alias_method :to_a, :all
|
66
74
|
|
67
|
-
def each(&block)
|
68
|
-
|
75
|
+
def each(batch_size:nil, &block)
|
76
|
+
if batch_size.nil?
|
77
|
+
all.each(&block)
|
78
|
+
else
|
79
|
+
each_batch(size: batch_size) do |models|
|
80
|
+
models.each { |model| block.call model }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def each_batch(size:, &block)
|
86
|
+
primary_keys.each_slice(size) do |pks|
|
87
|
+
query = where(collection_class.primary_key => pks)
|
88
|
+
block.call query.all
|
89
|
+
end
|
69
90
|
end
|
70
91
|
|
71
92
|
def graph(*relations)
|
@@ -74,11 +95,9 @@ module Rasti
|
|
74
95
|
|
75
96
|
def join(*relations)
|
76
97
|
graph = Relations::Graph.new environment, collection_class, relations
|
77
|
-
|
78
|
-
ds = graph.add_joins(dataset)
|
79
|
-
|
80
|
-
.select_all(collection_class.collection_name)
|
81
|
-
|
98
|
+
|
99
|
+
ds = graph.add_joins(dataset).distinct
|
100
|
+
|
82
101
|
build_query dataset: ds
|
83
102
|
end
|
84
103
|
|
@@ -96,12 +115,12 @@ module Rasti
|
|
96
115
|
|
97
116
|
def first
|
98
117
|
row = dataset.first
|
99
|
-
row ?
|
118
|
+
row ? build_model(row) : nil
|
100
119
|
end
|
101
120
|
|
102
121
|
def last
|
103
122
|
row = dataset.last
|
104
|
-
row ?
|
123
|
+
row ? build_model(row) : nil
|
105
124
|
end
|
106
125
|
|
107
126
|
def detect(*args, &block)
|
@@ -118,10 +137,15 @@ module Rasti
|
|
118
137
|
|
119
138
|
raise NQL::InvalidExpressionError.new(filter_expression) if sentence.nil?
|
120
139
|
|
140
|
+
ds = sentence.computed_attributes(collection_class).inject(dataset) do |ds, name|
|
141
|
+
collection_class.computed_attributes[name].apply_join ds
|
142
|
+
end
|
143
|
+
query = build_query dataset: ds
|
144
|
+
|
121
145
|
dependency_tables = sentence.dependency_tables
|
122
|
-
query =
|
123
|
-
|
124
|
-
query.where sentence.filter_condition
|
146
|
+
query = query.join(*dependency_tables) unless dependency_tables.empty?
|
147
|
+
|
148
|
+
query.where sentence.filter_condition(collection_class)
|
125
149
|
end
|
126
150
|
|
127
151
|
private
|
@@ -139,6 +163,10 @@ module Rasti
|
|
139
163
|
Query.new(**current_args.merge(args))
|
140
164
|
end
|
141
165
|
|
166
|
+
def build_model(row)
|
167
|
+
collection_class.model.new with_graph(row)
|
168
|
+
end
|
169
|
+
|
142
170
|
def chainable(&block)
|
143
171
|
build_query dataset: instance_eval(&block)
|
144
172
|
end
|
@@ -3,62 +3,58 @@ module Rasti
|
|
3
3
|
module TypeConverters
|
4
4
|
class Postgres
|
5
5
|
|
6
|
-
CONVERTERS = [
|
7
|
-
|
8
|
-
|
6
|
+
CONVERTERS = [
|
7
|
+
PostgresTypes::JSON,
|
8
|
+
PostgresTypes::JSONB,
|
9
|
+
PostgresTypes::HStore,
|
10
|
+
PostgresTypes::Array
|
11
|
+
]
|
9
12
|
|
10
13
|
class << self
|
11
14
|
|
12
15
|
def to_db(db, collection_name, attribute_name, value)
|
13
|
-
|
14
|
-
|
15
|
-
if to_db_mapping.key? attribute_name
|
16
|
-
to_db_mapping[attribute_name][:converter].to_db value, to_db_mapping[attribute_name][:sub_type]
|
17
|
-
else
|
18
|
-
value
|
19
|
-
end
|
16
|
+
converter, type = find_to_db_converter_and_type db, collection_name, attribute_name
|
17
|
+
converter ? converter.to_db(value, type) : value
|
20
18
|
end
|
21
19
|
|
22
|
-
def from_db(
|
23
|
-
|
24
|
-
|
25
|
-
else
|
26
|
-
object
|
27
|
-
end
|
20
|
+
def from_db(value)
|
21
|
+
converter = find_from_db_converter value.class
|
22
|
+
converter ? converter.from_db(value) : value
|
28
23
|
end
|
29
24
|
|
30
25
|
private
|
31
26
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
@to_db_mapping[key] ||= begin
|
36
|
-
columns = Hash[db.schema(collection_name)]
|
27
|
+
def from_db_converters
|
28
|
+
@from_db_converters ||= {}
|
29
|
+
end
|
37
30
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
31
|
+
def to_db_converters
|
32
|
+
@to_db_converters ||= {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def find_to_db_converter_and_type(db, collection_name, attribute_name)
|
36
|
+
key = [db.opts[:database], collection_name].join('.')
|
42
37
|
|
43
|
-
|
44
|
-
|
45
|
-
|
38
|
+
to_db_converters[key] ||= begin
|
39
|
+
columns = Hash[db.schema(collection_name)]
|
40
|
+
to_db_converters[key] = columns.each_with_object({}) do |(name, schema), hash|
|
41
|
+
converter = CONVERTERS.detect { |c| c.to_db? schema[:db_type] }
|
42
|
+
hash[name] = [converter, schema[:db_type]] if converter
|
46
43
|
end
|
47
44
|
end
|
45
|
+
|
46
|
+
to_db_converters[key].fetch(attribute_name, [])
|
48
47
|
end
|
49
48
|
|
50
|
-
def
|
51
|
-
|
52
|
-
CONVERTERS.
|
53
|
-
converter.db_classes.each do |db_class|
|
54
|
-
result[db_class] = converter
|
55
|
-
end
|
56
|
-
end
|
49
|
+
def find_from_db_converter(klass)
|
50
|
+
if !from_db_converters.key?(klass)
|
51
|
+
from_db_converters[klass] = CONVERTERS.detect { |c| c.from_db? klass }
|
57
52
|
end
|
53
|
+
|
54
|
+
from_db_converters[klass]
|
58
55
|
end
|
59
56
|
|
60
57
|
end
|
61
|
-
|
62
58
|
end
|
63
59
|
end
|
64
60
|
end
|
@@ -3,28 +3,30 @@ module Rasti
|
|
3
3
|
module TypeConverters
|
4
4
|
module PostgresTypes
|
5
5
|
class Array
|
6
|
-
|
7
6
|
class << self
|
8
7
|
|
9
|
-
|
10
|
-
|
8
|
+
DB_TYPE_REGEX = /^([a-z]+)\[\]$/
|
9
|
+
|
10
|
+
def to_db?(type)
|
11
|
+
!type.match(DB_TYPE_REGEX).nil?
|
11
12
|
end
|
12
13
|
|
13
|
-
def to_db(value,
|
14
|
+
def to_db(value, type)
|
15
|
+
sub_type = type[0..-3]
|
14
16
|
array = sub_type == 'hstore' ? value.map { |v| Sequel.hstore v } : value
|
15
17
|
Sequel.pg_array array, sub_type
|
16
18
|
end
|
17
19
|
|
18
|
-
def
|
19
|
-
|
20
|
+
def from_db?(klass)
|
21
|
+
defined?(Sequel::Postgres::PGArray) &&
|
22
|
+
klass == Sequel::Postgres::PGArray
|
20
23
|
end
|
21
24
|
|
22
|
-
def from_db(
|
23
|
-
|
25
|
+
def from_db(value)
|
26
|
+
value.to_a
|
24
27
|
end
|
25
28
|
|
26
29
|
end
|
27
|
-
|
28
30
|
end
|
29
31
|
end
|
30
32
|
end
|
@@ -3,27 +3,28 @@ module Rasti
|
|
3
3
|
module TypeConverters
|
4
4
|
module PostgresTypes
|
5
5
|
class HStore
|
6
|
-
|
7
6
|
class << self
|
8
7
|
|
9
|
-
|
10
|
-
|
8
|
+
DB_TYPE_REGEX = /^hstore$/
|
9
|
+
|
10
|
+
def to_db?(type)
|
11
|
+
!type.match(DB_TYPE_REGEX).nil?
|
11
12
|
end
|
12
13
|
|
13
|
-
def to_db(value,
|
14
|
+
def to_db(value, type)
|
14
15
|
Sequel.hstore value
|
15
16
|
end
|
16
17
|
|
17
|
-
def
|
18
|
-
|
18
|
+
def from_db?(klass)
|
19
|
+
defined?(Sequel::Postgres::HStore) &&
|
20
|
+
klass == Sequel::Postgres::HStore
|
19
21
|
end
|
20
22
|
|
21
|
-
def from_db(
|
22
|
-
|
23
|
+
def from_db(value)
|
24
|
+
value.to_h
|
23
25
|
end
|
24
26
|
|
25
27
|
end
|
26
|
-
|
27
28
|
end
|
28
29
|
end
|
29
30
|
end
|
@@ -3,36 +3,39 @@ module Rasti
|
|
3
3
|
module TypeConverters
|
4
4
|
module PostgresTypes
|
5
5
|
class JSON
|
6
|
-
|
7
6
|
class << self
|
8
7
|
|
9
|
-
|
10
|
-
|
8
|
+
DB_TYPE_REGEX = /^json$/
|
9
|
+
|
10
|
+
def to_db?(type)
|
11
|
+
!type.match(DB_TYPE_REGEX).nil?
|
11
12
|
end
|
12
13
|
|
13
|
-
def to_db(value,
|
14
|
+
def to_db(value, type)
|
14
15
|
Sequel.pg_json value
|
15
16
|
end
|
16
17
|
|
17
|
-
def
|
18
|
-
|
18
|
+
def from_db?(klass)
|
19
|
+
to_hash?(klass) || to_array?(klass)
|
19
20
|
end
|
20
21
|
|
21
|
-
def from_db(
|
22
|
-
|
22
|
+
def from_db(value)
|
23
|
+
to_hash?(value.class) ? value.to_h : value.to_a
|
23
24
|
end
|
24
25
|
|
25
26
|
private
|
26
27
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
Sequel::Postgres::JSONArray => :to_a
|
31
|
-
}
|
28
|
+
def to_hash?(klass)
|
29
|
+
defined?(Sequel::Postgres::JSONHash) &&
|
30
|
+
klass == Sequel::Postgres::JSONHash
|
32
31
|
end
|
33
32
|
|
34
|
-
|
33
|
+
def to_array?(klass)
|
34
|
+
defined?(Sequel::Postgres::JSONArray) &&
|
35
|
+
klass == Sequel::Postgres::JSONArray
|
36
|
+
end
|
35
37
|
|
38
|
+
end
|
36
39
|
end
|
37
40
|
end
|
38
41
|
end
|
@@ -3,36 +3,39 @@ module Rasti
|
|
3
3
|
module TypeConverters
|
4
4
|
module PostgresTypes
|
5
5
|
class JSONB
|
6
|
-
|
7
6
|
class << self
|
8
7
|
|
9
|
-
|
10
|
-
|
8
|
+
DB_TYPE_REGEX = /^jsonb$/
|
9
|
+
|
10
|
+
def to_db?(type)
|
11
|
+
!type.match(DB_TYPE_REGEX).nil?
|
11
12
|
end
|
12
13
|
|
13
|
-
def to_db(value,
|
14
|
+
def to_db(value, type)
|
14
15
|
Sequel.pg_jsonb value
|
15
16
|
end
|
16
17
|
|
17
|
-
def
|
18
|
-
|
18
|
+
def from_db?(klass)
|
19
|
+
to_hash?(klass) || to_array?(klass)
|
19
20
|
end
|
20
21
|
|
21
|
-
def from_db(
|
22
|
-
|
22
|
+
def from_db(value)
|
23
|
+
to_hash?(value.class) ? value.to_h : value.to_a
|
23
24
|
end
|
24
25
|
|
25
26
|
private
|
26
27
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
Sequel::Postgres::JSONBArray => :to_a
|
31
|
-
}
|
28
|
+
def to_hash?(klass)
|
29
|
+
defined?(Sequel::Postgres::JSONBHash) &&
|
30
|
+
klass == Sequel::Postgres::JSONBHash
|
32
31
|
end
|
33
32
|
|
34
|
-
|
33
|
+
def to_array?(klass)
|
34
|
+
defined?(Sequel::Postgres::JSONBArray) &&
|
35
|
+
klass == Sequel::Postgres::JSONBArray
|
36
|
+
end
|
35
37
|
|
38
|
+
end
|
36
39
|
end
|
37
40
|
end
|
38
41
|
end
|