rasti-db 2.1.0 → 2.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rasti/db.rb +11 -1
  3. data/lib/rasti/db/nql/filter_condition_strategies/base.rb +17 -0
  4. data/lib/rasti/db/nql/filter_condition_strategies/postgres.rb +21 -0
  5. data/lib/rasti/db/nql/filter_condition_strategies/sqlite.rb +21 -0
  6. data/lib/rasti/db/nql/filter_condition_strategies/types/generic.rb +49 -0
  7. data/lib/rasti/db/nql/filter_condition_strategies/types/pg_array.rb +32 -0
  8. data/lib/rasti/db/nql/filter_condition_strategies/types/sqlite_array.rb +34 -0
  9. data/lib/rasti/db/nql/filter_condition_strategies/unsupported_type_comparison.rb +22 -0
  10. data/lib/rasti/db/nql/nodes/array_content.rb +21 -0
  11. data/lib/rasti/db/nql/nodes/comparisons/base.rb +10 -0
  12. data/lib/rasti/db/nql/nodes/comparisons/equal.rb +0 -4
  13. data/lib/rasti/db/nql/nodes/comparisons/greater_than.rb +0 -4
  14. data/lib/rasti/db/nql/nodes/comparisons/greater_than_or_equal.rb +0 -4
  15. data/lib/rasti/db/nql/nodes/comparisons/include.rb +0 -4
  16. data/lib/rasti/db/nql/nodes/comparisons/less_than.rb +0 -4
  17. data/lib/rasti/db/nql/nodes/comparisons/less_than_or_equal.rb +0 -4
  18. data/lib/rasti/db/nql/nodes/comparisons/like.rb +0 -4
  19. data/lib/rasti/db/nql/nodes/comparisons/not_equal.rb +0 -4
  20. data/lib/rasti/db/nql/nodes/comparisons/not_include.rb +0 -4
  21. data/lib/rasti/db/nql/nodes/constants/array.rb +17 -0
  22. data/lib/rasti/db/nql/nodes/constants/base.rb +17 -0
  23. data/lib/rasti/db/nql/nodes/constants/false.rb +1 -1
  24. data/lib/rasti/db/nql/nodes/constants/float.rb +1 -1
  25. data/lib/rasti/db/nql/nodes/constants/integer.rb +1 -1
  26. data/lib/rasti/db/nql/nodes/constants/literal_string.rb +1 -1
  27. data/lib/rasti/db/nql/nodes/constants/string.rb +1 -1
  28. data/lib/rasti/db/nql/nodes/constants/time.rb +1 -1
  29. data/lib/rasti/db/nql/nodes/constants/true.rb +1 -1
  30. data/lib/rasti/db/nql/syntax.rb +229 -11
  31. data/lib/rasti/db/nql/syntax.treetop +24 -11
  32. data/lib/rasti/db/query.rb +11 -15
  33. data/lib/rasti/db/type_converters/postgres.rb +32 -36
  34. data/lib/rasti/db/type_converters/postgres_types/array.rb +11 -9
  35. data/lib/rasti/db/type_converters/postgres_types/hstore.rb +10 -9
  36. data/lib/rasti/db/type_converters/postgres_types/json.rb +17 -14
  37. data/lib/rasti/db/type_converters/postgres_types/jsonb.rb +17 -14
  38. data/lib/rasti/db/type_converters/sqlite.rb +62 -0
  39. data/lib/rasti/db/type_converters/sqlite_types/array.rb +34 -0
  40. data/lib/rasti/db/type_converters/time_in_zone.rb +1 -1
  41. data/lib/rasti/db/version.rb +1 -1
  42. data/rasti-db.gemspec +1 -0
  43. data/spec/collection_spec.rb +8 -0
  44. data/spec/minitest_helper.rb +4 -4
  45. data/spec/nql/filter_condition_spec.rb +19 -2
  46. data/spec/nql/filter_condition_strategies_spec.rb +112 -0
  47. data/spec/nql/syntax_parser_spec.rb +24 -0
  48. data/spec/query_spec.rb +95 -6
  49. data/spec/type_converters/sqlite_spec.rb +66 -0
  50. data/spec/type_converters/time_in_zone_spec.rb +1 -1
  51. metadata +32 -2
@@ -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 append_computed_attribute(name)
61
- computed_attribute = collection_class.computed_attributes[name]
62
- ds = computed_attribute.apply_join(dataset).select_append(computed_attribute.identifier.as(name))
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
- each_model_in_batches(size: batch_size, &block)
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
- dataset.each_page(size) do |page|
83
- query = build_query dataset: page
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 = [PostgresTypes::JSON, PostgresTypes::JSONB, PostgresTypes::HStore, PostgresTypes::Array]
7
-
8
- @to_db_mapping = {}
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
- 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, 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(object)
23
- if from_db_mapping.key? object.class
24
- from_db_mapping[object.class].from_db object
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 to_db_mapping_for(db, collection_name)
33
- key = [db.opts[:database], collection_name]
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
- columns.each_with_object({}) do |(name, schema), hash|
39
- CONVERTERS.each do |converter|
40
- unless hash.key? name
41
- match = converter.column_type_regex.match schema[:db_type]
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
- hash[name] = {converter: converter, sub_type: match.captures.first} if match
44
- end
45
- end
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 from_db_mapping
51
- @from_db_mapping ||= begin
52
- CONVERTERS.each_with_object({}) do |converter, result|
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
- def column_type_regex
10
- /^([a-z]+)\[\]$/
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, sub_type)
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 db_classes
19
- [Sequel::Postgres::PGArray]
20
+ def from_db?(klass)
21
+ defined?(Sequel::Postgres::PGArray) &&
22
+ klass == Sequel::Postgres::PGArray
20
23
  end
21
24
 
22
- def from_db(object)
23
- object.to_a
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
- def column_type_regex
10
- /^hstore$/
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, sub_type)
14
+ def to_db(value, type)
14
15
  Sequel.hstore value
15
16
  end
16
17
 
17
- def db_classes
18
- [Sequel::Postgres::HStore]
18
+ def from_db?(klass)
19
+ defined?(Sequel::Postgres::HStore) &&
20
+ klass == Sequel::Postgres::HStore
19
21
  end
20
22
 
21
- def from_db(object)
22
- object.to_h
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
- def column_type_regex
10
- /^json$/
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, sub_type)
14
+ def to_db(value, type)
14
15
  Sequel.pg_json value
15
16
  end
16
17
 
17
- def db_classes
18
- @db_classes ||= from_db_convertions.keys
18
+ def from_db?(klass)
19
+ to_hash?(klass) || to_array?(klass)
19
20
  end
20
21
 
21
- def from_db(object)
22
- object.public_send from_db_convertions[object.class]
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 from_db_convertions
28
- @from_db_convertions ||= {
29
- Sequel::Postgres::JSONHash => :to_h,
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
- end
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
- def column_type_regex
10
- /^jsonb$/
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, sub_type)
14
+ def to_db(value, type)
14
15
  Sequel.pg_jsonb value
15
16
  end
16
17
 
17
- def db_classes
18
- @db_classes ||= from_db_convertions.keys
18
+ def from_db?(klass)
19
+ to_hash?(klass) || to_array?(klass)
19
20
  end
20
21
 
21
- def from_db(object)
22
- object.public_send from_db_convertions[object.class]
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 from_db_convertions
28
- @from_db_convertions ||= {
29
- Sequel::Postgres::JSONBHash => :to_h,
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
- end
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
@@ -6,7 +6,7 @@ module Rasti
6
6
  class << self
7
7
 
8
8
  def to_db(db, collection_name, attribute_name, value)
9
- value
9
+ value.is_a?(Timing::TimeInZone) ? value.to_time : value
10
10
  end
11
11
 
12
12
  def from_db(value)