rasti-db 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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