/
pg_search_spec.rb
281 lines (233 loc) · 9.27 KB
/
pg_search_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
require "spec_helper"
# For AR 5 and greater, the association reflection's cache needs be cleared
# because we're stubbing the related constants.
class << PgSearch::Document
if ActiveRecord::VERSION::MAJOR >= 5
def clear_searchable_cache
reflect_on_association(:searchable).clear_association_scope_cache
end
else
def clear_searchable_cache
end
end
end
describe PgSearch do
describe ".multisearch" do
with_table "pg_search_documents", {}, &DOCUMENTS_SCHEMA
describe "delegation to PgSearch::Document.search" do
subject { PgSearch.multisearch(query) }
let(:query) { double(:query) }
let(:relation) { double(:relation) }
before do
expect(PgSearch::Document).to receive(:search).with(query).and_return(relation)
end
it { is_expected.to eq(relation) }
end
context "with PgSearch.multisearch_options set to a Hash" do
before { allow(PgSearch).to receive(:multisearch_options).and_return(:using => :dmetaphone) }
subject do
PgSearch::Document.clear_searchable_cache
PgSearch.multisearch(query).map(&:searchable)
end
with_model :MultisearchableModel do
table do |t|
t.string :title
end
model do
include PgSearch
multisearchable :against => :title
end
end
let!(:soundalike_record) { MultisearchableModel.create!(:title => 'foning') }
let(:query) { "Phoning" }
it { is_expected.to include(soundalike_record) }
end
context "with PgSearch.multisearch_options set to a Proc" do
subject do
PgSearch::Document.clear_searchable_cache
PgSearch.multisearch(query, soundalike).map(&:searchable)
end
before do
allow(PgSearch).to receive(:multisearch_options) do
lambda do |query, soundalike|
if soundalike
{:using => :dmetaphone, :query => query}
else
{:query => query}
end
end
end
end
with_model :MultisearchableModel do
table do |t|
t.string :title
end
model do
include PgSearch
multisearchable :against => :title
end
end
let!(:soundalike_record) { MultisearchableModel.create!(:title => 'foning') }
let(:query) { "Phoning" }
context "with soundalike true" do
let(:soundalike) { true }
it { is_expected.to include(soundalike_record) }
end
context "with soundalike false" do
let(:soundalike) { false }
it { is_expected.not_to include(soundalike_record) }
end
end
context "on an STI subclass" do
context "with standard type column" do
with_model :SuperclassModel do
table do |t|
t.text 'content'
t.string 'type'
end
end
before do
searchable_subclass_model = Class.new(SuperclassModel) do
include PgSearch
multisearchable :against => :content
end
stub_const("SearchableSubclassModel", searchable_subclass_model)
stub_const("AnotherSearchableSubclassModel", searchable_subclass_model)
stub_const("NonSearchableSubclassModel", Class.new(SuperclassModel))
end
it "returns only results for that subclass" do
included = SearchableSubclassModel.create!(:content => "foo bar")
SearchableSubclassModel.create!(:content => "baz")
SuperclassModel.create!(:content => "foo bar")
SuperclassModel.create!(:content => "baz")
NonSearchableSubclassModel.create!(:content => "foo bar")
NonSearchableSubclassModel.create!(:content => "baz")
expect(SuperclassModel.count).to be 6
expect(SearchableSubclassModel.count).to be 2
expect(PgSearch::Document.count).to be 2
results = PgSearch.multisearch("foo bar")
expect(results).to eq [included.pg_search_document]
end
it "updates an existing STI model does not create a new pg_search document" do
model = SearchableSubclassModel.create!(:content => "foo bar")
expect(SearchableSubclassModel.count).to eq(1)
# We fetch the model from the database again otherwise
# the pg_search_document from the cache is used.
model = SearchableSubclassModel.find(model.id)
model.content = "foo"
model.save!
results = PgSearch.multisearch("foo")
expect(results.size).to eq(SearchableSubclassModel.count)
end
it "reindexing works" do
NonSearchableSubclassModel.create!(:content => "foo bar")
NonSearchableSubclassModel.create!(:content => "baz")
expected = SearchableSubclassModel.create!(:content => "baz")
SuperclassModel.create!(:content => "foo bar")
SuperclassModel.create!(:content => "baz")
SuperclassModel.create!(:content => "baz2")
expect(SuperclassModel.count).to be 6
expect(NonSearchableSubclassModel.count).to be 2
expect(SearchableSubclassModel.count).to be 1
expect(PgSearch::Document.count).to be 1
PgSearch::Multisearch.rebuild(SearchableSubclassModel)
PgSearch::Document.clear_searchable_cache
expect(PgSearch::Document.count).to be 1
expect(PgSearch::Document.first.searchable.class).to be SearchableSubclassModel
expect(PgSearch::Document.first.searchable).to eq expected
end
it "reindexing searchable STI doesn't clobber other related STI models" do
SearchableSubclassModel.create!(:content => "baz")
AnotherSearchableSubclassModel.create!(:content => "baz")
expect(PgSearch::Document.count).to be 2
PgSearch::Multisearch.rebuild(SearchableSubclassModel)
expect(PgSearch::Document.count).to be 2
PgSearch::Document.clear_searchable_cache
classes = PgSearch::Document.all.collect {|d| d.searchable.class }
expect(classes).to include SearchableSubclassModel
expect(classes).to include AnotherSearchableSubclassModel
end
end
context "with custom type column" do
with_model :SuperclassModel do
table do |t|
t.text 'content'
t.string 'inherit'
end
model do
self.inheritance_column = 'inherit'
end
end
before do
searchable_subclass_model = Class.new(SuperclassModel) do
include PgSearch
multisearchable :against => :content
end
stub_const("SearchableSubclassModel", searchable_subclass_model)
stub_const("AnotherSearchableSubclassModel", searchable_subclass_model)
stub_const("NonSearchableSubclassModel", Class.new(SuperclassModel))
end
it "returns only results for that subclass" do
included = SearchableSubclassModel.create!(:content => "foo bar")
SearchableSubclassModel.create!(:content => "baz")
SuperclassModel.create!(:content => "foo bar")
SuperclassModel.create!(:content => "baz")
NonSearchableSubclassModel.create!(:content => "foo bar")
NonSearchableSubclassModel.create!(:content => "baz")
expect(SuperclassModel.count).to be 6
expect(SearchableSubclassModel.count).to be 2
expect(PgSearch::Document.count).to be 2
results = PgSearch.multisearch("foo bar")
expect(results).to eq [included.pg_search_document]
end
end
end
end
describe ".disable_multisearch" do
it "should temporarily disable multisearch" do
@multisearch_enabled_before = PgSearch.multisearch_enabled?
PgSearch.disable_multisearch do
@multisearch_enabled_inside = PgSearch.multisearch_enabled?
end
@multisearch_enabled_after = PgSearch.multisearch_enabled?
expect(@multisearch_enabled_before).to be(true)
expect(@multisearch_enabled_inside).to be(false)
expect(@multisearch_enabled_after).to be(true)
end
it "should reenable multisearch after an error" do
@multisearch_enabled_before = PgSearch.multisearch_enabled?
begin
PgSearch.disable_multisearch do
@multisearch_enabled_inside = PgSearch.multisearch_enabled?
raise
end
rescue StandardError
end
@multisearch_enabled_after = PgSearch.multisearch_enabled?
expect(@multisearch_enabled_before).to be(true)
expect(@multisearch_enabled_inside).to be(false)
expect(@multisearch_enabled_after).to be(true)
end
it "should not disable multisearch on other threads" do
values = Queue.new
sync = Queue.new
Thread.new do
values.push PgSearch.multisearch_enabled?
sync.pop # wait
values.push PgSearch.multisearch_enabled?
sync.pop # wait
values.push PgSearch.multisearch_enabled?
end
@multisearch_enabled_before = values.pop
PgSearch.disable_multisearch do
sync.push :go
@multisearch_enabled_inside = values.pop
end
sync.push :go
@multisearch_enabled_after = values.pop
expect(@multisearch_enabled_before).to be(true)
expect(@multisearch_enabled_inside).to be(true)
expect(@multisearch_enabled_after).to be(true)
end
end
end