rasti-db 2.0.1 → 2.1.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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -3
  3. data/lib/rasti/db/collection.rb +10 -1
  4. data/lib/rasti/db/computed_attribute.rb +22 -0
  5. data/lib/rasti/db/nql/nodes/attribute.rb +37 -0
  6. data/lib/rasti/db/nql/nodes/binary_node.rb +4 -0
  7. data/lib/rasti/db/nql/nodes/comparisons/base.rb +5 -1
  8. data/lib/rasti/db/nql/nodes/comparisons/equal.rb +2 -2
  9. data/lib/rasti/db/nql/nodes/comparisons/greater_than.rb +2 -2
  10. data/lib/rasti/db/nql/nodes/comparisons/greater_than_or_equal.rb +2 -2
  11. data/lib/rasti/db/nql/nodes/comparisons/include.rb +2 -2
  12. data/lib/rasti/db/nql/nodes/comparisons/less_than.rb +2 -2
  13. data/lib/rasti/db/nql/nodes/comparisons/less_than_or_equal.rb +2 -2
  14. data/lib/rasti/db/nql/nodes/comparisons/like.rb +2 -2
  15. data/lib/rasti/db/nql/nodes/comparisons/not_equal.rb +2 -2
  16. data/lib/rasti/db/nql/nodes/comparisons/not_include.rb +2 -2
  17. data/lib/rasti/db/nql/nodes/conjunction.rb +2 -2
  18. data/lib/rasti/db/nql/nodes/disjunction.rb +2 -2
  19. data/lib/rasti/db/nql/nodes/parenthesis_sentence.rb +6 -2
  20. data/lib/rasti/db/nql/nodes/sentence.rb +6 -2
  21. data/lib/rasti/db/nql/syntax.rb +33 -33
  22. data/lib/rasti/db/nql/syntax.treetop +12 -12
  23. data/lib/rasti/db/query.rb +41 -7
  24. data/lib/rasti/db/version.rb +1 -1
  25. data/spec/computed_attribute_spec.rb +32 -0
  26. data/spec/minitest_helper.rb +30 -3
  27. data/spec/model_spec.rb +1 -1
  28. data/spec/nql/computed_attributes_spec.rb +29 -0
  29. data/spec/nql/filter_condition_spec.rb +4 -2
  30. data/spec/nql/syntax_parser_spec.rb +12 -5
  31. data/spec/query_spec.rb +146 -34
  32. metadata +8 -3
  33. data/lib/rasti/db/nql/nodes/field.rb +0 -23
@@ -42,44 +42,44 @@ module Rasti
42
42
  comparison_equal
43
43
  end
44
44
 
45
- rule field
46
- _tables:(table:field_name '.')* _column:field_name <Nodes::Field>
45
+ rule attribute
46
+ _tables:(table:attribute_name '.')* _column:attribute_name <Nodes::Attribute>
47
47
  end
48
48
 
49
49
  rule comparison_include
50
- field:field space* comparator:':' space* argument:basic <Nodes::Comparisons::Include>
50
+ attribute:attribute space* comparator:':' space* argument:basic <Nodes::Comparisons::Include>
51
51
  end
52
52
 
53
53
  rule comparison_not_include
54
- field:field space* comparator:'!:' space* argument:basic <Nodes::Comparisons::NotInclude>
54
+ attribute:attribute space* comparator:'!:' space* argument:basic <Nodes::Comparisons::NotInclude>
55
55
  end
56
56
 
57
57
  rule comparison_like
58
- field:field space* comparator:'~' space* argument:basic <Nodes::Comparisons::Like>
58
+ attribute:attribute space* comparator:'~' space* argument:basic <Nodes::Comparisons::Like>
59
59
  end
60
60
 
61
61
  rule comparison_greater_than
62
- field:field space* comparator:'>' space* argument:basic <Nodes::Comparisons::GreaterThan>
62
+ attribute:attribute space* comparator:'>' space* argument:basic <Nodes::Comparisons::GreaterThan>
63
63
  end
64
64
 
65
65
  rule comparison_greater_than_or_equal
66
- field:field space* comparator:'>=' space* argument:basic <Nodes::Comparisons::GreaterThanOrEqual>
66
+ attribute:attribute space* comparator:'>=' space* argument:basic <Nodes::Comparisons::GreaterThanOrEqual>
67
67
  end
68
68
 
69
69
  rule comparison_less_than
70
- field:field space* comparator:'<' space* argument:basic <Nodes::Comparisons::LessThan>
70
+ attribute:attribute space* comparator:'<' space* argument:basic <Nodes::Comparisons::LessThan>
71
71
  end
72
72
 
73
73
  rule comparison_less_than_or_equal
74
- field:field space* comparator:'<=' space* argument:basic <Nodes::Comparisons::LessThanOrEqual>
74
+ attribute:attribute space* comparator:'<=' space* argument:basic <Nodes::Comparisons::LessThanOrEqual>
75
75
  end
76
76
 
77
77
  rule comparison_not_equal
78
- field:field space* comparator:'!=' space* argument:basic <Nodes::Comparisons::NotEqual>
78
+ attribute:attribute space* comparator:'!=' space* argument:basic <Nodes::Comparisons::NotEqual>
79
79
  end
80
80
 
81
81
  rule comparison_equal
82
- field:field space* comparator:'=' space* argument:basic <Nodes::Comparisons::Equal>
82
+ attribute:attribute space* comparator:'=' space* argument:basic <Nodes::Comparisons::Equal>
83
83
  end
84
84
 
85
85
  rule basic
@@ -95,7 +95,7 @@ module Rasti
95
95
  [\s\t\n]
96
96
  end
97
97
 
98
- rule field_name
98
+ rule attribute_name
99
99
  [a-z_]+
100
100
  end
101
101
 
@@ -57,6 +57,12 @@ 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))
63
+ build_query dataset: ds
64
+ end
65
+
60
66
  def all
61
67
  with_graph(dataset.all).map do |row|
62
68
  collection_class.model.new row
@@ -64,8 +70,19 @@ module Rasti
64
70
  end
65
71
  alias_method :to_a, :all
66
72
 
67
- def each(&block)
68
- all.each(&block)
73
+ def each(batch_size:nil, &block)
74
+ if batch_size.nil?
75
+ all.each(&block)
76
+ else
77
+ each_model_in_batches(size: batch_size, &block)
78
+ end
79
+ end
80
+
81
+ def each_batch(size:, &block)
82
+ dataset.each_page(size) do |page|
83
+ query = build_query dataset: page
84
+ block.call query.all
85
+ end
69
86
  end
70
87
 
71
88
  def graph(*relations)
@@ -94,12 +111,12 @@ module Rasti
94
111
 
95
112
  def first
96
113
  row = dataset.first
97
- row ? collection_class.model.new(with_graph(row)) : nil
114
+ row ? build_model(row) : nil
98
115
  end
99
116
 
100
117
  def last
101
118
  row = dataset.last
102
- row ? collection_class.model.new(with_graph(row)) : nil
119
+ row ? build_model(row) : nil
103
120
  end
104
121
 
105
122
  def detect(*args, &block)
@@ -116,10 +133,15 @@ module Rasti
116
133
 
117
134
  raise NQL::InvalidExpressionError.new(filter_expression) if sentence.nil?
118
135
 
136
+ ds = sentence.computed_attributes(collection_class).inject(dataset) do |ds, name|
137
+ collection_class.computed_attributes[name].apply_join ds
138
+ end
139
+ query = build_query dataset: ds
140
+
119
141
  dependency_tables = sentence.dependency_tables
120
- query = dependency_tables.empty? ? self : join(*dependency_tables)
121
-
122
- query.where sentence.filter_condition
142
+ query = query.join(*dependency_tables) unless dependency_tables.empty?
143
+
144
+ query.where sentence.filter_condition(collection_class)
123
145
  end
124
146
 
125
147
  private
@@ -137,10 +159,22 @@ module Rasti
137
159
  Query.new(**current_args.merge(args))
138
160
  end
139
161
 
162
+ def build_model(row)
163
+ collection_class.model.new with_graph(row)
164
+ end
165
+
140
166
  def chainable(&block)
141
167
  build_query dataset: instance_eval(&block)
142
168
  end
143
169
 
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
+
144
178
  def with_related(relation_name, primary_keys)
145
179
  ds = collection_class.relations[relation_name].apply_filter environment, dataset, primary_keys
146
180
  build_query dataset: ds
@@ -1,5 +1,5 @@
1
1
  module Rasti
2
2
  module DB
3
- VERSION = '2.0.1'
3
+ VERSION = '2.1.0'
4
4
  end
5
5
  end
@@ -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
@@ -13,11 +13,11 @@ Rasti::DB.configure do |config|
13
13
  config.type_converters = [Rasti::DB::TypeConverters::TimeInZone]
14
14
  end
15
15
 
16
- User = Rasti::DB::Model[:id, :name, :posts, :comments, :person]
17
- Post = Rasti::DB::Model[:id, :title, :body, :user_id, :user, :comments, :categories, :language_id, :language]
16
+ User = Rasti::DB::Model[:id, :name, :posts, :comments, :person, :comments_count]
17
+ Post = Rasti::DB::Model[:id, :title, :body, :user_id, :user, :comments, :categories, :language_id, :language, :notice, :author]
18
18
  Comment = Rasti::DB::Model[:id, :text, :user_id, :user, :post_id, :post]
19
19
  Category = Rasti::DB::Model[:id, :name, :posts]
20
- Person = Rasti::DB::Model[:document_number, :first_name, :last_name, :birth_date, :user_id, :user, :languages]
20
+ Person = Rasti::DB::Model[:document_number, :first_name, :last_name, :birth_date, :user_id, :user, :languages, :full_name]
21
21
  Language = Rasti::DB::Model[:id, :name, :people]
22
22
 
23
23
 
@@ -25,6 +25,18 @@ class Users < Rasti::DB::Collection
25
25
  one_to_many :posts
26
26
  one_to_many :comments
27
27
  one_to_one :person
28
+
29
+ computed_attribute :comments_count do
30
+ Rasti::DB::ComputedAttribute.new(Sequel[:comments_count][:value]) do |dataset|
31
+ subquery = dataset.db.from(:comments)
32
+ .select(Sequel[:user_id], Sequel.function('count', :id).as(:value))
33
+ .group(:user_id)
34
+ .as(:comments_count)
35
+
36
+ dataset.join_table(:inner, subquery, :user_id => :id)
37
+ end
38
+ end
39
+
28
40
  end
29
41
 
30
42
  class Posts < Rasti::DB::Collection
@@ -47,6 +59,15 @@ class Posts < Rasti::DB::Collection
47
59
  .distinct
48
60
  end
49
61
  end
62
+
63
+ computed_attribute :notice do
64
+ Rasti::DB::ComputedAttribute.new Sequel.join([:title, ': ', :body])
65
+ end
66
+
67
+ computed_attribute :author do
68
+ Rasti::DB::ComputedAttribute.new Sequel[:user]
69
+ end
70
+
50
71
  end
51
72
 
52
73
  class Comments < Rasti::DB::Collection
@@ -73,6 +94,10 @@ class People < Rasti::DB::Collection
73
94
 
74
95
  many_to_one :user
75
96
  many_to_many :languages
97
+
98
+ computed_attribute :full_name do |db|
99
+ Rasti::DB::ComputedAttribute.new Sequel.join([:first_name, ' ', :last_name])
100
+ end
76
101
  end
77
102
 
78
103
  class Languages < Rasti::DB::Collection
@@ -107,6 +132,8 @@ class Minitest::Spec
107
132
  let :db do
108
133
  Sequel.connect(driver).tap do |db|
109
134
 
135
+ db.extension :pagination
136
+
110
137
  db.create_table :users do
111
138
  primary_key :id
112
139
  String :name, null: false, unique: true
@@ -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
@@ -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)
@@ -95,7 +97,7 @@ describe 'NQL::FilterCondition' do
95
97
 
96
98
  end
97
99
 
98
- it 'must create filter from expression with field with multiple tables' do
100
+ it 'must create filter from expression with attribute with multiple tables' do
99
101
  filter = filter_condition 'table_one.table_two.column = test'
100
102
  identifier, value = filter.first
101
103
 
@@ -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.field.text_value.must_equal 'column'
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.field.text_value.must_equal 'column'
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 field with tables' do
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.field
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 'column'
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
@@ -5,13 +5,13 @@ describe 'Query' do
5
5
  before do
6
6
  custom_db[:languages].insert name: 'Spanish'
7
7
 
8
- 1.upto(10) do |i|
8
+ 1.upto(10) do |i|
9
9
  db[:users].insert name: "User #{i}"
10
10
 
11
- db[:people].insert user_id: i,
12
- document_number: "document_#{i}",
13
- first_name: "Name #{i}",
14
- last_name: "Last Name #{i}",
11
+ db[:people].insert user_id: i,
12
+ document_number: "document_#{i}",
13
+ first_name: "Name #{i}",
14
+ last_name: "Last Name #{i}",
15
15
  birth_date: Date.parse('2020-04-24')
16
16
 
17
17
  db[:languages_people].insert language_id: 1, document_number: "document_#{i}"
@@ -22,7 +22,7 @@ describe 'Query' do
22
22
  db[:posts].insert user_id: 4, title: 'Best post', body: '...', language_id: 1
23
23
 
24
24
  1.upto(3) { |i| db[:categories].insert name: "Category #{i}" }
25
-
25
+
26
26
  db[:comments].insert post_id: 1, user_id: 5, text: 'Comment 1'
27
27
  db[:comments].insert post_id: 1, user_id: 7, text: 'Comment 2'
28
28
  db[:comments].insert post_id: 2, user_id: 2, text: 'Comment 3'
@@ -35,11 +35,13 @@ describe 'Query' do
35
35
  end
36
36
 
37
37
  let(:users_query) { Rasti::DB::Query.new collection_class: Users, dataset: db[:users], environment: environment }
38
-
38
+
39
39
  let(:posts_query) { Rasti::DB::Query.new collection_class: Posts, dataset: db[:posts], environment: environment }
40
-
40
+
41
41
  let(:comments_query) { Rasti::DB::Query.new collection_class: Comments, dataset: db[:comments], environment: environment }
42
42
 
43
+ let(:people_query) { Rasti::DB::Query.new collection_class: People, dataset: db[:people], environment: environment }
44
+
43
45
  let(:languages_query) { Rasti::DB::Query.new collection_class: Languages, dataset: custom_db[:languages], environment: environment }
44
46
 
45
47
  it 'Count' do
@@ -87,8 +89,8 @@ describe 'Query' do
87
89
  post = Post.new db[:posts].where(id: 1).first.merge(user: user, categories: categories)
88
90
 
89
91
  selected_attributes = {
90
- user: [:id],
91
- 'user.person' => [:document_number, :user_id],
92
+ user: [:id],
93
+ 'user.person' => [:document_number, :user_id],
92
94
  'user.person.languages' => [:id],
93
95
  categories: [:id]
94
96
  }
@@ -99,7 +101,7 @@ describe 'Query' do
99
101
  .all
100
102
  .must_equal [post]
101
103
  end
102
-
104
+
103
105
  it 'Exclude graph attributes' do
104
106
  language = Language.new custom_db[:languages].where(id: 1).select(:id).first
105
107
 
@@ -112,8 +114,8 @@ describe 'Query' do
112
114
  post = Post.new db[:posts].where(id: 1).first.merge(user: user, categories: categories)
113
115
 
114
116
  excluded_attributes = {
115
- user: [:name],
116
- 'user.person' => [:first_name, :last_name, :birth_date],
117
+ user: [:name],
118
+ 'user.person' => [:first_name, :last_name, :birth_date],
117
119
  'user.person.languages' => [:name],
118
120
  categories: [:name]
119
121
  }
@@ -124,7 +126,7 @@ describe 'Query' do
124
126
  .all
125
127
  .must_equal [post]
126
128
  end
127
-
129
+
128
130
  it 'All graph attributes' do
129
131
  person = Person.new db[:people].where(document_number: 'document_2').first
130
132
 
@@ -140,6 +142,30 @@ describe 'Query' do
140
142
  .must_equal [post]
141
143
  end
142
144
 
145
+ describe 'Append computed attribute' do
146
+ it 'With join' do
147
+ db[:comments].insert post_id: 1, user_id: 5, text: 'Comment 4'
148
+ users_query.append_computed_attribute(:comments_count)
149
+ .where(id: 5)
150
+ .all
151
+ .must_equal [User.new(id: 5, name: 'User 5', comments_count: 2)]
152
+ end
153
+
154
+ it 'Without join' do
155
+ person_expected = Person.new user_id: 1,
156
+ document_number: 'document_1',
157
+ first_name: 'Name 1',
158
+ last_name: 'Last Name 1',
159
+ birth_date: Date.parse('2020-04-24'),
160
+ full_name: 'Name 1 Last Name 1'
161
+
162
+ people_query.append_computed_attribute(:full_name)
163
+ .where(document_number: 'document_1')
164
+ .all
165
+ .must_equal [person_expected]
166
+ end
167
+ end
168
+
143
169
  it 'Map' do
144
170
  users_query.map(&:name).must_equal db[:users].map(:name)
145
171
  end
@@ -148,45 +174,88 @@ describe 'Query' do
148
174
  users_query.detect(id: 3).must_equal User.new(id: 3, name: 'User 3')
149
175
  end
150
176
 
177
+ describe 'Each' do
178
+
179
+ it 'without size' do
180
+ users = []
181
+
182
+ users_query.each do |user|
183
+ users << user
184
+ end
185
+
186
+ users.size.must_equal 10
187
+ users.each_with_index do |user, i|
188
+ user.must_equal User.new(id: i+1, name: "User #{i+1}")
189
+ end
190
+ end
191
+
192
+ it 'with size' do
193
+ users = []
194
+ users_query.each(batch_size: 2) do |user|
195
+ users << user
196
+ end
197
+
198
+ users.size.must_equal 10
199
+ users.each_with_index do |user, i|
200
+ user.must_equal User.new(id: i+1, name: "User #{i+1}")
201
+ end
202
+ end
203
+
204
+ end
205
+
206
+ it 'Each batch' do
207
+ users_batch = []
208
+ users_query.each_batch(size: 2) do |page|
209
+ users_batch << page
210
+ end
211
+
212
+ users_batch.size.must_equal 5
213
+ i = 1
214
+ users_batch.each do |user_page|
215
+ user_page.must_equal [User.new(id: i, name: "User #{i}"), User.new(id: i+1, name: "User #{i+1}")]
216
+ i += 2
217
+ end
218
+ end
219
+
151
220
  it 'Where' do
152
221
  users_query.where(id: 3).all.must_equal [User.new(id: 3, name: 'User 3')]
153
222
  end
154
-
223
+
155
224
  it 'Exclude' do
156
225
  users_query.exclude(id: [1,2,3,4,5,6,7,8,9]).all.must_equal [User.new(id: 10, name: 'User 10')]
157
226
  end
158
-
227
+
159
228
  it 'And' do
160
229
  users_query.where(id: [1,2]).where(name: 'User 2').all.must_equal [User.new(id: 2, name: 'User 2')]
161
230
  end
162
-
231
+
163
232
  it 'Or' do
164
233
  users_query.where(id: 1).or(name: 'User 2').all.must_equal [
165
- User.new(id: 1, name: 'User 1'),
234
+ User.new(id: 1, name: 'User 1'),
166
235
  User.new(id: 2, name: 'User 2')
167
236
  ]
168
237
  end
169
-
238
+
170
239
  it 'Order' do
171
240
  posts_query.order(:title).all.must_equal [
172
- Post.new(id: 2, user_id: 1, title: 'Another post', body: '...', language_id: 1),
173
- Post.new(id: 3, user_id: 4, title: 'Best post', body: '...', language_id: 1),
241
+ Post.new(id: 2, user_id: 1, title: 'Another post', body: '...', language_id: 1),
242
+ Post.new(id: 3, user_id: 4, title: 'Best post', body: '...', language_id: 1),
174
243
  Post.new(id: 1, user_id: 2, title: 'Sample post', body: '...', language_id: 1)
175
244
  ]
176
245
  end
177
-
246
+
178
247
  it 'Reverse order' do
179
248
  posts_query.reverse_order(:title).all.must_equal [
180
249
  Post.new(id: 1, user_id: 2, title: 'Sample post', body: '...', language_id: 1),
181
- Post.new(id: 3, user_id: 4, title: 'Best post', body: '...', language_id: 1),
250
+ Post.new(id: 3, user_id: 4, title: 'Best post', body: '...', language_id: 1),
182
251
  Post.new(id: 2, user_id: 1, title: 'Another post', body: '...', language_id: 1)
183
252
  ]
184
253
  end
185
-
254
+
186
255
  it 'Limit and offset' do
187
256
  users_query.limit(1).offset(1).all.must_equal [User.new(id: 2, name: 'User 2')]
188
257
  end
189
-
258
+
190
259
  it 'First' do
191
260
  users_query.first.must_equal User.new(id: 1, name: 'User 1')
192
261
  end
@@ -203,21 +272,21 @@ describe 'Query' do
203
272
  language = Language.new id: 1, name: 'Spanish'
204
273
 
205
274
  person = Person.new user_id: 2,
206
- document_number: 'document_2',
207
- first_name: 'Name 2',
208
- last_name: 'Last Name 2',
275
+ document_number: 'document_2',
276
+ first_name: 'Name 2',
277
+ last_name: 'Last Name 2',
209
278
  birth_date: Date.parse('2020-04-24'),
210
279
  languages: [language]
211
280
 
212
- user = User.new id: 2,
281
+ user = User.new id: 2,
213
282
  name: 'User 2',
214
283
  person: person
215
284
 
216
- post = Post.new id: 1,
217
- user_id: 2,
285
+ post = Post.new id: 1,
286
+ user_id: 2,
218
287
  user: user,
219
- title: 'Sample post',
220
- body: '...',
288
+ title: 'Sample post',
289
+ body: '...',
221
290
  language_id: 1,
222
291
  language: language
223
292
 
@@ -323,7 +392,7 @@ describe 'Query' do
323
392
  error = proc { posts_query.nql('a + b') }.must_raise Rasti::DB::NQL::InvalidExpressionError
324
393
  error.message.must_equal 'Invalid filter expression: a + b'
325
394
  end
326
-
395
+
327
396
  it 'Filter to self table' do
328
397
  posts_query.nql('user_id > 1')
329
398
  .pluck(:user_id)
@@ -350,6 +419,49 @@ describe 'Query' do
350
419
  .must_equal [2]
351
420
  end
352
421
 
422
+ describe 'Computed Attributes' do
423
+
424
+ it 'Filter relation computed attribute' do
425
+ db[:comments].insert post_id: 1, user_id: 5, text: 'Comment 4'
426
+ users_query.nql('comments_count = 2').all.must_equal [User.new(id: 5, name: 'User 5')]
427
+ end
428
+
429
+ it 'Filter with relation computed attribute with "and" combined' do
430
+ db[:comments].insert post_id: 1, user_id: 5, text: 'Comment 4'
431
+ db[:comments].insert post_id: 1, user_id: 4, text: 'Comment 3'
432
+ users_query.nql('(comments_count > 1) & (id = 5)').all.must_equal [User.new(id: 5, name: 'User 5')]
433
+ end
434
+
435
+ it 'Filter relation computed attribute with "or" combined' do
436
+ db[:comments].insert post_id: 1, user_id: 2, text: 'Comment 3'
437
+ users_query.nql('(comments_count = 2) | (id = 5)')
438
+ .order(:id)
439
+ .all
440
+ .must_equal [ User.new(id: 2, name: 'User 2'), User.new(id: 5, name: 'User 5') ]
441
+ end
442
+
443
+ it 'Filter relation computed attribute with "and" and "or" combined' do
444
+ db[:comments].insert post_id: 1, user_id: 2, text: 'Comment 3'
445
+ users_query.nql('((comments_count = 2) | (id = 5)) & (name: User 5)')
446
+ .order(:id)
447
+ .all
448
+ .must_equal [ User.new(id: 5, name: 'User 5') ]
449
+ end
450
+
451
+ it 'Filter simple computed attribute' do
452
+ person_expected = Person.new user_id: 1,
453
+ document_number: 'document_1',
454
+ first_name: 'Name 1',
455
+ last_name: 'Last Name 1',
456
+ birth_date: Date.parse('2020-04-24')
457
+
458
+ people_query.nql('full_name = Name 1 Last Name 1')
459
+ .all
460
+ .must_equal [person_expected]
461
+ end
462
+
463
+ end
464
+
353
465
  end
354
466
 
355
467
  end