rasti-db 1.5.0 → 2.3.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.
- 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
|