mincer 0.1.2 → 0.2.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/.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
|