rasti-db 2.1.0 → 2.3.3
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/lib/rasti/db.rb +11 -1
- 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/comparisons/base.rb +10 -0
- 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/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/syntax.rb +229 -11
- data/lib/rasti/db/nql/syntax.treetop +24 -11
- data/lib/rasti/db/query.rb +11 -15
- 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/type_converters/time_in_zone.rb +1 -1
- data/lib/rasti/db/version.rb +1 -1
- data/rasti-db.gemspec +1 -0
- data/spec/collection_spec.rb +8 -0
- data/spec/minitest_helper.rb +4 -4
- data/spec/nql/filter_condition_spec.rb +19 -2
- data/spec/nql/filter_condition_strategies_spec.rb +112 -0
- data/spec/nql/syntax_parser_spec.rb +24 -0
- data/spec/query_spec.rb +95 -6
- data/spec/type_converters/sqlite_spec.rb +66 -0
- data/spec/type_converters/time_in_zone_spec.rb +1 -1
- metadata +32 -2
data/lib/rasti/db/query.rb
CHANGED
@@ -57,14 +57,16 @@ module Rasti
|
|
57
57
|
build_query relations_graph: relations_graph.with_all_attributes_for(relations)
|
58
58
|
end
|
59
59
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
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
|
63
65
|
build_query dataset: ds
|
64
66
|
end
|
65
67
|
|
66
68
|
def all
|
67
|
-
with_graph(dataset.all).map do |row|
|
69
|
+
with_graph(dataset.all).map do |row|
|
68
70
|
collection_class.model.new row
|
69
71
|
end
|
70
72
|
end
|
@@ -74,13 +76,15 @@ module Rasti
|
|
74
76
|
if batch_size.nil?
|
75
77
|
all.each(&block)
|
76
78
|
else
|
77
|
-
|
79
|
+
each_batch(size: batch_size) do |models|
|
80
|
+
models.each { |model| block.call model }
|
81
|
+
end
|
78
82
|
end
|
79
83
|
end
|
80
84
|
|
81
85
|
def each_batch(size:, &block)
|
82
|
-
|
83
|
-
query =
|
86
|
+
primary_keys.each_slice(size) do |pks|
|
87
|
+
query = where(collection_class.primary_key => pks)
|
84
88
|
block.call query.all
|
85
89
|
end
|
86
90
|
end
|
@@ -167,14 +171,6 @@ module Rasti
|
|
167
171
|
build_query dataset: instance_eval(&block)
|
168
172
|
end
|
169
173
|
|
170
|
-
def each_model_in_batches(size:, &block)
|
171
|
-
dataset.each_page(size) do |page|
|
172
|
-
page.each do |row|
|
173
|
-
block.call build_model(row)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
174
|
def with_related(relation_name, primary_keys)
|
179
175
|
ds = collection_class.relations[relation_name].apply_filter environment, dataset, primary_keys
|
180
176
|
build_query dataset: ds
|
@@ -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]
|
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
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Rasti
|
2
|
+
module DB
|
3
|
+
module TypeConverters
|
4
|
+
class SQLite
|
5
|
+
|
6
|
+
CONVERTERS = [SQLiteTypes::Array]
|
7
|
+
|
8
|
+
@to_db_mapping = {}
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def to_db(db, collection_name, attribute_name, value)
|
13
|
+
to_db_mapping = to_db_mapping_for db, collection_name
|
14
|
+
|
15
|
+
if to_db_mapping.key? attribute_name
|
16
|
+
to_db_mapping[attribute_name][:converter].to_db value
|
17
|
+
else
|
18
|
+
value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_db(object)
|
23
|
+
converter = find_converter_from_db object
|
24
|
+
if !converter.nil?
|
25
|
+
converter.from_db object
|
26
|
+
else
|
27
|
+
object
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def to_db_mapping_for(db, collection_name)
|
34
|
+
key = [db.opts[:database], collection_name]
|
35
|
+
|
36
|
+
@to_db_mapping[key] ||= begin
|
37
|
+
columns = Hash[db.schema(collection_name)]
|
38
|
+
|
39
|
+
columns.each_with_object({}) do |(name, schema), hash|
|
40
|
+
CONVERTERS.each do |converter|
|
41
|
+
unless hash.key? name
|
42
|
+
match = converter.column_type_regex.match schema[:db_type]
|
43
|
+
|
44
|
+
hash[name] = { converter: converter } if match
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def find_converter_from_db(object)
|
52
|
+
CONVERTERS.find do |converter|
|
53
|
+
converter.respond_for? object
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Rasti
|
2
|
+
module DB
|
3
|
+
module TypeConverters
|
4
|
+
module SQLiteTypes
|
5
|
+
class Array
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def column_type_regex
|
10
|
+
/^([a-z]+)\[\]$/
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_db(values)
|
14
|
+
JSON.dump(values)
|
15
|
+
end
|
16
|
+
|
17
|
+
def respond_for?(object)
|
18
|
+
parsed = JSON.parse object
|
19
|
+
object == to_db(parsed)
|
20
|
+
rescue
|
21
|
+
false
|
22
|
+
end
|
23
|
+
|
24
|
+
def from_db(object)
|
25
|
+
JSON.parse object
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|