rasti-db 1.5.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +52 -19
- data/lib/rasti/db.rb +11 -2
- data/lib/rasti/db/collection.rb +67 -36
- data/lib/rasti/db/computed_attribute.rb +22 -0
- data/lib/rasti/db/data_source.rb +18 -0
- data/lib/rasti/db/environment.rb +32 -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 +55 -23
- data/lib/rasti/db/relations/base.rb +22 -8
- data/lib/rasti/db/relations/graph.rb +10 -16
- data/lib/rasti/db/relations/many_to_many.rb +57 -23
- data/lib/rasti/db/relations/many_to_one.rb +9 -7
- data/lib/rasti/db/relations/one_to_many.rb +21 -13
- 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 +210 -50
- data/spec/computed_attribute_spec.rb +32 -0
- data/spec/minitest_helper.rb +77 -15
- data/spec/model_spec.rb +4 -2
- 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 +340 -54
- data/spec/relations_spec.rb +27 -7
- data/spec/type_converters/sqlite_spec.rb +66 -0
- metadata +40 -4
- data/lib/rasti/db/helpers.rb +0 -16
- data/lib/rasti/db/nql/nodes/field.rb +0 -23
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe 'ComputedAttribute' do
|
4
|
+
|
5
|
+
it 'Apply Join wiht join attribute must generate correct query' do
|
6
|
+
dataset = db[:users]
|
7
|
+
computed_attribute = Rasti::DB::ComputedAttribute.new(Sequel[:comments_count][:value]) do |dataset|
|
8
|
+
subquery = dataset.db.from(:comments)
|
9
|
+
.select(Sequel[:user_id], Sequel.function('count', :id).as(:value))
|
10
|
+
.group(:user_id)
|
11
|
+
.as(:comments_count)
|
12
|
+
|
13
|
+
dataset.join_table(:inner, subquery, :user_id => :id)
|
14
|
+
end
|
15
|
+
expected_query = "SELECT *, `comments_count`.`value` AS 'value' FROM `users` INNER JOIN (SELECT `user_id`, count(`id`) AS 'value' FROM `comments` GROUP BY `user_id`) AS 'comments_count' ON (`comments_count`.`user_id` = `users`.`id`)"
|
16
|
+
computed_attribute.apply_join(dataset)
|
17
|
+
.select_append(computed_attribute.identifier)
|
18
|
+
.sql
|
19
|
+
.must_equal expected_query
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'Apply join without join attribute must generate correct query' do
|
23
|
+
dataset = db[:people]
|
24
|
+
computed_attribute = Rasti::DB::ComputedAttribute.new Sequel.join([:first_name, ' ', :last_name])
|
25
|
+
expected_query = "SELECT * FROM `people` WHERE ((`first_name` || ' ' || `last_name`) = 'FULL NAME')"
|
26
|
+
computed_attribute.apply_join(dataset)
|
27
|
+
.where(computed_attribute.identifier => 'FULL NAME')
|
28
|
+
.sql
|
29
|
+
.must_equal expected_query
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/spec/minitest_helper.rb
CHANGED
@@ -10,24 +10,39 @@ require 'sequel/extensions/pg_array'
|
|
10
10
|
require 'sequel/extensions/pg_json'
|
11
11
|
|
12
12
|
Rasti::DB.configure do |config|
|
13
|
-
config.type_converters = [Rasti::DB::TypeConverters::TimeInZone]
|
13
|
+
config.type_converters = [Rasti::DB::TypeConverters::TimeInZone, Rasti::DB::TypeConverters::SQLite]
|
14
|
+
config.nql_filter_condition_strategy = Rasti::DB::NQL::FilterConditionStrategies::SQLite.new
|
14
15
|
end
|
15
16
|
|
16
|
-
User = Rasti::DB::Model[:id, :name, :posts, :comments, :person]
|
17
|
-
Post = Rasti::DB::Model[:id, :title, :body, :user_id, :user, :comments, :categories]
|
18
|
-
Comment = Rasti::DB::Model[:id, :text, :user_id, :user, :post_id, :post]
|
17
|
+
User = Rasti::DB::Model[:id, :name, :posts, :comments, :person, :comments_count]
|
18
|
+
Post = Rasti::DB::Model[:id, :title, :body, :user_id, :user, :comments, :categories, :language_id, :language, :notice, :author]
|
19
|
+
Comment = Rasti::DB::Model[:id, :text, :user_id, :user, :post_id, :post, :tags]
|
19
20
|
Category = Rasti::DB::Model[:id, :name, :posts]
|
20
|
-
Person = Rasti::DB::Model[:document_number, :first_name, :last_name, :birth_date, :user_id, :user]
|
21
|
+
Person = Rasti::DB::Model[:document_number, :first_name, :last_name, :birth_date, :user_id, :user, :languages, :full_name]
|
22
|
+
Language = Rasti::DB::Model[:id, :name, :people]
|
21
23
|
|
22
24
|
|
23
25
|
class Users < Rasti::DB::Collection
|
24
26
|
one_to_many :posts
|
25
27
|
one_to_many :comments
|
26
28
|
one_to_one :person
|
29
|
+
|
30
|
+
computed_attribute :comments_count do
|
31
|
+
Rasti::DB::ComputedAttribute.new(Sequel[:comments_count][:value]) do |dataset|
|
32
|
+
subquery = dataset.db.from(:comments)
|
33
|
+
.select(Sequel[:user_id], Sequel.function('count', :id).as(:value))
|
34
|
+
.group(:user_id)
|
35
|
+
.as(:comments_count)
|
36
|
+
|
37
|
+
dataset.join_table(:inner, subquery, :user_id => :id)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
27
41
|
end
|
28
42
|
|
29
43
|
class Posts < Rasti::DB::Collection
|
30
44
|
many_to_one :user
|
45
|
+
many_to_one :language
|
31
46
|
many_to_many :categories
|
32
47
|
one_to_many :comments
|
33
48
|
|
@@ -39,12 +54,21 @@ class Posts < Rasti::DB::Collection
|
|
39
54
|
|
40
55
|
query :commented_by do |user_id|
|
41
56
|
chainable do
|
42
|
-
dataset.join(
|
43
|
-
.where(
|
57
|
+
dataset.join(qualify(:comments), post_id: :id)
|
58
|
+
.where(Sequel[:comments][:user_id] => user_id)
|
44
59
|
.select_all(:posts)
|
45
60
|
.distinct
|
46
61
|
end
|
47
62
|
end
|
63
|
+
|
64
|
+
computed_attribute :notice do
|
65
|
+
Rasti::DB::ComputedAttribute.new Sequel.join([:title, ': ', :body])
|
66
|
+
end
|
67
|
+
|
68
|
+
computed_attribute :author do
|
69
|
+
Rasti::DB::ComputedAttribute.new Sequel[:user]
|
70
|
+
end
|
71
|
+
|
48
72
|
end
|
49
73
|
|
50
74
|
class Comments < Rasti::DB::Collection
|
@@ -53,7 +77,7 @@ class Comments < Rasti::DB::Collection
|
|
53
77
|
|
54
78
|
def posts_commented_by(user_id)
|
55
79
|
dataset.where(Sequel[:comments][:user_id] => user_id)
|
56
|
-
.join(
|
80
|
+
.join(qualify(:posts, data_source_name: :default), id: :post_id)
|
57
81
|
.select_all(:posts)
|
58
82
|
.map { |row| Post.new row }
|
59
83
|
end
|
@@ -70,24 +94,43 @@ class People < Rasti::DB::Collection
|
|
70
94
|
set_model Person
|
71
95
|
|
72
96
|
many_to_one :user
|
97
|
+
many_to_many :languages
|
98
|
+
|
99
|
+
computed_attribute :full_name do |db|
|
100
|
+
Rasti::DB::ComputedAttribute.new Sequel.join([:first_name, ' ', :last_name])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class Languages < Rasti::DB::Collection
|
105
|
+
set_data_source_name :custom
|
106
|
+
|
107
|
+
many_to_many :people, collection: People, relation_data_source_name: :default
|
108
|
+
one_to_many :posts
|
73
109
|
end
|
74
110
|
|
75
111
|
|
76
112
|
class Minitest::Spec
|
77
113
|
|
78
|
-
let(:users) { Users.new
|
114
|
+
let(:users) { Users.new environment }
|
79
115
|
|
80
|
-
let(:posts) { Posts.new
|
116
|
+
let(:posts) { Posts.new environment }
|
81
117
|
|
82
|
-
let(:comments) { Comments.new
|
118
|
+
let(:comments) { Comments.new environment }
|
83
119
|
|
84
|
-
let(:categories) { Categories.new
|
120
|
+
let(:categories) { Categories.new environment }
|
85
121
|
|
86
|
-
let(:people) { People.new
|
122
|
+
let(:people) { People.new environment }
|
87
123
|
|
88
|
-
let
|
89
|
-
|
124
|
+
let(:languages) { Languages.new environment }
|
125
|
+
|
126
|
+
let(:driver) { (RUBY_ENGINE == 'jruby') ? 'jdbc:sqlite::memory:' : {adapter: :sqlite} }
|
127
|
+
|
128
|
+
let :environment do
|
129
|
+
Rasti::DB::Environment.new default: Rasti::DB::DataSource.new(db),
|
130
|
+
custom: Rasti::DB::DataSource.new(custom_db)
|
131
|
+
end
|
90
132
|
|
133
|
+
let :db do
|
91
134
|
Sequel.connect(driver).tap do |db|
|
92
135
|
|
93
136
|
db.create_table :users do
|
@@ -99,12 +142,14 @@ class Minitest::Spec
|
|
99
142
|
primary_key :id
|
100
143
|
String :title, null: false, unique: true
|
101
144
|
String :body, null: false
|
145
|
+
Integer :language_id, null: false, index: true
|
102
146
|
foreign_key :user_id, :users, null: false, index: true
|
103
147
|
end
|
104
148
|
|
105
149
|
db.create_table :comments do
|
106
150
|
primary_key :id
|
107
151
|
String :text, null: false
|
152
|
+
String :tags, default: Sequel.lit("'[]'")
|
108
153
|
foreign_key :user_id, :users, null: false, index: true
|
109
154
|
foreign_key :post_id, :posts, null: false, index: true
|
110
155
|
end
|
@@ -128,6 +173,23 @@ class Minitest::Spec
|
|
128
173
|
foreign_key :user_id, :users, null: false, unique: true
|
129
174
|
end
|
130
175
|
|
176
|
+
db.create_table :languages_people do
|
177
|
+
Integer :language_id, null: false, index: true
|
178
|
+
foreign_key :document_number, :people, type: String, null: false, index: true
|
179
|
+
primary_key [:language_id, :document_number]
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
let :custom_db do
|
186
|
+
Sequel.connect(driver).tap do |db|
|
187
|
+
|
188
|
+
db.create_table :languages do
|
189
|
+
primary_key :id
|
190
|
+
String :name, null: false, unique: true
|
191
|
+
end
|
192
|
+
|
131
193
|
end
|
132
194
|
end
|
133
195
|
|
data/spec/model_spec.rb
CHANGED
@@ -10,7 +10,7 @@ describe 'Model' do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'Invalid definition' do
|
13
|
-
error = proc {
|
13
|
+
error = proc { Rasti::DB::Model[:id, :name, :name] }.must_raise ArgumentError
|
14
14
|
error.message.must_equal 'Attribute name already exists'
|
15
15
|
end
|
16
16
|
|
@@ -42,7 +42,7 @@ describe 'Model' do
|
|
42
42
|
describe 'To String' do
|
43
43
|
|
44
44
|
it 'Class' do
|
45
|
-
User.to_s.must_equal 'User[id, name, posts, comments, person]'
|
45
|
+
User.to_s.must_equal 'User[id, name, posts, comments, person, comments_count]'
|
46
46
|
end
|
47
47
|
|
48
48
|
it 'Instance' do
|
@@ -82,6 +82,8 @@ describe 'Model' do
|
|
82
82
|
it 'Merge' do
|
83
83
|
user = User.new(id: 1, name: 'User 1')
|
84
84
|
changed_user = user.merge(name: 'User 2')
|
85
|
+
|
86
|
+
user.must_equal User.new(id: 1, name: 'User 1')
|
85
87
|
changed_user.must_equal User.new(id: 1, name: 'User 2')
|
86
88
|
end
|
87
89
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe 'NQL::ComputedAttributes' do
|
4
|
+
|
5
|
+
let(:parser) { Rasti::DB::NQL::SyntaxParser.new }
|
6
|
+
|
7
|
+
def parse(expression)
|
8
|
+
parser.parse expression
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'must have one computed attributes' do
|
12
|
+
tree = parse 'notice = any notice'
|
13
|
+
|
14
|
+
tree.computed_attributes(Posts).must_equal [:notice]
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'must have multiple computed attributes' do
|
18
|
+
tree = parse 'notice = any notice & (author: anonym | title = good morning)'
|
19
|
+
|
20
|
+
tree.computed_attributes(Posts).must_equal [:notice, :author]
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'must have not repeated computed attributes when expression have it' do
|
24
|
+
tree = parse 'notice = Hi | notice = Bye'
|
25
|
+
|
26
|
+
tree.computed_attributes(Posts).must_equal [:notice]
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -4,9 +4,11 @@ describe 'NQL::FilterCondition' do
|
|
4
4
|
|
5
5
|
let(:parser) { Rasti::DB::NQL::SyntaxParser.new }
|
6
6
|
|
7
|
+
let(:collection_class) { Rasti::DB::Collection }
|
8
|
+
|
7
9
|
def filter_condition(expression)
|
8
10
|
tree = parser.parse expression
|
9
|
-
tree.filter_condition
|
11
|
+
tree.filter_condition(collection_class)
|
10
12
|
end
|
11
13
|
|
12
14
|
def assert_identifier(identifier, expected_value)
|
@@ -17,13 +19,30 @@ describe 'NQL::FilterCondition' do
|
|
17
19
|
def assert_comparison(filter, expected_left, expected_comparator, expected_right)
|
18
20
|
filter.must_be_instance_of Sequel::SQL::BooleanExpression
|
19
21
|
filter.op.must_equal expected_comparator.to_sym
|
20
|
-
|
22
|
+
|
21
23
|
left, right = filter.args
|
22
24
|
assert_identifier left, expected_left
|
23
25
|
|
24
26
|
right.must_equal expected_right
|
25
27
|
end
|
26
28
|
|
29
|
+
describe 'None Filter Condition Strategy Validation' do
|
30
|
+
|
31
|
+
before do
|
32
|
+
Rasti::DB.nql_filter_condition_strategy = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
after do
|
36
|
+
Rasti::DB.nql_filter_condition_strategy = Rasti::DB::NQL::FilterConditionStrategies::SQLite.new
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'must raise error' do
|
40
|
+
error = proc { filter_condition 'column = value' }.must_raise RuntimeError
|
41
|
+
error.message.must_equal 'Undefined Rasti::DB.nql_filter_condition_strategy'
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
27
46
|
describe 'Comparison' do
|
28
47
|
|
29
48
|
it 'must create filter from expression with <' do
|
@@ -95,7 +114,7 @@ describe 'NQL::FilterCondition' do
|
|
95
114
|
|
96
115
|
end
|
97
116
|
|
98
|
-
it 'must create filter from expression with
|
117
|
+
it 'must create filter from expression with attribute with multiple tables' do
|
99
118
|
filter = filter_condition 'table_one.table_two.column = test'
|
100
119
|
identifier, value = filter.first
|
101
120
|
|
@@ -132,7 +151,7 @@ describe 'NQL::FilterCondition' do
|
|
132
151
|
|
133
152
|
filter.must_be_instance_of Sequel::SQL::BooleanExpression
|
134
153
|
filter.op.must_equal :AND
|
135
|
-
|
154
|
+
|
136
155
|
major_expression, and_expression = filter.args
|
137
156
|
assert_comparison major_expression, 'column_one', '>', 1
|
138
157
|
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe 'NQL::FilterConditionStrategies' do
|
4
|
+
|
5
|
+
let(:comments_query) { Rasti::DB::Query.new collection_class: Comments, dataset: db[:comments], environment: environment }
|
6
|
+
|
7
|
+
def sqls_where(query)
|
8
|
+
"#<Rasti::DB::Query: \"SELECT `comments`.* FROM `comments` WHERE (#{query})\">"
|
9
|
+
end
|
10
|
+
|
11
|
+
def sqls_where_not(query)
|
12
|
+
"#<Rasti::DB::Query: \"SELECT `comments`.* FROM `comments` WHERE NOT (#{query})\">"
|
13
|
+
end
|
14
|
+
|
15
|
+
def nql_s(nql_query)
|
16
|
+
comments_query.nql(nql_query).to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'Generic' do
|
20
|
+
|
21
|
+
it 'Equal' do
|
22
|
+
nql_s('text = hola').must_equal sqls_where("`comments`.`text` = 'hola'")
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'Not Equal' do
|
26
|
+
nql_s('text != hola').must_equal sqls_where("`comments`.`text` != 'hola'")
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'Greather Than' do
|
30
|
+
nql_s('id > 1').must_equal sqls_where("`comments`.`id` > 1")
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'Greather Than or Equal' do
|
34
|
+
nql_s('id >= 1').must_equal sqls_where("`comments`.`id` >= 1")
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'Less Than' do
|
38
|
+
nql_s('id < 1').must_equal sqls_where("`comments`.`id` < 1")
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'Less Than or Equal' do
|
42
|
+
nql_s('id <= 1').must_equal sqls_where("`comments`.`id` <= 1")
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'Like' do
|
46
|
+
nql_s('text ~ hola').must_equal sqls_where("UPPER(`comments`.`text`) LIKE UPPER('hola') ESCAPE '\\'")
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'Include' do
|
50
|
+
nql_s('text: hola').must_equal sqls_where("UPPER(`comments`.`text`) LIKE UPPER('%hola%') ESCAPE '\\'")
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'Not Include' do
|
54
|
+
nql_s('text!: hola').must_equal sqls_where_not("UPPER(`comments`.`text`) LIKE UPPER('%hola%') ESCAPE '\\'")
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'SQLite Array' do
|
60
|
+
|
61
|
+
it 'Equal' do
|
62
|
+
nql_s('tags = [notice]').must_equal sqls_where("`comments`.`tags` = '[\"notice\"]'")
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'Not Equal' do
|
66
|
+
nql_s('tags != [notice]').must_equal sqls_where_not("`comments`.`tags` LIKE '%\"notice\"%' ESCAPE '\\'")
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'Like' do
|
70
|
+
nql_s('tags ~ [notice]').must_equal sqls_where("`comments`.`tags` LIKE '%notice%' ESCAPE '\\'")
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'Include' do
|
74
|
+
nql_s('tags: [notice]').must_equal sqls_where("`comments`.`tags` LIKE '%\"notice\"%' ESCAPE '\\'")
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'Not Include' do
|
78
|
+
nql_s('tags!: [notice]').must_equal sqls_where_not("`comments`.`tags` LIKE '%\"notice\"%' ESCAPE '\\'")
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
describe 'Postgres Array' do
|
84
|
+
|
85
|
+
before do
|
86
|
+
Rasti::DB.nql_filter_condition_strategy = Rasti::DB::NQL::FilterConditionStrategies::Postgres.new
|
87
|
+
Sequel.extension :pg_array_ops
|
88
|
+
end
|
89
|
+
|
90
|
+
after do
|
91
|
+
Rasti::DB.nql_filter_condition_strategy = Rasti::DB::NQL::FilterConditionStrategies::SQLite.new
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'Equal' do
|
95
|
+
nql_s('tags = [notice]').must_equal sqls_where("(`comments`.`tags` @> ARRAY['notice']) AND (`comments`.`tags` <@ ARRAY['notice'])")
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'Not Equal' do
|
99
|
+
nql_s('tags != [notice]').must_equal sqls_where("NOT (`comments`.`tags` @> ARRAY['notice']) OR NOT (`comments`.`tags` <@ ARRAY['notice'])")
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'Include' do
|
103
|
+
nql_s('tags: [notice]').must_equal sqls_where("`comments`.`tags` && ARRAY['notice']")
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'Not Include' do
|
107
|
+
nql_s('tags!: [notice]').must_equal sqls_where_not("`comments`.`tags` && ARRAY['notice']")
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -31,7 +31,7 @@ describe 'NQL::SyntaxParser' do
|
|
31
31
|
proposition = tree.proposition
|
32
32
|
proposition.must_be_instance_of node_class
|
33
33
|
proposition.comparator.text_value.must_equal comparator
|
34
|
-
proposition.
|
34
|
+
proposition.attribute.text_value.must_equal 'column'
|
35
35
|
proposition.argument.text_value.must_equal 'value'
|
36
36
|
end
|
37
37
|
end
|
@@ -44,7 +44,7 @@ describe 'NQL::SyntaxParser' do
|
|
44
44
|
proposition = tree.proposition
|
45
45
|
proposition.must_be_instance_of Rasti::DB::NQL::Nodes::Comparisons::Equal
|
46
46
|
proposition.comparator.text_value.must_equal '='
|
47
|
-
proposition.
|
47
|
+
proposition.attribute.text_value.must_equal 'column'
|
48
48
|
proposition.argument.text_value.must_equal 'value'
|
49
49
|
end
|
50
50
|
|
@@ -128,12 +128,19 @@ describe 'NQL::SyntaxParser' do
|
|
128
128
|
|
129
129
|
end
|
130
130
|
|
131
|
-
it 'must parse expression with
|
131
|
+
it 'must parse expression with attribute with tables' do
|
132
132
|
tree = parse 'relation_table_one.relation_table_two.column = 1'
|
133
133
|
|
134
|
-
left_hand_operand = tree.proposition.
|
134
|
+
left_hand_operand = tree.proposition.attribute
|
135
135
|
left_hand_operand.tables.must_equal ['relation_table_one', 'relation_table_two']
|
136
|
-
left_hand_operand.column.must_equal
|
136
|
+
left_hand_operand.column.must_equal :column
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'must parse expression with computed attribute' do
|
140
|
+
tree = parse 'comments_count = 1'
|
141
|
+
computed = tree.proposition.attribute
|
142
|
+
computed.tables.must_equal []
|
143
|
+
computed.computed_attributes(Users).must_equal [:comments_count]
|
137
144
|
end
|
138
145
|
|
139
146
|
end
|
@@ -206,4 +213,28 @@ describe 'NQL::SyntaxParser' do
|
|
206
213
|
|
207
214
|
end
|
208
215
|
|
216
|
+
describe 'Array' do
|
217
|
+
|
218
|
+
it 'must parse array with one element' do
|
219
|
+
tree = parse 'column = [ a ]'
|
220
|
+
tree.proposition.argument.value.must_equal ['a']
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'must parse array with many elements' do
|
224
|
+
tree = parse 'column = [ a, b, c ]'
|
225
|
+
tree.proposition.argument.value.must_equal ['a', 'b', 'c']
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'must parse array respecting content types' do
|
229
|
+
tree = parse 'column = [ true, 12:00, 1.1, 1, "literal string", string ]'
|
230
|
+
tree.proposition.argument.value.must_equal [true, Timing::TimeInZone.parse('12:00').to_s, 1.1, 1, "literal string", 'string']
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'must parse array with literal string allowing reserved characters in conflict with array' do
|
234
|
+
tree = parse 'column = [ ",", "[", "]", "[,", "],", "[,]", "simple literal" ]'
|
235
|
+
tree.proposition.argument.value.must_equal [ ',', '[', ']', '[,', '],', '[,]', 'simple literal' ]
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
|
209
240
|
end
|