mincer 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/Gemfile +2 -1
- data/README.md +111 -64
- data/lib/mincer/action_view/sort_helper.rb +7 -7
- data/lib/mincer/base.rb +1 -1
- data/lib/mincer/config.rb +29 -0
- data/lib/mincer/core_ext/string.rb +5 -0
- data/lib/mincer/processors/cache_digest/processor.rb +54 -0
- data/lib/mincer/processors/helpers.rb +15 -0
- data/lib/mincer/processors/pagination/processor.rb +63 -0
- data/lib/mincer/processors/pg_json_dumper/processor.rb +69 -0
- data/lib/mincer/processors/pg_search/processor.rb +148 -0
- data/lib/mincer/processors/pg_search/sanitizer.rb +61 -0
- data/lib/mincer/processors/pg_search/search_engines/array.rb +41 -0
- data/lib/mincer/processors/pg_search/search_engines/base.rb +56 -0
- data/lib/mincer/processors/pg_search/search_engines/fulltext.rb +58 -0
- data/lib/mincer/processors/pg_search/search_engines/trigram.rb +41 -0
- data/lib/mincer/processors/pg_search/search_statement.rb +31 -0
- data/lib/mincer/processors/sorting/processor.rb +113 -0
- data/lib/mincer/version.rb +1 -1
- data/lib/mincer.rb +31 -31
- data/mincer.gemspec +0 -2
- data/spec/lib/mincer/action_view/sort_helper_spec.rb +11 -0
- data/spec/lib/mincer/base_spec.rb +15 -0
- data/spec/lib/mincer/config_spec.rb +7 -0
- data/spec/lib/{processors/cache_digest_spec.rb → mincer/processors/cache_digest/processor_spec.rb} +2 -9
- data/spec/lib/{processors/paginate_spec.rb → mincer/processors/pagination/processor_spec.rb} +43 -17
- data/spec/lib/{processors/pg_json_dumper_spec.rb → mincer/processors/pg_json_dumper/processor_spec.rb} +2 -6
- data/spec/lib/mincer/processors/pg_search/processor_spec.rb +268 -0
- data/spec/lib/mincer/processors/pg_search/sanitizer_spec.rb +38 -0
- data/spec/lib/mincer/processors/pg_search/search_engines/array_spec.rb +83 -0
- data/spec/lib/mincer/processors/pg_search/search_engines/fulltext_spec.rb +101 -0
- data/spec/lib/mincer/processors/pg_search/search_engines/trigram_spec.rb +91 -0
- data/spec/lib/mincer/processors/sorting/processor_spec.rb +181 -0
- data/spec/mincer_config.rb +38 -0
- data/spec/spec_helper.rb +40 -4
- data/spec/support/postgres_adapter.rb +12 -3
- data/spec/support/sqlite3_adapter.rb +3 -0
- metadata +42 -45
- data/lib/mincer/processors/cache_digest.rb +0 -50
- data/lib/mincer/processors/paginate.rb +0 -41
- data/lib/mincer/processors/pg_json_dumper.rb +0 -51
- data/lib/mincer/processors/search.rb +0 -34
- data/lib/mincer/processors/sort.rb +0 -59
- data/spec/lib/processors/search_spec.rb +0 -77
- data/spec/lib/processors/sort_spec.rb +0 -77
data/spec/lib/{processors/cache_digest_spec.rb → mincer/processors/cache_digest/processor_spec.rb}
RENAMED
@@ -1,11 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe ::Mincer::Processors::CacheDigest do
|
3
|
+
describe ::Mincer::Processors::CacheDigest::Processor do
|
4
4
|
context 'when postgres used' do
|
5
5
|
before do
|
6
|
-
|
7
|
-
class ActiveRecordModel < ActiveRecord::Base
|
8
|
-
end
|
6
|
+
setup_postgres_table
|
9
7
|
ActiveRecordModel.create!(text: 'Test1')
|
10
8
|
ActiveRecordModel.create!(text: 'Test2')
|
11
9
|
end
|
@@ -26,9 +24,4 @@ describe ::Mincer::Processors::CacheDigest do
|
|
26
24
|
end
|
27
25
|
end
|
28
26
|
|
29
|
-
|
30
|
-
context 'when postgres used' do
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
27
|
end
|
data/spec/lib/{processors/paginate_spec.rb → mincer/processors/pagination/processor_spec.rb}
RENAMED
@@ -1,10 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe ::Mincer::Processors::
|
3
|
+
describe ::Mincer::Processors::Pagination::Processor do
|
4
4
|
before do
|
5
5
|
setup_basic_sqlite3_table
|
6
|
-
class ActiveRecordModel < ActiveRecord::Base
|
7
|
-
end
|
8
6
|
30.times { |i| ActiveRecordModel.create(text: i) }
|
9
7
|
end
|
10
8
|
|
@@ -39,10 +37,7 @@ describe ::Mincer::Processors::Paginate do
|
|
39
37
|
|
40
38
|
context 'when WillPaginate is used for pagination' do
|
41
39
|
before do
|
42
|
-
|
43
|
-
#::Mincer::Processors::Paginator.any_instance.stub(:kaminari?).and_return(false)
|
44
|
-
#require 'will_paginate'
|
45
|
-
#require 'will_paginate/active_record'
|
40
|
+
::Mincer::Processors::Pagination::Processor.stub(:kaminari?).and_return(false)
|
46
41
|
end
|
47
42
|
|
48
43
|
describe 'paginating with basic model without any Mincer::Base configuration' do
|
@@ -51,17 +46,11 @@ describe ::Mincer::Processors::Paginate do
|
|
51
46
|
end
|
52
47
|
|
53
48
|
it 'paginates by with provided page and per_page in args' do
|
54
|
-
|
55
|
-
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'paginates by default page(1) and per_page(10) when nothing passed to args' do
|
59
|
-
query = subject.new(ActiveRecordModel)
|
60
|
-
query.to_a.count.should eq(25)
|
49
|
+
ActiveRecord::Relation.any_instance.should_receive(:paginate).with(page: '2', per_page: '20')
|
50
|
+
subject.new(ActiveRecordModel, { 'page' => '2', 'per_page' => '20' })
|
61
51
|
end
|
62
52
|
end
|
63
53
|
|
64
|
-
|
65
54
|
describe 'paginating when basic model has disabled pagination' do
|
66
55
|
it 'returns all items' do
|
67
56
|
subject = Class.new(Mincer::Base) do
|
@@ -76,8 +65,8 @@ describe ::Mincer::Processors::Paginate do
|
|
76
65
|
context 'when there is no gem for pagination in loaded' do
|
77
66
|
it 'returns all items' do
|
78
67
|
subject = Class.new(Mincer::Base)
|
79
|
-
::Mincer::Processors::
|
80
|
-
::Mincer::Processors::
|
68
|
+
::Mincer::Processors::Pagination::Processor.stub(:kaminari?).and_return(false)
|
69
|
+
::Mincer::Processors::Pagination::Processor.stub(:will_paginate?).and_return(false)
|
81
70
|
|
82
71
|
query = subject.new(ActiveRecordModel)
|
83
72
|
query.to_a.count.should eq(30)
|
@@ -85,4 +74,41 @@ describe ::Mincer::Processors::Paginate do
|
|
85
74
|
|
86
75
|
end
|
87
76
|
|
77
|
+
|
78
|
+
describe 'configuration of pg_search' do
|
79
|
+
before do
|
80
|
+
Mincer.config.instance_variable_set('@pagination', nil)
|
81
|
+
end
|
82
|
+
|
83
|
+
describe 'param_name' do
|
84
|
+
it 'uses "page" as default value for page_param_name' do
|
85
|
+
Mincer.config.pagination.page_param_name.should == :page
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'sets param_name string' do
|
89
|
+
Mincer.configure do |config|
|
90
|
+
config.pagination do |search|
|
91
|
+
search.page_param_name = 's'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
Mincer.config.pagination.page_param_name.should == 's'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe 'param_name' do
|
99
|
+
it 'uses :per_page as default value for per_page_param_name' do
|
100
|
+
Mincer.config.pagination.per_page_param_name.should == :per_page
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'sets param_name string' do
|
104
|
+
Mincer.configure do |config|
|
105
|
+
config.pagination do |search|
|
106
|
+
search.per_page_param_name = 's'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
Mincer.config.pagination.per_page_param_name.should == 's'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
88
114
|
end
|
@@ -1,11 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe ::Mincer::Processors::PgJsonDumper do
|
3
|
+
describe ::Mincer::Processors::PgJsonDumper::Processor do
|
4
4
|
context 'when postgres is used' do
|
5
5
|
before do
|
6
|
-
|
7
|
-
class ActiveRecordModel < ActiveRecord::Base
|
8
|
-
end
|
6
|
+
setup_postgres_table
|
9
7
|
ActiveRecordModel.create!(text: 'Test1')
|
10
8
|
ActiveRecordModel.create!(text: 'Test2')
|
11
9
|
end
|
@@ -48,8 +46,6 @@ describe ::Mincer::Processors::PgJsonDumper do
|
|
48
46
|
context 'when postgres is NOT used' do
|
49
47
|
before do
|
50
48
|
setup_basic_sqlite3_table
|
51
|
-
class ActiveRecordModel < ActiveRecord::Base
|
52
|
-
end
|
53
49
|
ActiveRecordModel.create!(id: 1, text: 'Test1')
|
54
50
|
ActiveRecordModel.create!(id: 2, text: 'Test2')
|
55
51
|
end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ::Mincer::Processors::PgSearch::Processor do
|
4
|
+
context 'when postgres used' do
|
5
|
+
before do
|
6
|
+
setup_postgres_table
|
7
|
+
ActiveRecordModel.create!(text: 'Test')
|
8
|
+
ActiveRecordModel.create!(text: 'Bingo')
|
9
|
+
ActiveRecordModel.create!(text: 'Bongo')
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'config' do
|
13
|
+
it 'defines method with pg_options' do
|
14
|
+
subject = Class.new(Mincer::Base) do
|
15
|
+
pg_search [{ columns: %w{"active_record_models"."tags" }, engines: [:array] }]
|
16
|
+
end
|
17
|
+
query = subject.new(ActiveRecordModel)
|
18
|
+
query.send(:pg_search_params).should == [{ columns: %w{"active_record_models"."tags" }, engines: [:array] }]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'searching' do
|
23
|
+
describe 'searches with basic model without any Mincer::Base configuration' do
|
24
|
+
subject(:model) do
|
25
|
+
Class.new(Mincer::Base)
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'searching with fulltext search' do
|
29
|
+
it 'searches by pattern in args' do
|
30
|
+
query = subject.new(ActiveRecordModel, { 'pattern' => 'Bingo' })
|
31
|
+
query.to_a.count.should eq(1)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'avoids search when pattern is an empty string or spaces' do
|
35
|
+
query = subject.new(ActiveRecordModel, { 'pattern' => ' ' })
|
36
|
+
|
37
|
+
query.to_a.count.should eq(3)
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'when another search_column exists with nil value on a found item' do
|
41
|
+
before do
|
42
|
+
setup_postgres_table([['id', 'SERIAL PRIMARY KEY'], ['text', 'TEXT'], ['text2', 'TEXT']])
|
43
|
+
ActiveRecordModel.create!(text: 'Test')
|
44
|
+
ActiveRecordModel.create!(text: 'Bingo')
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'still includes found item in results' do
|
48
|
+
query = subject.new(ActiveRecordModel, { 'pattern' => 'Bingo' })
|
49
|
+
query.to_a.count.should eq(1)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'searching with 2 statements' do
|
54
|
+
before do
|
55
|
+
setup_postgres_table([['id', 'SERIAL PRIMARY KEY'], ['text', 'TEXT'], ['tags', 'TEXT[]']])
|
56
|
+
ActiveRecordModel.create!(text: 'Test', tags: ['a', 'b'])
|
57
|
+
ActiveRecordModel.create!(text: 'Bingo', tags: ['b', 'c'])
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'searches using 2 statements' do
|
61
|
+
subject = Class.new(Mincer::Base) do
|
62
|
+
pg_search [
|
63
|
+
{ :columns => %w{"active_record_models"."tags" }, engines: [:array] },
|
64
|
+
{ :columns => %w{"active_record_models"."text" }, engines: [:fulltext] }
|
65
|
+
]
|
66
|
+
end
|
67
|
+
query = subject.new(ActiveRecordModel, { 'pattern' => 'c' })
|
68
|
+
query.to_a.count.should eq(1)
|
69
|
+
query.to_a.first.text.should == 'Bingo'
|
70
|
+
|
71
|
+
query = subject.new(ActiveRecordModel, { 'pattern' => 'Test' })
|
72
|
+
query.to_a.count.should eq(1)
|
73
|
+
query.to_a.first.text.should == 'Test'
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'searches using 2 statements with aggregator set to :and' do
|
77
|
+
subject = Class.new(Mincer::Base) do
|
78
|
+
pg_search [
|
79
|
+
{ :columns => %w{"active_record_models"."tags" }, engines: [:array] },
|
80
|
+
{ :columns => %w{"active_record_models"."text" }, engines: [:fulltext] }
|
81
|
+
], join_with: :and
|
82
|
+
end
|
83
|
+
|
84
|
+
ActiveRecordModel.create!(text: 'O', tags: ['O'])
|
85
|
+
|
86
|
+
query = subject.new(ActiveRecordModel, { 'pattern' => 'O' })
|
87
|
+
query.to_a.count.should eq(1)
|
88
|
+
query.to_a.first.text.should == 'O'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
describe 'searching with array' do
|
94
|
+
before do
|
95
|
+
setup_postgres_table([['id', 'SERIAL PRIMARY KEY'], ['text', 'TEXT'], ['tags', 'TEXT[]']])
|
96
|
+
ActiveRecordModel.create!(text: 'Test', tags: ['a', 'b'])
|
97
|
+
ActiveRecordModel.create!(text: 'Bingo', tags: ['b', 'c'])
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'includes 2 items when both items include pattern' do
|
101
|
+
subject = Class.new(Mincer::Base) do
|
102
|
+
pg_search [{ :columns => %w{"active_record_models"."tags" }, engines: [:array] }]
|
103
|
+
end
|
104
|
+
query = subject.new(ActiveRecordModel, { 'pattern' => 'b' })
|
105
|
+
query.to_a.count.should eq(2)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'includes 1 item when match was found on one item' do
|
109
|
+
subject = Class.new(Mincer::Base) do
|
110
|
+
pg_search [{ :columns => %w{"active_record_models"."tags" }, engines: [:array] }]
|
111
|
+
end
|
112
|
+
query = subject.new(ActiveRecordModel, { 'pattern' => 'c' })
|
113
|
+
query.to_a.count.should eq(1)
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'includes both when matched with array overlap and option "any_word" set to true' do
|
117
|
+
subject = Class.new(Mincer::Base) do
|
118
|
+
pg_search [{ :columns => %w{"active_record_models"."tags" }, engines: [:array], any_word: true }]
|
119
|
+
end
|
120
|
+
query = subject.new(ActiveRecordModel, { 'pattern' => 'a c d' })
|
121
|
+
query.to_a.count.should eq(2)
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'includes both when matched with array overlap and option "any_word" set to true(separated with ",")' do
|
125
|
+
subject = Class.new(Mincer::Base) do
|
126
|
+
def pg_search_params
|
127
|
+
[{ :columns => %w{"active_record_models"."tags"}, engines: [:array], any_word: true }]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
query = subject.new(ActiveRecordModel, { 'pattern' => 'a, c,d' })
|
131
|
+
query.to_a.count.should eq(2)
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'includes no items when nothing matched pattern' do
|
135
|
+
subject = Class.new(Mincer::Base) do
|
136
|
+
def pg_search_params
|
137
|
+
[{ :columns => %w{"active_record_models"."tags" }, engines: [:array] }]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
query = subject.new(ActiveRecordModel, { 'pattern' => 'd e' })
|
141
|
+
query.to_a.count.should eq(0)
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
describe 'searching when basic model has disabled search' do
|
150
|
+
it 'does not modifies relation' do
|
151
|
+
subject = Class.new(Mincer::Base) do
|
152
|
+
skip_search!
|
153
|
+
end
|
154
|
+
query = subject.new(ActiveRecordModel, { 'pattern' => 'Bingo' })
|
155
|
+
query.to_a.count.should eq(3)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context 'when postgres is NOT used' do
|
162
|
+
before do
|
163
|
+
setup_basic_sqlite3_table
|
164
|
+
ActiveRecordModel.create!(text: 'Test')
|
165
|
+
ActiveRecordModel.create!(text: 'Bingo')
|
166
|
+
ActiveRecordModel.create!(text: 'Bongo')
|
167
|
+
end
|
168
|
+
|
169
|
+
subject(:model) do
|
170
|
+
Class.new(Mincer::Base)
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'returns all records' do
|
174
|
+
query = subject.new(ActiveRecordModel, { 'pattern' => 'Bingo' })
|
175
|
+
query.to_a.count.should eq(3)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
describe 'configuration of pg_search' do
|
181
|
+
before do
|
182
|
+
Mincer.config.instance_variable_set('@pg_search', nil)
|
183
|
+
end
|
184
|
+
|
185
|
+
describe 'param_name' do
|
186
|
+
it 'uses "pattern" as default value for param_name' do
|
187
|
+
Mincer.config.pg_search.param_name.should == 'pattern'
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'sets param_name string' do
|
191
|
+
Mincer.configure do |config|
|
192
|
+
config.pg_search do |search|
|
193
|
+
search.param_name = 's'
|
194
|
+
end
|
195
|
+
end
|
196
|
+
Mincer.config.pg_search.param_name.should == 's'
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe 'fulltext_engine' do
|
201
|
+
it 'sets "ignore_accent" to true as default value' do
|
202
|
+
Mincer.config.pg_search.fulltext_engine[:ignore_accent].should be_true
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'sets "any_word" to false as default value' do
|
206
|
+
Mincer.config.pg_search.fulltext_engine[:any_word].should be_false
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'sets "dictionary" to "simple" as default value' do
|
210
|
+
Mincer.config.pg_search.fulltext_engine[:dictionary].should == :simple
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'sets "ignore_case" to "false" as default value' do
|
214
|
+
Mincer.config.pg_search.fulltext_engine[:ignore_case].should be_false
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'sets fulltext_engine options while merging with defaults' do
|
218
|
+
Mincer.configure do |config|
|
219
|
+
config.pg_search do |search|
|
220
|
+
search.fulltext_engine = search.fulltext_engine.merge(ignore_accent: false)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
Mincer.config.pg_search.fulltext_engine.should == { ignore_accent: false, any_word: false, dictionary: :simple, ignore_case: false }
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe 'trigram_engine' do
|
228
|
+
it 'sets "ignore_accent" to true as default value' do
|
229
|
+
Mincer.config.pg_search.trigram_engine[:ignore_accent].should be_true
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'sets "threshold" to 0.3 as default value' do
|
233
|
+
Mincer.config.pg_search.trigram_engine[:threshold].should == 0.3
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'sets trigram_engine options while merging with defaults' do
|
237
|
+
Mincer.configure do |config|
|
238
|
+
config.pg_search do |search|
|
239
|
+
search.trigram_engine = search.trigram_engine.merge(threshold: 0.5)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
Mincer.config.pg_search.trigram_engine.should == { ignore_accent: true, threshold: 0.5 }
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
describe 'array_engine' do
|
247
|
+
it 'sets "ignore_accent" to true as default value' do
|
248
|
+
Mincer.config.pg_search.array_engine[:ignore_accent].should be_true
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'sets "any_word" to false as default value' do
|
252
|
+
Mincer.config.pg_search.array_engine[:any_word].should be_true
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'sets array_engine options while merging with defaults' do
|
256
|
+
Mincer.configure do |config|
|
257
|
+
config.pg_search do |search|
|
258
|
+
search.array_engine = search.array_engine.merge(any_word: false)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
Mincer.config.pg_search.array_engine.should == { ignore_accent: true, any_word: false }
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
|
268
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ::Mincer::Processors::PgSearch::Sanitizer do
|
4
|
+
before do
|
5
|
+
setup_postgres_table
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#sanitize' do
|
9
|
+
subject { ::Mincer::Processors::PgSearch::Sanitizer }
|
10
|
+
it 'applies "ignore_case" option' do
|
11
|
+
subject.sanitize_string('text', :ignore_case).to_sql.should == "lower('text')"
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'applies "ignore_accent" option' do
|
15
|
+
subject.sanitize_string('text', :ignore_accent).to_sql.should == "unaccent('text')"
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'coalesce option' do
|
19
|
+
context 'when postgres extension installed' do
|
20
|
+
it 'applies "coalesce" option' do
|
21
|
+
subject.sanitize_string('text', :coalesce).to_sql.should == "coalesce('text', '')"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
context 'when postgres extension is unavailable' do
|
25
|
+
it 'applies "coalesce" option' do
|
26
|
+
Mincer.instance_variable_get('@installed_extensions')[:unaccent] = false
|
27
|
+
subject.sanitize_string('text', :coalesce).to_sql.should == 'text'
|
28
|
+
Mincer.instance_variable_get('@installed_extensions')[:unaccent] = true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'applies multiple sanitizers' do
|
34
|
+
subject.sanitize_string('text', :ignore_case, :ignore_accent, :coalesce).to_sql.should == "unaccent(lower(coalesce('text', '')))"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ::Mincer::PgSearch::SearchEngines::Array do
|
4
|
+
before do
|
5
|
+
setup_postgres_table
|
6
|
+
end
|
7
|
+
subject(:search_engine_class) { ::Mincer::PgSearch::SearchEngines::Array }
|
8
|
+
let(:search_statement_class) { ::Mincer::Processors::PgSearch::SearchStatement }
|
9
|
+
|
10
|
+
describe '.search_engine_statements' do
|
11
|
+
context 'when 2 columns' do
|
12
|
+
it 'stores them in instance variable hash under :or key' do
|
13
|
+
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:array])
|
14
|
+
search_statement2 = search_statement_class.new(['"records"."text2"'], engines: [:array])
|
15
|
+
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1, search_statement2])
|
16
|
+
search_engine.send(:search_engine_statements).should include(search_statement1)
|
17
|
+
search_engine.send(:search_engine_statements).should include(search_statement2)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'ignores other engines' do
|
22
|
+
search_statement1 = search_statement_class.new(['"records"."text"'])
|
23
|
+
search_statement2 = search_statement_class.new(['"records"."text2"'], engines: [:trigram])
|
24
|
+
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1, search_statement2])
|
25
|
+
search_engine.send(:search_engine_statements).should == []
|
26
|
+
search_engine.send(:search_engine_statements).should == []
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '.conditions' do
|
31
|
+
it 'generates search condition with one column, one term and no options' do
|
32
|
+
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:array])
|
33
|
+
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1])
|
34
|
+
search_engine.conditions.to_sql.should == %{((("records"."text"::text[]) @> ARRAY['search']))}
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'generates search condition with two columns, one term and no options' do
|
38
|
+
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:array])
|
39
|
+
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1])
|
40
|
+
search_engine.conditions.to_sql.should == %{((("records"."text"::text[] || "records"."text2"::text[]) @> ARRAY['search']))}
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'generates search condition with two columns, two terms and no options' do
|
44
|
+
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:array])
|
45
|
+
search_engine = search_engine_class.new({ pattern: 'search word' }, [search_statement1])
|
46
|
+
search_engine.conditions.to_sql.should == %{((("records"."text"::text[] || "records"."text2"::text[]) @> ARRAY['search','word']))}
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'generates search condition with two columns, two terms and option "ignore_accent" set to true ' do
|
50
|
+
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:array], ignore_accent: true)
|
51
|
+
search_engine = search_engine_class.new({ pattern: 'search word' }, [search_statement1])
|
52
|
+
search_engine.conditions.to_sql.should == %{((("records"."text"::text[] || "records"."text2"::text[]) @> ARRAY[unaccent('search'),unaccent('word')]))}
|
53
|
+
end
|
54
|
+
|
55
|
+
#TODO: sanitizer can not be set on array columns since we ned to unpack an reconstruct those arrays. Find a solution
|
56
|
+
it 'generates search condition with two columns, two terms and option "any_word" set to true ' do
|
57
|
+
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:array], any_word: true)
|
58
|
+
search_engine = search_engine_class.new({ pattern: 'search word' }, [search_statement1])
|
59
|
+
search_engine.conditions.to_sql.should == %{((("records"."text"::text[] || "records"."text2"::text[]) && ARRAY['search','word']))}
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'generates search condition with two columns, two terms and option "ignore_accent" and "ignore_case" set to true ' do
|
63
|
+
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:array], ignore_accent: true, ignore_case: true)
|
64
|
+
search_engine = search_engine_class.new({ pattern: 'search word' }, [search_statement1])
|
65
|
+
search_engine.conditions.to_sql.should == %{((("records"."text"::text[] || "records"."text2"::text[]) @> ARRAY[unaccent(lower('search')),unaccent(lower('word'))]))}
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'generates search condition with one column, one term, two statements and no options' do
|
69
|
+
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:array])
|
70
|
+
search_statement2 = search_statement_class.new(['"records"."text2"'], engines: [:array], any_word: true)
|
71
|
+
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1, search_statement2])
|
72
|
+
search_engine.conditions.to_sql.should == %{((("records"."text"::text[]) @> ARRAY['search']) OR (("records"."text2"::text[]) && ARRAY['search']))}
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'generates search condition with one column, one term, two statements and no "param_name" options set to "s"' do
|
76
|
+
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:array])
|
77
|
+
search_statement2 = search_statement_class.new(['"records"."text2"'], engines: [:array], any_word: true, param_name: 's')
|
78
|
+
search_engine = search_engine_class.new({ pattern: 'search', s: 'word' }, [search_statement1, search_statement2])
|
79
|
+
search_engine.conditions.to_sql.should == %{((("records"."text"::text[]) @> ARRAY['search']) OR (("records"."text2"::text[]) && ARRAY['word']))}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ::Mincer::PgSearch::SearchEngines::Fulltext do
|
4
|
+
before do
|
5
|
+
setup_postgres_table
|
6
|
+
end
|
7
|
+
subject(:search_engine_class) { ::Mincer::PgSearch::SearchEngines::Fulltext }
|
8
|
+
let(:search_statement_class) { ::Mincer::Processors::PgSearch::SearchStatement }
|
9
|
+
|
10
|
+
describe '.search_engine_statements' do
|
11
|
+
context 'when 2 columns' do
|
12
|
+
it 'stores them in instance variable hash under :or key' do
|
13
|
+
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:fulltext])
|
14
|
+
search_statement2 = search_statement_class.new(['"records"."text2"'], engines: [:fulltext])
|
15
|
+
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1, search_statement2])
|
16
|
+
search_engine.send(:search_engine_statements).should include(search_statement1)
|
17
|
+
search_engine.send(:search_engine_statements).should include(search_statement2)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'ignores other engines' do
|
22
|
+
search_statement1 = search_statement_class.new(['"records"."text"'])
|
23
|
+
search_statement2 = search_statement_class.new(['"records"."text2"'], engines: [:array])
|
24
|
+
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1, search_statement2])
|
25
|
+
search_engine.send(:search_engine_statements).should == []
|
26
|
+
search_engine.send(:search_engine_statements).should == []
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '.conditions' do
|
31
|
+
it 'generates search condition with one column, one term and no options with columns wrapped with coalesce' do
|
32
|
+
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:fulltext])
|
33
|
+
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1])
|
34
|
+
search_engine.conditions.to_sql.should == %{(((to_tsvector('simple', coalesce("records"."text", ''))) @@ (to_tsquery('simple', 'search'))))}
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'generates search condition with two columns, one term and no options' do
|
38
|
+
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:fulltext])
|
39
|
+
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1])
|
40
|
+
search_engine.conditions.to_sql.should == %{(((to_tsvector('simple', coalesce("records"."text", '')) || to_tsvector('simple', coalesce("records"."text2", ''))) @@ (to_tsquery('simple', 'search'))))}
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'generates search condition with two columns, two terms and no options' do
|
44
|
+
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:fulltext])
|
45
|
+
search_engine = search_engine_class.new({ pattern: 'search word' }, [search_statement1])
|
46
|
+
search_engine.conditions.to_sql.should == %{(((to_tsvector('simple', coalesce("records"."text", '')) || to_tsvector('simple', coalesce("records"."text2", ''))) @@ (to_tsquery('simple', 'search' || ' & ' || 'word'))))}
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'generates search condition with two columns, two terms and option "any_word" set to true ' do
|
50
|
+
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:fulltext], any_word: true)
|
51
|
+
search_engine = search_engine_class.new({ pattern: 'search word' }, [search_statement1])
|
52
|
+
search_engine.conditions.to_sql.should == %{(((to_tsvector('simple', coalesce("records"."text", '')) || to_tsvector('simple', coalesce("records"."text2", ''))) @@ (to_tsquery('simple', 'search' || ' | ' || 'word'))))}
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'generates search condition with two columns, two terms and option "any_word" set to true while escaping special characters' do
|
56
|
+
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:fulltext], any_word: true)
|
57
|
+
search_engine = search_engine_class.new({ pattern: 'search word!(:&|) !' }, [search_statement1])
|
58
|
+
search_engine.conditions.to_sql.should == %{(((to_tsvector('simple', coalesce("records"."text", '')) || to_tsvector('simple', coalesce("records"."text2", ''))) @@ (to_tsquery('simple', 'search' || ' | ' || 'word'))))}
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'generates search condition with two columns, two terms and option "ignore_accent" set to true ' do
|
62
|
+
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:fulltext], ignore_accent: true)
|
63
|
+
search_engine = search_engine_class.new({ pattern: 'search word' }, [search_statement1])
|
64
|
+
search_engine.conditions.to_sql.should == %{(((to_tsvector('simple', unaccent(coalesce("records"."text", ''))) || to_tsvector('simple', unaccent(coalesce("records"."text2", '')))) @@ (to_tsquery('simple', unaccent('search') || ' & ' || unaccent('word')))))}
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'generates search condition with two columns, two terms and option "ignore_accent" and "ignore_case" set to true ' do
|
68
|
+
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:fulltext], ignore_accent: true, ignore_case: true)
|
69
|
+
search_engine = search_engine_class.new({ pattern: 'search word' }, [search_statement1])
|
70
|
+
search_engine.conditions.to_sql.should == %{(((to_tsvector('simple', unaccent(lower(coalesce("records"."text", '')))) || to_tsvector('simple', unaccent(lower(coalesce("records"."text2", ''))))) @@ (to_tsquery('simple', unaccent(lower('search')) || ' & ' || unaccent(lower('word'))))))}
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'generates search condition with one column, one term and option "dictionary" set to :english' do
|
74
|
+
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:fulltext], dictionary: :english)
|
75
|
+
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1])
|
76
|
+
search_engine.conditions.to_sql.should == %{(((to_tsvector('english', coalesce("records"."text", ''))) @@ (to_tsquery('english', 'search'))))}
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'generates search condition with two search statements one column, one term and no options' do
|
80
|
+
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:fulltext])
|
81
|
+
search_statement2 = search_statement_class.new(['"records"."text2"'], engines: [:fulltext])
|
82
|
+
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1, search_statement2])
|
83
|
+
search_engine.conditions.to_sql.should == %{(((to_tsvector('simple', coalesce("records"."text", ''))) @@ (to_tsquery('simple', 'search'))) OR ((to_tsvector('simple', coalesce("records"."text2", ''))) @@ (to_tsquery('simple', 'search'))))}
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'generates search condition with one column, one term, two statements and no options' do
|
87
|
+
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:fulltext])
|
88
|
+
search_statement2 = search_statement_class.new(['"records"."text2"'], engines: [:fulltext])
|
89
|
+
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1, search_statement2])
|
90
|
+
search_engine.conditions.to_sql.should == %{(((to_tsvector('simple', coalesce("records"."text", ''))) @@ (to_tsquery('simple', 'search'))) OR ((to_tsvector('simple', coalesce("records"."text2", ''))) @@ (to_tsquery('simple', 'search'))))}
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'generates search condition with one column, one term, two statements and "param_name" option set to "s"' do
|
94
|
+
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:fulltext])
|
95
|
+
search_statement2 = search_statement_class.new(['"records"."text2"'], engines: [:fulltext], param_name: 's')
|
96
|
+
search_engine = search_engine_class.new({ pattern: 'search', s: 'word' }, [search_statement1, search_statement2])
|
97
|
+
search_engine.conditions.to_sql.should == %{(((to_tsvector('simple', coalesce("records"."text", ''))) @@ (to_tsquery('simple', 'search'))) OR ((to_tsvector('simple', coalesce("records"."text2", ''))) @@ (to_tsquery('simple', 'word'))))}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|