pg_search 2.1.2 → 2.3.6
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 +5 -5
- data/.codeclimate.yml +1 -0
- data/.editorconfig +10 -0
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/ci.yml +75 -0
- data/.jrubyrc +1 -0
- data/.rubocop.yml +95 -10
- data/.travis.yml +26 -48
- data/CHANGELOG.md +179 -112
- data/CODE_OF_CONDUCT.md +76 -0
- data/CONTRIBUTING.md +5 -3
- data/Gemfile +6 -4
- data/LICENSE +1 -1
- data/README.md +307 -198
- data/Rakefile +7 -3
- data/lib/pg_search/configuration/association.rb +2 -0
- data/lib/pg_search/configuration/column.rb +2 -0
- data/lib/pg_search/configuration/foreign_column.rb +2 -0
- data/lib/pg_search/configuration.rb +20 -6
- data/lib/pg_search/document.rb +7 -5
- data/lib/pg_search/features/dmetaphone.rb +7 -7
- data/lib/pg_search/features/feature.rb +4 -2
- data/lib/pg_search/features/trigram.rb +31 -5
- data/lib/pg_search/features/tsearch.rb +18 -14
- data/lib/pg_search/features.rb +2 -0
- data/lib/pg_search/migration/dmetaphone_generator.rb +3 -1
- data/lib/pg_search/migration/generator.rb +4 -2
- data/lib/pg_search/migration/multisearch_generator.rb +2 -1
- data/lib/pg_search/migration/templates/add_pg_search_dmetaphone_support_functions.rb.erb +6 -6
- data/lib/pg_search/migration/templates/create_pg_search_documents.rb.erb +3 -3
- data/lib/pg_search/model.rb +57 -0
- data/lib/pg_search/multisearch/rebuilder.rb +14 -6
- data/lib/pg_search/multisearch.rb +23 -6
- data/lib/pg_search/multisearchable.rb +10 -6
- data/lib/pg_search/normalizer.rb +2 -0
- data/lib/pg_search/railtie.rb +2 -0
- data/lib/pg_search/scope_options.rb +26 -49
- data/lib/pg_search/tasks.rb +5 -1
- data/lib/pg_search/version.rb +3 -1
- data/lib/pg_search.rb +17 -55
- data/pg_search.gemspec +19 -11
- data/spec/.rubocop.yml +2 -2
- data/spec/integration/.rubocop.yml +11 -0
- data/spec/integration/associations_spec.rb +125 -162
- data/spec/integration/deprecation_spec.rb +33 -0
- data/spec/integration/pagination_spec.rb +10 -8
- data/spec/integration/pg_search_spec.rb +359 -306
- data/spec/integration/single_table_inheritance_spec.rb +18 -17
- data/spec/lib/pg_search/configuration/association_spec.rb +17 -13
- data/spec/lib/pg_search/configuration/column_spec.rb +2 -0
- data/spec/lib/pg_search/configuration/foreign_column_spec.rb +6 -4
- data/spec/lib/pg_search/features/dmetaphone_spec.rb +6 -4
- data/spec/lib/pg_search/features/trigram_spec.rb +51 -20
- data/spec/lib/pg_search/features/tsearch_spec.rb +29 -21
- data/spec/lib/pg_search/multisearch/rebuilder_spec.rb +151 -85
- data/spec/lib/pg_search/multisearch_spec.rb +67 -37
- data/spec/lib/pg_search/multisearchable_spec.rb +217 -123
- data/spec/lib/pg_search/normalizer_spec.rb +14 -10
- data/spec/lib/pg_search_spec.rb +102 -89
- data/spec/spec_helper.rb +25 -6
- data/spec/support/database.rb +19 -21
- data/spec/support/with_model.rb +2 -0
- metadata +106 -29
- data/.autotest +0 -5
- data/.rubocop_todo.yml +0 -163
- data/Guardfile +0 -6
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
5
|
+
# rubocop:disable RSpec/NestedGroups
|
3
6
|
describe "an Active Record model which includes PgSearch" do
|
4
7
|
with_model :ModelWithPgSearch do
|
5
8
|
table do |t|
|
@@ -10,7 +13,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
10
13
|
end
|
11
14
|
|
12
15
|
model do
|
13
|
-
include PgSearch
|
16
|
+
include PgSearch::Model
|
14
17
|
belongs_to :parent_model
|
15
18
|
end
|
16
19
|
end
|
@@ -20,7 +23,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
20
23
|
end
|
21
24
|
|
22
25
|
model do
|
23
|
-
include PgSearch
|
26
|
+
include PgSearch::Model
|
24
27
|
has_many :models_with_pg_search
|
25
28
|
scope :active, -> { where(active: true) }
|
26
29
|
end
|
@@ -28,7 +31,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
28
31
|
|
29
32
|
describe ".pg_search_scope" do
|
30
33
|
it "builds a chainable scope" do
|
31
|
-
ModelWithPgSearch.pg_search_scope "matching_query", :
|
34
|
+
ModelWithPgSearch.pg_search_scope "matching_query", against: []
|
32
35
|
scope = ModelWithPgSearch.where("1 = 1").matching_query("foo").where("1 = 1")
|
33
36
|
expect(scope).to be_an ActiveRecord::Relation
|
34
37
|
end
|
@@ -36,15 +39,15 @@ describe "an Active Record model which includes PgSearch" do
|
|
36
39
|
context "when passed a lambda" do
|
37
40
|
it "builds a dynamic scope" do
|
38
41
|
ModelWithPgSearch.pg_search_scope :search_title_or_content,
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
lambda { |query, pick_content|
|
43
|
+
{
|
44
|
+
query: query.gsub("-remove-", ""),
|
45
|
+
against: pick_content ? :content : :title
|
46
|
+
}
|
47
|
+
}
|
45
48
|
|
46
|
-
included = ModelWithPgSearch.create!(:
|
47
|
-
excluded = ModelWithPgSearch.create!(:
|
49
|
+
included = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
|
50
|
+
excluded = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
|
48
51
|
|
49
52
|
expect(ModelWithPgSearch.search_title_or_content('fo-remove-o', false)).to eq([included])
|
50
53
|
expect(ModelWithPgSearch.search_title_or_content('b-remove-ar', true)).to eq([included])
|
@@ -54,18 +57,18 @@ describe "an Active Record model which includes PgSearch" do
|
|
54
57
|
context "when an unknown option is passed in" do
|
55
58
|
it "raises an exception when invoked" do
|
56
59
|
ModelWithPgSearch.pg_search_scope :with_unknown_option,
|
57
|
-
|
58
|
-
|
60
|
+
against: :content,
|
61
|
+
foo: :bar
|
59
62
|
|
60
63
|
expect {
|
61
64
|
ModelWithPgSearch.with_unknown_option("foo")
|
62
65
|
}.to raise_error(ArgumentError, /foo/)
|
63
66
|
end
|
64
67
|
|
65
|
-
context "
|
68
|
+
context "with a lambda" do
|
66
69
|
it "raises an exception when invoked" do
|
67
70
|
ModelWithPgSearch.pg_search_scope :with_unknown_option,
|
68
|
-
|
71
|
+
->(*) { { against: :content, foo: :bar } }
|
69
72
|
|
70
73
|
expect {
|
71
74
|
ModelWithPgSearch.with_unknown_option("foo")
|
@@ -77,18 +80,18 @@ describe "an Active Record model which includes PgSearch" do
|
|
77
80
|
context "when an unknown :using is passed" do
|
78
81
|
it "raises an exception when invoked" do
|
79
82
|
ModelWithPgSearch.pg_search_scope :with_unknown_using,
|
80
|
-
|
81
|
-
|
83
|
+
against: :content,
|
84
|
+
using: :foo
|
82
85
|
|
83
86
|
expect {
|
84
87
|
ModelWithPgSearch.with_unknown_using("foo")
|
85
88
|
}.to raise_error(ArgumentError, /foo/)
|
86
89
|
end
|
87
90
|
|
88
|
-
context "
|
91
|
+
context "with a lambda" do
|
89
92
|
it "raises an exception when invoked" do
|
90
93
|
ModelWithPgSearch.pg_search_scope :with_unknown_using,
|
91
|
-
|
94
|
+
->(*) { { against: :content, using: :foo } }
|
92
95
|
|
93
96
|
expect {
|
94
97
|
ModelWithPgSearch.with_unknown_using("foo")
|
@@ -100,18 +103,18 @@ describe "an Active Record model which includes PgSearch" do
|
|
100
103
|
context "when an unknown :ignoring is passed" do
|
101
104
|
it "raises an exception when invoked" do
|
102
105
|
ModelWithPgSearch.pg_search_scope :with_unknown_ignoring,
|
103
|
-
|
104
|
-
|
106
|
+
against: :content,
|
107
|
+
ignoring: :foo
|
105
108
|
|
106
109
|
expect {
|
107
110
|
ModelWithPgSearch.with_unknown_ignoring("foo")
|
108
111
|
}.to raise_error(ArgumentError, /ignoring.*foo/)
|
109
112
|
end
|
110
113
|
|
111
|
-
context "
|
114
|
+
context "with a lambda" do
|
112
115
|
it "raises an exception when invoked" do
|
113
116
|
ModelWithPgSearch.pg_search_scope :with_unknown_ignoring,
|
114
|
-
|
117
|
+
->(*) { { against: :content, ignoring: :foo } }
|
115
118
|
|
116
119
|
expect {
|
117
120
|
ModelWithPgSearch.with_unknown_ignoring("foo")
|
@@ -128,23 +131,39 @@ describe "an Active Record model which includes PgSearch" do
|
|
128
131
|
}.to raise_error(ArgumentError, /against/)
|
129
132
|
end
|
130
133
|
|
131
|
-
context "
|
134
|
+
context "with a lambda" do
|
132
135
|
it "raises an exception when invoked" do
|
133
|
-
ModelWithPgSearch.pg_search_scope :with_unknown_ignoring, ->(*){ {} }
|
136
|
+
ModelWithPgSearch.pg_search_scope :with_unknown_ignoring, ->(*) { {} }
|
134
137
|
|
135
138
|
expect {
|
136
139
|
ModelWithPgSearch.with_unknown_ignoring("foo")
|
137
140
|
}.to raise_error(ArgumentError, /against/)
|
138
141
|
end
|
139
142
|
end
|
143
|
+
|
144
|
+
context "when a tsvector column is specified" do
|
145
|
+
it "does not raise an exception when invoked" do
|
146
|
+
ModelWithPgSearch.pg_search_scope :with_unknown_ignoring, {
|
147
|
+
using: {
|
148
|
+
tsearch: {
|
149
|
+
tsvector_column: "tsv"
|
150
|
+
}
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
expect {
|
155
|
+
ModelWithPgSearch.with_unknown_ignoring("foo")
|
156
|
+
}.not_to raise_error
|
157
|
+
end
|
158
|
+
end
|
140
159
|
end
|
141
160
|
end
|
142
161
|
end
|
143
162
|
|
144
163
|
describe "a search scope" do
|
145
|
-
context "against a single column" do
|
164
|
+
context "when against a single column" do
|
146
165
|
before do
|
147
|
-
ModelWithPgSearch.pg_search_scope :search_content, :
|
166
|
+
ModelWithPgSearch.pg_search_scope :search_content, against: :content
|
148
167
|
end
|
149
168
|
|
150
169
|
context "when chained after a select() scope" do
|
@@ -155,7 +174,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
155
174
|
results = ModelWithPgSearch.select('id, title').search_content('foo')
|
156
175
|
|
157
176
|
expect(results).to include(included)
|
158
|
-
expect(results).
|
177
|
+
expect(results).not_to include(excluded)
|
159
178
|
|
160
179
|
expect(results.first.attributes.key?('content')).to eq false
|
161
180
|
|
@@ -172,7 +191,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
172
191
|
results = ModelWithPgSearch.search_content('foo').select('id, title')
|
173
192
|
|
174
193
|
expect(results).to include(included)
|
175
|
-
expect(results).
|
194
|
+
expect(results).not_to include(excluded)
|
176
195
|
|
177
196
|
expect(results.first.attributes.key?('content')).to eq false
|
178
197
|
|
@@ -189,7 +208,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
189
208
|
results = ModelWithPgSearch.select('id').search_content('foo').select('title')
|
190
209
|
|
191
210
|
expect(results).to include(included)
|
192
|
-
expect(results).
|
211
|
+
expect(results).not_to include(excluded)
|
193
212
|
|
194
213
|
expect(results.first.attributes.key?('content')).to eq false
|
195
214
|
|
@@ -198,7 +217,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
198
217
|
end
|
199
218
|
end
|
200
219
|
|
201
|
-
context "chained to a cross-table scope" do
|
220
|
+
context "when chained to a cross-table scope" do
|
202
221
|
with_model :House do
|
203
222
|
table do |t|
|
204
223
|
t.references :person
|
@@ -206,7 +225,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
206
225
|
end
|
207
226
|
|
208
227
|
model do
|
209
|
-
include PgSearch
|
228
|
+
include PgSearch::Model
|
210
229
|
belongs_to :person
|
211
230
|
pg_search_scope :search_city, against: [:city]
|
212
231
|
end
|
@@ -218,11 +237,11 @@ describe "an Active Record model which includes PgSearch" do
|
|
218
237
|
end
|
219
238
|
|
220
239
|
model do
|
221
|
-
include PgSearch
|
240
|
+
include PgSearch::Model
|
222
241
|
has_many :houses
|
223
242
|
pg_search_scope :named, against: [:name]
|
224
243
|
scope :with_house_in_city, lambda { |city|
|
225
|
-
joins(:houses).where(House.table_name.to_sym => {city: city})
|
244
|
+
joins(:houses).where(House.table_name.to_sym => { city: city })
|
226
245
|
}
|
227
246
|
scope :house_search_city, lambda { |query|
|
228
247
|
joins(:houses).merge(House.search_city(query))
|
@@ -268,7 +287,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
268
287
|
it "does not raise an exception" do
|
269
288
|
relation = Person.named('foo').house_search_city('bar')
|
270
289
|
|
271
|
-
expect { relation.to_a }.
|
290
|
+
expect { relation.to_a }.not_to raise_error
|
272
291
|
end
|
273
292
|
end
|
274
293
|
end
|
@@ -281,20 +300,20 @@ describe "an Active Record model which includes PgSearch" do
|
|
281
300
|
it "does not raise an exception" do
|
282
301
|
relation = ModelWithPgSearch.search_content('foo').search_title('bar')
|
283
302
|
|
284
|
-
expect { relation.to_a }.
|
303
|
+
expect { relation.to_a }.not_to raise_error
|
285
304
|
end
|
286
305
|
end
|
287
306
|
|
288
307
|
it "returns an empty array when a blank query is passed in" do
|
289
|
-
ModelWithPgSearch.create!(:
|
308
|
+
ModelWithPgSearch.create!(content: 'foo')
|
290
309
|
|
291
310
|
results = ModelWithPgSearch.search_content('')
|
292
311
|
expect(results).to eq([])
|
293
312
|
end
|
294
313
|
|
295
314
|
it "returns rows where the column contains the term in the query" do
|
296
|
-
included = ModelWithPgSearch.create!(:
|
297
|
-
excluded = ModelWithPgSearch.create!(:
|
315
|
+
included = ModelWithPgSearch.create!(content: 'foo')
|
316
|
+
excluded = ModelWithPgSearch.create!(content: 'bar')
|
298
317
|
|
299
318
|
results = ModelWithPgSearch.search_content('foo')
|
300
319
|
expect(results).to include(included)
|
@@ -302,24 +321,24 @@ describe "an Active Record model which includes PgSearch" do
|
|
302
321
|
end
|
303
322
|
|
304
323
|
it "returns the correct count" do
|
305
|
-
ModelWithPgSearch.create!(:
|
306
|
-
ModelWithPgSearch.create!(:
|
324
|
+
ModelWithPgSearch.create!(content: 'foo')
|
325
|
+
ModelWithPgSearch.create!(content: 'bar')
|
307
326
|
|
308
327
|
results = ModelWithPgSearch.search_content('foo')
|
309
328
|
expect(results.count).to eq 1
|
310
329
|
end
|
311
330
|
|
312
331
|
it "returns the correct count(:all)" do
|
313
|
-
ModelWithPgSearch.create!(:
|
314
|
-
ModelWithPgSearch.create!(:
|
332
|
+
ModelWithPgSearch.create!(content: 'foo')
|
333
|
+
ModelWithPgSearch.create!(content: 'bar')
|
315
334
|
|
316
335
|
results = ModelWithPgSearch.search_content('foo')
|
317
336
|
expect(results.count(:all)).to eq 1
|
318
337
|
end
|
319
338
|
|
320
339
|
it "supports #select" do
|
321
|
-
record = ModelWithPgSearch.create!(:
|
322
|
-
other_record = ModelWithPgSearch.create!(:
|
340
|
+
record = ModelWithPgSearch.create!(content: 'foo')
|
341
|
+
other_record = ModelWithPgSearch.create!(content: 'bar')
|
323
342
|
|
324
343
|
records_with_only_id = ModelWithPgSearch.search_content('foo').select('id')
|
325
344
|
expect(records_with_only_id.length).to eq 1
|
@@ -330,27 +349,27 @@ describe "an Active Record model which includes PgSearch" do
|
|
330
349
|
end
|
331
350
|
|
332
351
|
it "supports #pluck" do
|
333
|
-
record = ModelWithPgSearch.create!(:
|
334
|
-
other_record = ModelWithPgSearch.create!(:
|
352
|
+
record = ModelWithPgSearch.create!(content: 'foo')
|
353
|
+
other_record = ModelWithPgSearch.create!(content: 'bar')
|
335
354
|
|
336
355
|
ids = ModelWithPgSearch.search_content('foo').pluck('id')
|
337
356
|
expect(ids).to eq [record.id]
|
338
357
|
end
|
339
358
|
|
340
359
|
it "supports adding where clauses using the pg_search.rank" do
|
341
|
-
once = ModelWithPgSearch.create!(:
|
342
|
-
twice = ModelWithPgSearch.create!(:
|
360
|
+
once = ModelWithPgSearch.create!(content: 'foo bar')
|
361
|
+
twice = ModelWithPgSearch.create!(content: 'foo foo')
|
343
362
|
|
344
363
|
records = ModelWithPgSearch.search_content('foo')
|
345
|
-
|
364
|
+
.where("#{PgSearch::Configuration.alias(ModelWithPgSearch.table_name)}.rank > 0.07")
|
346
365
|
|
347
366
|
expect(records).to eq [twice]
|
348
367
|
end
|
349
368
|
|
350
369
|
it "returns rows where the column contains all the terms in the query in any order" do
|
351
|
-
included = [ModelWithPgSearch.create!(:
|
352
|
-
ModelWithPgSearch.create!(:
|
353
|
-
excluded = ModelWithPgSearch.create!(:
|
370
|
+
included = [ModelWithPgSearch.create!(content: 'foo bar'),
|
371
|
+
ModelWithPgSearch.create!(content: 'bar foo')]
|
372
|
+
excluded = ModelWithPgSearch.create!(content: 'foo')
|
354
373
|
|
355
374
|
results = ModelWithPgSearch.search_content('foo bar')
|
356
375
|
expect(results).to match_array(included)
|
@@ -358,8 +377,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
358
377
|
end
|
359
378
|
|
360
379
|
it "returns rows that match the query but not its case" do
|
361
|
-
included = [ModelWithPgSearch.create!(:
|
362
|
-
ModelWithPgSearch.create!(:
|
380
|
+
included = [ModelWithPgSearch.create!(content: "foo"),
|
381
|
+
ModelWithPgSearch.create!(content: "FOO")]
|
363
382
|
|
364
383
|
results = ModelWithPgSearch.search_content("Foo")
|
365
384
|
expect(results).to match_array(included)
|
@@ -369,8 +388,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
369
388
|
# \303\241 is a with acute accent
|
370
389
|
# \303\251 is e with acute accent
|
371
390
|
|
372
|
-
included = ModelWithPgSearch.create!(:
|
373
|
-
excluded = ModelWithPgSearch.create!(:
|
391
|
+
included = ModelWithPgSearch.create!(content: "abcd\303\251f")
|
392
|
+
excluded = ModelWithPgSearch.create!(content: "\303\241bcdef")
|
374
393
|
|
375
394
|
results = ModelWithPgSearch.search_content("abcd\303\251f")
|
376
395
|
expect(results).to eq([included])
|
@@ -378,8 +397,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
378
397
|
end
|
379
398
|
|
380
399
|
it "returns rows that match the query but not rows that are prefixed by the query" do
|
381
|
-
included = ModelWithPgSearch.create!(:
|
382
|
-
excluded = ModelWithPgSearch.create!(:
|
400
|
+
included = ModelWithPgSearch.create!(content: 'pre')
|
401
|
+
excluded = ModelWithPgSearch.create!(content: 'prefix')
|
383
402
|
|
384
403
|
results = ModelWithPgSearch.search_content("pre")
|
385
404
|
expect(results).to eq([included])
|
@@ -387,28 +406,37 @@ describe "an Active Record model which includes PgSearch" do
|
|
387
406
|
end
|
388
407
|
|
389
408
|
it "returns rows that match the query exactly and not those that match the query when stemmed by the default english dictionary" do
|
390
|
-
included = ModelWithPgSearch.create!(:
|
391
|
-
excluded = [ModelWithPgSearch.create!(:
|
392
|
-
ModelWithPgSearch.create!(:
|
409
|
+
included = ModelWithPgSearch.create!(content: "jumped")
|
410
|
+
excluded = [ModelWithPgSearch.create!(content: "jump"),
|
411
|
+
ModelWithPgSearch.create!(content: "jumping")]
|
393
412
|
|
394
413
|
results = ModelWithPgSearch.search_content("jumped")
|
395
414
|
expect(results).to eq([included])
|
396
415
|
end
|
397
416
|
|
398
417
|
it "returns rows that match sorted by rank" do
|
399
|
-
loser = ModelWithPgSearch.create!(:
|
400
|
-
winner = ModelWithPgSearch.create!(:
|
418
|
+
loser = ModelWithPgSearch.create!(content: 'foo')
|
419
|
+
winner = ModelWithPgSearch.create!(content: 'foo foo')
|
401
420
|
|
402
421
|
results = ModelWithPgSearch.search_content("foo").with_pg_search_rank
|
403
422
|
expect(results[0].pg_search_rank).to be > results[1].pg_search_rank
|
404
423
|
expect(results).to eq([winner, loser])
|
405
424
|
end
|
406
425
|
|
426
|
+
it 'preserves column selection when with_pg_search_rank is chained after a select()' do
|
427
|
+
loser = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
|
428
|
+
|
429
|
+
results = ModelWithPgSearch.search_content('bar').select(:content).with_pg_search_rank
|
430
|
+
|
431
|
+
expect(results.length).to be 1
|
432
|
+
expect(results.first.as_json.keys).to contain_exactly('id', 'content', 'pg_search_rank')
|
433
|
+
end
|
434
|
+
|
407
435
|
it 'allows pg_search_rank along with a join' do
|
408
436
|
parent_1 = ParentModel.create!(id: 98)
|
409
437
|
parent_2 = ParentModel.create!(id: 99)
|
410
|
-
loser = ModelWithPgSearch.create!(:
|
411
|
-
winner = ModelWithPgSearch.create!(:
|
438
|
+
loser = ModelWithPgSearch.create!(content: 'foo', parent_model: parent_2)
|
439
|
+
winner = ModelWithPgSearch.create!(content: 'foo foo', parent_model: parent_1)
|
412
440
|
|
413
441
|
results = ModelWithPgSearch.joins(:parent_model).merge(ParentModel.active).search_content("foo").with_pg_search_rank
|
414
442
|
expect(results.map(&:id)).to eq [winner.id, loser.id]
|
@@ -417,8 +445,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
417
445
|
end
|
418
446
|
|
419
447
|
it "returns results that match sorted by primary key for records that rank the same" do
|
420
|
-
sorted_results = [ModelWithPgSearch.create!(:
|
421
|
-
ModelWithPgSearch.create!(:
|
448
|
+
sorted_results = [ModelWithPgSearch.create!(content: 'foo'),
|
449
|
+
ModelWithPgSearch.create!(content: 'foo')].sort_by(&:id)
|
422
450
|
|
423
451
|
results = ModelWithPgSearch.search_content("foo")
|
424
452
|
expect(results).to eq(sorted_results)
|
@@ -426,13 +454,13 @@ describe "an Active Record model which includes PgSearch" do
|
|
426
454
|
|
427
455
|
it "returns results that match a query with multiple space-separated search terms" do
|
428
456
|
included = [
|
429
|
-
ModelWithPgSearch.create!(:
|
430
|
-
ModelWithPgSearch.create!(:
|
431
|
-
ModelWithPgSearch.create!(:
|
457
|
+
ModelWithPgSearch.create!(content: 'foo bar'),
|
458
|
+
ModelWithPgSearch.create!(content: 'bar foo'),
|
459
|
+
ModelWithPgSearch.create!(content: 'bar foo baz')
|
432
460
|
]
|
433
461
|
excluded = [
|
434
|
-
ModelWithPgSearch.create!(:
|
435
|
-
ModelWithPgSearch.create!(:
|
462
|
+
ModelWithPgSearch.create!(content: 'foo'),
|
463
|
+
ModelWithPgSearch.create!(content: 'foo baz')
|
436
464
|
]
|
437
465
|
|
438
466
|
results = ModelWithPgSearch.search_content('foo bar')
|
@@ -441,15 +469,15 @@ describe "an Active Record model which includes PgSearch" do
|
|
441
469
|
end
|
442
470
|
|
443
471
|
it "returns rows that match a query with characters that are invalid in a tsquery expression" do
|
444
|
-
included = ModelWithPgSearch.create!(:
|
472
|
+
included = ModelWithPgSearch.create!(content: "(:Foo.) Bar?, \\")
|
445
473
|
|
446
474
|
results = ModelWithPgSearch.search_content("foo :bar .,?() \\")
|
447
475
|
expect(results).to eq([included])
|
448
476
|
end
|
449
477
|
|
450
478
|
it "accepts non-string queries and calls #to_s on them" do
|
451
|
-
foo = ModelWithPgSearch.create!(:
|
452
|
-
not_a_string =
|
479
|
+
foo = ModelWithPgSearch.create!(content: "foo")
|
480
|
+
not_a_string = instance_double("Object", to_s: "foo")
|
453
481
|
expect(ModelWithPgSearch.search_content(not_a_string)).to eq([foo])
|
454
482
|
end
|
455
483
|
|
@@ -460,12 +488,12 @@ describe "an Active Record model which includes PgSearch" do
|
|
460
488
|
end
|
461
489
|
|
462
490
|
model do
|
463
|
-
include PgSearch
|
491
|
+
include PgSearch::Model
|
464
492
|
|
465
493
|
# WARNING: searching timestamps is not something PostgreSQL
|
466
494
|
# full-text search is good at. Use at your own risk.
|
467
495
|
pg_search_scope :search_timestamps,
|
468
|
-
|
496
|
+
against: %i[created_at updated_at]
|
469
497
|
end
|
470
498
|
end
|
471
499
|
|
@@ -479,19 +507,19 @@ describe "an Active Record model which includes PgSearch" do
|
|
479
507
|
end
|
480
508
|
end
|
481
509
|
|
482
|
-
context "against multiple columns" do
|
510
|
+
context "when against multiple columns" do
|
483
511
|
before do
|
484
|
-
ModelWithPgSearch.pg_search_scope :search_title_and_content, :
|
512
|
+
ModelWithPgSearch.pg_search_scope :search_title_and_content, against: %i[title content]
|
485
513
|
end
|
486
514
|
|
487
515
|
it "returns rows whose columns contain all of the terms in the query across columns" do
|
488
516
|
included = [
|
489
|
-
ModelWithPgSearch.create!(:
|
490
|
-
ModelWithPgSearch.create!(:
|
517
|
+
ModelWithPgSearch.create!(title: 'foo', content: 'bar'),
|
518
|
+
ModelWithPgSearch.create!(title: 'bar', content: 'foo')
|
491
519
|
]
|
492
520
|
excluded = [
|
493
|
-
ModelWithPgSearch.create!(:
|
494
|
-
ModelWithPgSearch.create!(:
|
521
|
+
ModelWithPgSearch.create!(title: 'foo', content: 'foo'),
|
522
|
+
ModelWithPgSearch.create!(title: 'bar', content: 'bar')
|
495
523
|
]
|
496
524
|
|
497
525
|
results = ModelWithPgSearch.search_title_and_content('foo bar')
|
@@ -503,8 +531,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
503
531
|
end
|
504
532
|
|
505
533
|
it "returns rows where at one column contains all of the terms in the query and another does not" do
|
506
|
-
in_title = ModelWithPgSearch.create!(:
|
507
|
-
in_content = ModelWithPgSearch.create!(:
|
534
|
+
in_title = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
|
535
|
+
in_content = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
|
508
536
|
|
509
537
|
results = ModelWithPgSearch.search_title_and_content('foo')
|
510
538
|
expect(results).to match_array([in_title, in_content])
|
@@ -512,39 +540,39 @@ describe "an Active Record model which includes PgSearch" do
|
|
512
540
|
|
513
541
|
# Searching with a NULL column will prevent any matches unless we coalesce it.
|
514
542
|
it "returns rows where at one column contains all of the terms in the query and another is NULL" do
|
515
|
-
included = ModelWithPgSearch.create!(:
|
543
|
+
included = ModelWithPgSearch.create!(title: 'foo', content: nil)
|
516
544
|
results = ModelWithPgSearch.search_title_and_content('foo')
|
517
545
|
expect(results).to eq([included])
|
518
546
|
end
|
519
547
|
end
|
520
548
|
|
521
|
-
context "using trigram" do
|
549
|
+
context "when using trigram" do
|
522
550
|
before do
|
523
|
-
ModelWithPgSearch.pg_search_scope :with_trigrams, :
|
551
|
+
ModelWithPgSearch.pg_search_scope :with_trigrams, against: %i[title content], using: :trigram
|
524
552
|
end
|
525
553
|
|
526
554
|
it "returns rows where one searchable column and the query share enough trigrams" do
|
527
|
-
included = ModelWithPgSearch.create!(:
|
555
|
+
included = ModelWithPgSearch.create!(title: 'abcdefghijkl', content: nil)
|
528
556
|
results = ModelWithPgSearch.with_trigrams('cdefhijkl')
|
529
557
|
expect(results).to eq([included])
|
530
558
|
end
|
531
559
|
|
532
560
|
it "returns rows where multiple searchable columns and the query share enough trigrams" do
|
533
|
-
included = ModelWithPgSearch.create!(:
|
561
|
+
included = ModelWithPgSearch.create!(title: 'abcdef', content: 'ghijkl')
|
534
562
|
results = ModelWithPgSearch.with_trigrams('cdefhijkl')
|
535
563
|
expect(results).to eq([included])
|
536
564
|
end
|
537
565
|
|
538
566
|
context "when a threshold is specified" do
|
539
567
|
before do
|
540
|
-
ModelWithPgSearch.pg_search_scope :with_strict_trigrams, :
|
541
|
-
ModelWithPgSearch.pg_search_scope :with_permissive_trigrams, :
|
568
|
+
ModelWithPgSearch.pg_search_scope :with_strict_trigrams, against: %i[title content], using: { trigram: { threshold: 0.5 } }
|
569
|
+
ModelWithPgSearch.pg_search_scope :with_permissive_trigrams, against: %i[title content], using: { trigram: { threshold: 0.1 } }
|
542
570
|
end
|
543
571
|
|
544
572
|
it "uses the threshold in the trigram expression" do
|
545
|
-
low_similarity = ModelWithPgSearch.create!(:
|
546
|
-
medium_similarity = ModelWithPgSearch.create!(:
|
547
|
-
high_similarity = ModelWithPgSearch.create!(:
|
573
|
+
low_similarity = ModelWithPgSearch.create!(title: "a")
|
574
|
+
medium_similarity = ModelWithPgSearch.create!(title: "abc")
|
575
|
+
high_similarity = ModelWithPgSearch.create!(title: "abcdefghijkl")
|
548
576
|
|
549
577
|
results = ModelWithPgSearch.with_strict_trigrams("abcdefg")
|
550
578
|
expect(results).to include(high_similarity)
|
@@ -560,19 +588,19 @@ describe "an Active Record model which includes PgSearch" do
|
|
560
588
|
end
|
561
589
|
end
|
562
590
|
|
563
|
-
context "using tsearch" do
|
591
|
+
context "when using tsearch" do
|
564
592
|
before do
|
565
593
|
ModelWithPgSearch.pg_search_scope :search_title_with_prefixes,
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
594
|
+
against: :title,
|
595
|
+
using: {
|
596
|
+
tsearch: { prefix: true }
|
597
|
+
}
|
570
598
|
end
|
571
599
|
|
572
|
-
context "with :
|
600
|
+
context "with prefix: true" do
|
573
601
|
it "returns rows that match the query and that are prefixed by the query" do
|
574
|
-
included = ModelWithPgSearch.create!(:
|
575
|
-
excluded = ModelWithPgSearch.create!(:
|
602
|
+
included = ModelWithPgSearch.create!(title: 'prefix')
|
603
|
+
excluded = ModelWithPgSearch.create!(title: 'postfix')
|
576
604
|
|
577
605
|
results = ModelWithPgSearch.search_title_with_prefixes("pre")
|
578
606
|
expect(results).to eq([included])
|
@@ -580,8 +608,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
580
608
|
end
|
581
609
|
|
582
610
|
it "returns rows that match the query when the query has a hyphen" do
|
583
|
-
included = ModelWithPgSearch.create!(:
|
584
|
-
excluded = ModelWithPgSearch.create!(:
|
611
|
+
included = ModelWithPgSearch.create!(title: 'foo-bar')
|
612
|
+
excluded = ModelWithPgSearch.create!(title: 'foo bar')
|
585
613
|
|
586
614
|
results = ModelWithPgSearch.search_title_with_prefixes("foo-bar")
|
587
615
|
expect(results).to include(included)
|
@@ -592,16 +620,16 @@ describe "an Active Record model which includes PgSearch" do
|
|
592
620
|
context "with the english dictionary" do
|
593
621
|
before do
|
594
622
|
ModelWithPgSearch.pg_search_scope :search_content_with_english,
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
623
|
+
against: :content,
|
624
|
+
using: {
|
625
|
+
tsearch: { dictionary: :english }
|
626
|
+
}
|
599
627
|
end
|
600
628
|
|
601
629
|
it "returns rows that match the query when stemmed by the english dictionary" do
|
602
|
-
included = [ModelWithPgSearch.create!(:
|
603
|
-
ModelWithPgSearch.create!(:
|
604
|
-
ModelWithPgSearch.create!(:
|
630
|
+
included = [ModelWithPgSearch.create!(content: "jump"),
|
631
|
+
ModelWithPgSearch.create!(content: "jumped"),
|
632
|
+
ModelWithPgSearch.create!(content: "jumping")]
|
605
633
|
|
606
634
|
results = ModelWithPgSearch.search_content_with_english("jump")
|
607
635
|
expect(results).to match_array(included)
|
@@ -611,14 +639,14 @@ describe "an Active Record model which includes PgSearch" do
|
|
611
639
|
describe "highlighting" do
|
612
640
|
before do
|
613
641
|
["Strip Down", "Down", "Down and Out", "Won't Let You Down"].each do |name|
|
614
|
-
ModelWithPgSearch.create! :content
|
642
|
+
ModelWithPgSearch.create! title: 'Just a title', content: name
|
615
643
|
end
|
616
644
|
end
|
617
645
|
|
618
646
|
context "with highlight turned on" do
|
619
647
|
before do
|
620
648
|
ModelWithPgSearch.pg_search_scope :search_content,
|
621
|
-
|
649
|
+
against: :content
|
622
650
|
end
|
623
651
|
|
624
652
|
it "adds a #pg_search_highlight method to each returned model record" do
|
@@ -632,26 +660,32 @@ describe "an Active Record model which includes PgSearch" do
|
|
632
660
|
|
633
661
|
expect(result.pg_search_highlight).to eq("Won't <b>Let</b> You Down")
|
634
662
|
end
|
663
|
+
|
664
|
+
it 'preserves column selection when with_pg_search_highlight is chained after a select()' do
|
665
|
+
result = ModelWithPgSearch.search_content("Let").select(:content).with_pg_search_highlight.first
|
666
|
+
|
667
|
+
expect(result.as_json.keys).to contain_exactly('id', 'content', 'pg_search_highlight')
|
668
|
+
end
|
635
669
|
end
|
636
670
|
|
637
671
|
context "with custom highlighting options" do
|
638
672
|
before do
|
639
|
-
ModelWithPgSearch.create! :
|
673
|
+
ModelWithPgSearch.create! content: "#{'text ' * 2}Let #{'text ' * 2}Let #{'text ' * 2}"
|
640
674
|
|
641
675
|
ModelWithPgSearch.pg_search_scope :search_content,
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
676
|
+
against: :content,
|
677
|
+
using: {
|
678
|
+
tsearch: {
|
679
|
+
highlight: {
|
680
|
+
StartSel: '<mark class="highlight">',
|
681
|
+
StopSel: '</mark>',
|
682
|
+
FragmentDelimiter: '<delim class="my_delim">',
|
683
|
+
MaxFragments: 2,
|
684
|
+
MaxWords: 2,
|
685
|
+
MinWords: 1
|
686
|
+
}
|
687
|
+
}
|
688
|
+
}
|
655
689
|
end
|
656
690
|
|
657
691
|
it "applies the options to the excerpts" do
|
@@ -665,12 +699,12 @@ describe "an Active Record model which includes PgSearch" do
|
|
665
699
|
describe "ranking" do
|
666
700
|
before do
|
667
701
|
["Strip Down", "Down", "Down and Out", "Won't Let You Down"].each do |name|
|
668
|
-
ModelWithPgSearch.create! :
|
702
|
+
ModelWithPgSearch.create! content: name
|
669
703
|
end
|
670
704
|
end
|
671
705
|
|
672
706
|
it "adds a #pg_search_rank method to each returned model record" do
|
673
|
-
ModelWithPgSearch.pg_search_scope :search_content, :
|
707
|
+
ModelWithPgSearch.pg_search_scope :search_content, against: :content
|
674
708
|
|
675
709
|
result = ModelWithPgSearch.search_content("Strip Down").with_pg_search_rank.first
|
676
710
|
|
@@ -680,10 +714,10 @@ describe "an Active Record model which includes PgSearch" do
|
|
680
714
|
context "with a normalization specified" do
|
681
715
|
before do
|
682
716
|
ModelWithPgSearch.pg_search_scope :search_content_with_normalization,
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
717
|
+
against: :content,
|
718
|
+
using: {
|
719
|
+
tsearch: { normalization: 2 }
|
720
|
+
}
|
687
721
|
end
|
688
722
|
|
689
723
|
it "ranks the results for documents with less text higher" do
|
@@ -697,8 +731,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
697
731
|
context "with no normalization" do
|
698
732
|
before do
|
699
733
|
ModelWithPgSearch.pg_search_scope :search_content_without_normalization,
|
700
|
-
|
701
|
-
|
734
|
+
against: :content,
|
735
|
+
using: :tsearch
|
702
736
|
end
|
703
737
|
|
704
738
|
it "ranks the results equally" do
|
@@ -710,15 +744,15 @@ describe "an Active Record model which includes PgSearch" do
|
|
710
744
|
end
|
711
745
|
end
|
712
746
|
|
713
|
-
context "against columns ranked with arrays" do
|
747
|
+
context "when against columns ranked with arrays" do
|
714
748
|
before do
|
715
749
|
ModelWithPgSearch.pg_search_scope :search_weighted_by_array_of_arrays,
|
716
|
-
|
750
|
+
against: [[:content, 'B'], [:title, 'A']]
|
717
751
|
end
|
718
752
|
|
719
753
|
it "returns results sorted by weighted rank" do
|
720
|
-
loser = ModelWithPgSearch.create!(:
|
721
|
-
winner = ModelWithPgSearch.create!(:
|
754
|
+
loser = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
|
755
|
+
winner = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
|
722
756
|
|
723
757
|
results = ModelWithPgSearch.search_weighted_by_array_of_arrays('foo').with_pg_search_rank
|
724
758
|
expect(results[0].pg_search_rank).to be > results[1].pg_search_rank
|
@@ -726,15 +760,15 @@ describe "an Active Record model which includes PgSearch" do
|
|
726
760
|
end
|
727
761
|
end
|
728
762
|
|
729
|
-
context "against columns ranked with a hash" do
|
763
|
+
context "when against columns ranked with a hash" do
|
730
764
|
before do
|
731
765
|
ModelWithPgSearch.pg_search_scope :search_weighted_by_hash,
|
732
|
-
|
766
|
+
against: { content: 'B', title: 'A' }
|
733
767
|
end
|
734
768
|
|
735
769
|
it "returns results sorted by weighted rank" do
|
736
|
-
loser = ModelWithPgSearch.create!(:
|
737
|
-
winner = ModelWithPgSearch.create!(:
|
770
|
+
loser = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
|
771
|
+
winner = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
|
738
772
|
|
739
773
|
results = ModelWithPgSearch.search_weighted_by_hash('foo').with_pg_search_rank
|
740
774
|
expect(results[0].pg_search_rank).to be > results[1].pg_search_rank
|
@@ -742,15 +776,15 @@ describe "an Active Record model which includes PgSearch" do
|
|
742
776
|
end
|
743
777
|
end
|
744
778
|
|
745
|
-
context "against columns of which only some are ranked" do
|
779
|
+
context "when against columns of which only some are ranked" do
|
746
780
|
before do
|
747
781
|
ModelWithPgSearch.pg_search_scope :search_weighted,
|
748
|
-
|
782
|
+
against: [:content, [:title, 'A']]
|
749
783
|
end
|
750
784
|
|
751
785
|
it "returns results sorted by weighted rank using an implied low rank for unranked columns" do
|
752
|
-
loser = ModelWithPgSearch.create!(:
|
753
|
-
winner = ModelWithPgSearch.create!(:
|
786
|
+
loser = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
|
787
|
+
winner = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
|
754
788
|
|
755
789
|
results = ModelWithPgSearch.search_weighted('foo').with_pg_search_rank
|
756
790
|
expect(results[0].pg_search_rank).to be > results[1].pg_search_rank
|
@@ -758,20 +792,20 @@ describe "an Active Record model which includes PgSearch" do
|
|
758
792
|
end
|
759
793
|
end
|
760
794
|
|
761
|
-
context "searching any_word option" do
|
795
|
+
context "when searching any_word option" do
|
762
796
|
before do
|
763
797
|
ModelWithPgSearch.pg_search_scope :search_title_with_any_word,
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
798
|
+
against: :title,
|
799
|
+
using: {
|
800
|
+
tsearch: { any_word: true }
|
801
|
+
}
|
768
802
|
|
769
803
|
ModelWithPgSearch.pg_search_scope :search_title_with_all_words,
|
770
|
-
|
804
|
+
against: :title
|
771
805
|
end
|
772
806
|
|
773
807
|
it "returns all results containing any word in their title" do
|
774
|
-
numbers = %w[one two three four].map { |number| ModelWithPgSearch.create!(:
|
808
|
+
numbers = %w[one two three four].map { |number| ModelWithPgSearch.create!(title: number) }
|
775
809
|
|
776
810
|
results = ModelWithPgSearch.search_title_with_any_word("one two three four")
|
777
811
|
|
@@ -786,21 +820,21 @@ describe "an Active Record model which includes PgSearch" do
|
|
786
820
|
context "with :negation" do
|
787
821
|
before do
|
788
822
|
ModelWithPgSearch.pg_search_scope :search_with_negation,
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
823
|
+
against: :title,
|
824
|
+
using: {
|
825
|
+
tsearch: { negation: true }
|
826
|
+
}
|
793
827
|
end
|
794
828
|
|
795
829
|
it "doesn't return results that contain terms prepended with '!'" do
|
796
830
|
included = [
|
797
|
-
ModelWithPgSearch.create!(:
|
798
|
-
ModelWithPgSearch.create!(:
|
831
|
+
ModelWithPgSearch.create!(title: "one fish"),
|
832
|
+
ModelWithPgSearch.create!(title: "two fish")
|
799
833
|
]
|
800
834
|
|
801
835
|
excluded = [
|
802
|
-
ModelWithPgSearch.create!(:
|
803
|
-
ModelWithPgSearch.create!(:
|
836
|
+
ModelWithPgSearch.create!(title: "red fish"),
|
837
|
+
ModelWithPgSearch.create!(title: "blue fish")
|
804
838
|
]
|
805
839
|
|
806
840
|
results = ModelWithPgSearch.search_with_negation("fish !red !blue")
|
@@ -813,19 +847,19 @@ describe "an Active Record model which includes PgSearch" do
|
|
813
847
|
context "without :negation" do
|
814
848
|
before do
|
815
849
|
ModelWithPgSearch.pg_search_scope :search_without_negation,
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
850
|
+
against: :title,
|
851
|
+
using: {
|
852
|
+
tsearch: {}
|
853
|
+
}
|
820
854
|
end
|
821
855
|
|
822
856
|
it "return results that contain terms prepended with '!'" do
|
823
857
|
included = [
|
824
|
-
ModelWithPgSearch.create!(:
|
858
|
+
ModelWithPgSearch.create!(title: "!bang")
|
825
859
|
]
|
826
860
|
|
827
861
|
excluded = [
|
828
|
-
ModelWithPgSearch.create!(:
|
862
|
+
ModelWithPgSearch.create!(title: "?question")
|
829
863
|
]
|
830
864
|
|
831
865
|
results = ModelWithPgSearch.search_without_negation("!bang")
|
@@ -836,30 +870,30 @@ describe "an Active Record model which includes PgSearch" do
|
|
836
870
|
end
|
837
871
|
end
|
838
872
|
|
839
|
-
context "using dmetaphone" do
|
873
|
+
context "when using dmetaphone" do
|
840
874
|
before do
|
841
875
|
ModelWithPgSearch.pg_search_scope :with_dmetaphones,
|
842
|
-
|
843
|
-
|
876
|
+
against: %i[title content],
|
877
|
+
using: :dmetaphone
|
844
878
|
end
|
845
879
|
|
846
880
|
it "returns rows where one searchable column and the query share enough dmetaphones" do
|
847
|
-
included = ModelWithPgSearch.create!(:
|
848
|
-
excluded = ModelWithPgSearch.create!(:
|
881
|
+
included = ModelWithPgSearch.create!(title: 'Geoff', content: nil)
|
882
|
+
excluded = ModelWithPgSearch.create!(title: 'Bob', content: nil)
|
849
883
|
results = ModelWithPgSearch.with_dmetaphones('Jeff')
|
850
884
|
expect(results).to eq([included])
|
851
885
|
end
|
852
886
|
|
853
887
|
it "returns rows where multiple searchable columns and the query share enough dmetaphones" do
|
854
|
-
included = ModelWithPgSearch.create!(:
|
855
|
-
excluded = ModelWithPgSearch.create!(:
|
888
|
+
included = ModelWithPgSearch.create!(title: 'Geoff', content: 'George')
|
889
|
+
excluded = ModelWithPgSearch.create!(title: 'Bob', content: 'Jones')
|
856
890
|
results = ModelWithPgSearch.with_dmetaphones('Jeff Jorge')
|
857
891
|
expect(results).to eq([included])
|
858
892
|
end
|
859
893
|
|
860
894
|
it "returns rows that match dmetaphones that are English stopwords" do
|
861
|
-
included = ModelWithPgSearch.create!(:
|
862
|
-
excluded = ModelWithPgSearch.create!(:
|
895
|
+
included = ModelWithPgSearch.create!(title: 'White', content: nil)
|
896
|
+
excluded = ModelWithPgSearch.create!(title: 'Black', content: nil)
|
863
897
|
results = ModelWithPgSearch.with_dmetaphones('Wight')
|
864
898
|
expect(results).to eq([included])
|
865
899
|
end
|
@@ -867,50 +901,50 @@ describe "an Active Record model which includes PgSearch" do
|
|
867
901
|
it "can handle terms that do not have a dmetaphone equivalent" do
|
868
902
|
term_with_blank_metaphone = "w"
|
869
903
|
|
870
|
-
included = ModelWithPgSearch.create!(:
|
871
|
-
excluded = ModelWithPgSearch.create!(:
|
904
|
+
included = ModelWithPgSearch.create!(title: 'White', content: nil)
|
905
|
+
excluded = ModelWithPgSearch.create!(title: 'Black', content: nil)
|
872
906
|
|
873
907
|
results = ModelWithPgSearch.with_dmetaphones('Wight W')
|
874
908
|
expect(results).to eq([included])
|
875
909
|
end
|
876
910
|
end
|
877
911
|
|
878
|
-
context "using multiple features" do
|
912
|
+
context "when using multiple features" do
|
879
913
|
before do
|
880
914
|
ModelWithPgSearch.pg_search_scope :with_tsearch,
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
915
|
+
against: :title,
|
916
|
+
using: [
|
917
|
+
[:tsearch, { dictionary: 'english' }]
|
918
|
+
]
|
885
919
|
|
886
920
|
ModelWithPgSearch.pg_search_scope :with_trigram,
|
887
|
-
|
888
|
-
|
921
|
+
against: :title,
|
922
|
+
using: :trigram
|
889
923
|
|
890
924
|
ModelWithPgSearch.pg_search_scope :with_trigram_and_ignoring_accents,
|
891
|
-
|
892
|
-
|
893
|
-
|
925
|
+
against: :title,
|
926
|
+
ignoring: :accents,
|
927
|
+
using: :trigram
|
894
928
|
|
895
929
|
ModelWithPgSearch.pg_search_scope :with_tsearch_and_trigram,
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
930
|
+
against: :title,
|
931
|
+
using: [
|
932
|
+
[:tsearch, { dictionary: 'english' }],
|
933
|
+
:trigram
|
934
|
+
]
|
901
935
|
|
902
936
|
ModelWithPgSearch.pg_search_scope :complex_search,
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
937
|
+
against: %i[content title],
|
938
|
+
ignoring: :accents,
|
939
|
+
using: {
|
940
|
+
tsearch: { dictionary: 'english' },
|
941
|
+
dmetaphone: {},
|
942
|
+
trigram: {}
|
943
|
+
}
|
910
944
|
end
|
911
945
|
|
912
946
|
it "returns rows that match using any of the features" do
|
913
|
-
record = ModelWithPgSearch.create!(:
|
947
|
+
record = ModelWithPgSearch.create!(title: "tiling is grouty")
|
914
948
|
|
915
949
|
# matches trigram only
|
916
950
|
trigram_query = "ling is grouty"
|
@@ -948,33 +982,40 @@ describe "an Active Record model which includes PgSearch" do
|
|
948
982
|
end
|
949
983
|
|
950
984
|
context "with feature-specific configuration" do
|
951
|
-
|
952
|
-
|
953
|
-
@trigram_config = trigram_config = {:foo => 'bar'}
|
985
|
+
let(:tsearch_config) { { dictionary: 'english' } }
|
986
|
+
let(:trigram_config) { { foo: 'bar' } }
|
954
987
|
|
988
|
+
before do
|
955
989
|
ModelWithPgSearch.pg_search_scope :with_tsearch_and_trigram_using_hash,
|
956
|
-
|
957
|
-
|
958
|
-
:tsearch => tsearch_config,
|
959
|
-
:trigram => trigram_config
|
960
|
-
}
|
990
|
+
against: :title,
|
991
|
+
using: { tsearch: tsearch_config, trigram: trigram_config }
|
961
992
|
end
|
962
993
|
|
963
|
-
it "
|
964
|
-
|
965
|
-
|
966
|
-
:
|
994
|
+
it "passes the custom configuration down to the specified feature" do
|
995
|
+
tsearch_feature = instance_double(
|
996
|
+
"PgSearch::Features::TSearch",
|
997
|
+
conditions: Arel::Nodes::Grouping.new(Arel.sql("1 = 1")),
|
998
|
+
rank: Arel::Nodes::Grouping.new(Arel.sql("1.0"))
|
967
999
|
)
|
968
1000
|
|
969
|
-
|
970
|
-
|
1001
|
+
trigram_feature = instance_double(
|
1002
|
+
"PgSearch::Features::Trigram",
|
1003
|
+
conditions: Arel::Nodes::Grouping.new(Arel.sql("1 = 1")),
|
1004
|
+
rank: Arel::Nodes::Grouping.new(Arel.sql("1.0"))
|
1005
|
+
)
|
1006
|
+
|
1007
|
+
allow(PgSearch::Features::TSearch).to receive(:new).with(anything, tsearch_config, anything, anything, anything).and_return(tsearch_feature)
|
1008
|
+
allow(PgSearch::Features::Trigram).to receive(:new).with(anything, trigram_config, anything, anything, anything).and_return(trigram_feature)
|
971
1009
|
|
972
1010
|
ModelWithPgSearch.with_tsearch_and_trigram_using_hash("foo")
|
1011
|
+
|
1012
|
+
expect(PgSearch::Features::TSearch).to have_received(:new).with(anything, tsearch_config, anything, anything, anything).at_least(:once)
|
1013
|
+
expect(PgSearch::Features::Trigram).to have_received(:new).with(anything, trigram_config, anything, anything, anything).at_least(:once)
|
973
1014
|
end
|
974
1015
|
end
|
975
1016
|
end
|
976
1017
|
|
977
|
-
context "using a tsvector column and an association" do
|
1018
|
+
context "when using a tsvector column and an association" do
|
978
1019
|
with_model :Comment do
|
979
1020
|
table do |t|
|
980
1021
|
t.integer :post_id
|
@@ -993,7 +1034,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
993
1034
|
end
|
994
1035
|
|
995
1036
|
model do
|
996
|
-
include PgSearch
|
1037
|
+
include PgSearch::Model
|
997
1038
|
has_many :comments
|
998
1039
|
end
|
999
1040
|
end
|
@@ -1002,7 +1043,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
1002
1043
|
let!(:unexpected) { Post.create!(content: 'longcat is looooooooong') }
|
1003
1044
|
|
1004
1045
|
before do
|
1005
|
-
ActiveRecord::Base.connection.execute
|
1046
|
+
ActiveRecord::Base.connection.execute <<~SQL.squish
|
1006
1047
|
UPDATE #{Post.quoted_table_name}
|
1007
1048
|
SET content_tsvector = to_tsvector('english'::regconfig, #{Post.quoted_table_name}."content")
|
1008
1049
|
SQL
|
@@ -1011,41 +1052,41 @@ describe "an Active Record model which includes PgSearch" do
|
|
1011
1052
|
unexpected.comments.create(body: 'commentwo')
|
1012
1053
|
|
1013
1054
|
Post.pg_search_scope :search_by_content_with_tsvector,
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1055
|
+
associated_against: { comments: [:body] },
|
1056
|
+
using: {
|
1057
|
+
tsearch: {
|
1058
|
+
tsvector_column: 'content_tsvector',
|
1059
|
+
dictionary: 'english'
|
1060
|
+
}
|
1061
|
+
}
|
1021
1062
|
end
|
1022
1063
|
|
1023
|
-
it "
|
1064
|
+
it "finds by the tsvector column" do
|
1024
1065
|
expect(Post.search_by_content_with_tsvector("phooey").map(&:id)).to eq([expected.id])
|
1025
1066
|
end
|
1026
1067
|
|
1027
|
-
it "
|
1068
|
+
it "finds by the associated record" do
|
1028
1069
|
expect(Post.search_by_content_with_tsvector("commentone").map(&:id)).to eq([expected.id])
|
1029
1070
|
end
|
1030
1071
|
|
1031
|
-
it '
|
1072
|
+
it 'finds by a combination of the two' do
|
1032
1073
|
expect(Post.search_by_content_with_tsvector("phooey commentone").map(&:id)).to eq([expected.id])
|
1033
1074
|
end
|
1034
1075
|
end
|
1035
1076
|
|
1036
|
-
context 'using multiple tsvector columns' do
|
1077
|
+
context 'when using multiple tsvector columns' do
|
1037
1078
|
with_model :ModelWithTsvector do
|
1038
1079
|
model do
|
1039
|
-
include PgSearch
|
1080
|
+
include PgSearch::Model
|
1040
1081
|
|
1041
1082
|
pg_search_scope :search_by_multiple_tsvector_columns,
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1083
|
+
against: ['content', 'message'],
|
1084
|
+
using: {
|
1085
|
+
tsearch: {
|
1086
|
+
tsvector_column: ['content_tsvector', 'message_tsvector'],
|
1087
|
+
dictionary: 'english'
|
1088
|
+
}
|
1089
|
+
}
|
1049
1090
|
end
|
1050
1091
|
end
|
1051
1092
|
|
@@ -1057,40 +1098,41 @@ describe "an Active Record model which includes PgSearch" do
|
|
1057
1098
|
end
|
1058
1099
|
end
|
1059
1100
|
|
1060
|
-
context "using a tsvector column with" do
|
1101
|
+
context "when using a tsvector column with" do
|
1061
1102
|
with_model :ModelWithTsvector do
|
1062
1103
|
table do |t|
|
1063
1104
|
t.text 'content'
|
1064
1105
|
t.tsvector 'content_tsvector'
|
1065
1106
|
end
|
1066
1107
|
|
1067
|
-
model { include PgSearch }
|
1108
|
+
model { include PgSearch::Model }
|
1068
1109
|
end
|
1069
1110
|
|
1070
|
-
let!(:expected) { ModelWithTsvector.create!(:
|
1071
|
-
let!(:unexpected) { ModelWithTsvector.create!(:content => 'longcat is looooooooong') }
|
1111
|
+
let!(:expected) { ModelWithTsvector.create!(content: 'tiling is grouty') }
|
1072
1112
|
|
1073
1113
|
before do
|
1074
|
-
|
1114
|
+
ModelWithTsvector.create!(content: 'longcat is looooooooong')
|
1115
|
+
|
1116
|
+
ActiveRecord::Base.connection.execute <<~SQL.squish
|
1075
1117
|
UPDATE #{ModelWithTsvector.quoted_table_name}
|
1076
1118
|
SET content_tsvector = to_tsvector('english'::regconfig, #{ModelWithTsvector.quoted_table_name}."content")
|
1077
1119
|
SQL
|
1078
1120
|
|
1079
1121
|
ModelWithTsvector.pg_search_scope :search_by_content_with_tsvector,
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1122
|
+
against: :content,
|
1123
|
+
using: {
|
1124
|
+
tsearch: {
|
1125
|
+
tsvector_column: 'content_tsvector',
|
1126
|
+
dictionary: 'english'
|
1127
|
+
}
|
1128
|
+
}
|
1087
1129
|
end
|
1088
1130
|
|
1089
|
-
it "
|
1131
|
+
it "does not use to_tsvector in the query" do
|
1090
1132
|
expect(ModelWithTsvector.search_by_content_with_tsvector("tiles").to_sql).not_to match(/to_tsvector/)
|
1091
1133
|
end
|
1092
1134
|
|
1093
|
-
it "
|
1135
|
+
it "finds the expected result" do
|
1094
1136
|
expect(ModelWithTsvector.search_by_content_with_tsvector("tiles").map(&:id)).to eq([expected.id])
|
1095
1137
|
end
|
1096
1138
|
|
@@ -1106,7 +1148,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
1106
1148
|
ModelWithTsvector.has_many :another_models
|
1107
1149
|
end
|
1108
1150
|
|
1109
|
-
it "
|
1151
|
+
it "refers to the tsvector column in the query unambiguously" do
|
1110
1152
|
expect {
|
1111
1153
|
ModelWithTsvector.joins(:another_models).search_by_content_with_tsvector("test").to_a
|
1112
1154
|
}.not_to raise_exception
|
@@ -1114,46 +1156,55 @@ describe "an Active Record model which includes PgSearch" do
|
|
1114
1156
|
end
|
1115
1157
|
end
|
1116
1158
|
|
1117
|
-
context "ignoring accents" do
|
1159
|
+
context "when ignoring accents" do
|
1118
1160
|
before do
|
1119
1161
|
ModelWithPgSearch.pg_search_scope :search_title_without_accents,
|
1120
|
-
|
1121
|
-
|
1162
|
+
against: :title,
|
1163
|
+
ignoring: :accents
|
1122
1164
|
end
|
1123
1165
|
|
1124
1166
|
it "returns rows that match the query but not its accents" do
|
1125
1167
|
# \303\241 is a with acute accent
|
1126
1168
|
# \303\251 is e with acute accent
|
1127
1169
|
|
1128
|
-
included = ModelWithPgSearch.create!(:
|
1170
|
+
included = ModelWithPgSearch.create!(title: "\303\241bcdef")
|
1129
1171
|
|
1130
1172
|
results = ModelWithPgSearch.search_title_without_accents("abcd\303\251f")
|
1131
1173
|
expect(results).to eq([included])
|
1132
1174
|
end
|
1175
|
+
|
1176
|
+
context "when the query includes accents" do
|
1177
|
+
it "does not create an erroneous tsquery expression" do
|
1178
|
+
included = ModelWithPgSearch.create!(title: "Weird L‘Content")
|
1179
|
+
|
1180
|
+
results = ModelWithPgSearch.search_title_without_accents("L‘Content")
|
1181
|
+
expect(results).to eq([included])
|
1182
|
+
end
|
1183
|
+
end
|
1133
1184
|
end
|
1134
1185
|
|
1135
1186
|
context "when passed a :ranked_by expression" do
|
1136
1187
|
before do
|
1137
1188
|
ModelWithPgSearch.pg_search_scope :search_content_with_default_rank,
|
1138
|
-
|
1189
|
+
against: :content
|
1139
1190
|
|
1140
1191
|
ModelWithPgSearch.pg_search_scope :search_content_with_importance_as_rank,
|
1141
|
-
|
1142
|
-
|
1192
|
+
against: :content,
|
1193
|
+
ranked_by: "importance"
|
1143
1194
|
|
1144
1195
|
ModelWithPgSearch.pg_search_scope :search_content_with_importance_as_rank_multiplier,
|
1145
|
-
|
1146
|
-
|
1196
|
+
against: :content,
|
1197
|
+
ranked_by: ":tsearch * importance"
|
1147
1198
|
end
|
1148
1199
|
|
1149
|
-
it "
|
1150
|
-
ModelWithPgSearch.create!(:
|
1200
|
+
it "returns records with a rank attribute equal to the :ranked_by expression" do
|
1201
|
+
ModelWithPgSearch.create!(content: 'foo', importance: 10)
|
1151
1202
|
results = ModelWithPgSearch.search_content_with_importance_as_rank("foo").with_pg_search_rank
|
1152
1203
|
expect(results.first.pg_search_rank).to eq(10)
|
1153
1204
|
end
|
1154
1205
|
|
1155
|
-
it "
|
1156
|
-
ModelWithPgSearch.create!(:
|
1206
|
+
it "substitutes :tsearch with the tsearch rank expression in the :ranked_by expression" do
|
1207
|
+
ModelWithPgSearch.create!(content: 'foo', importance: 10)
|
1157
1208
|
|
1158
1209
|
tsearch_result =
|
1159
1210
|
ModelWithPgSearch.search_content_with_default_rank("foo").with_pg_search_rank.first
|
@@ -1162,19 +1213,19 @@ describe "an Active Record model which includes PgSearch" do
|
|
1162
1213
|
|
1163
1214
|
multiplied_result =
|
1164
1215
|
ModelWithPgSearch.search_content_with_importance_as_rank_multiplier("foo")
|
1165
|
-
|
1166
|
-
|
1216
|
+
.with_pg_search_rank
|
1217
|
+
.first
|
1167
1218
|
|
1168
1219
|
multiplied_rank = multiplied_result.pg_search_rank
|
1169
1220
|
|
1170
1221
|
expect(multiplied_rank).to be_within(0.001).of(tsearch_rank * 10)
|
1171
1222
|
end
|
1172
1223
|
|
1173
|
-
it "
|
1224
|
+
it "returns results in descending order of the value of the rank expression" do
|
1174
1225
|
records = [
|
1175
|
-
ModelWithPgSearch.create!(:
|
1176
|
-
ModelWithPgSearch.create!(:
|
1177
|
-
ModelWithPgSearch.create!(:
|
1226
|
+
ModelWithPgSearch.create!(content: 'foo', importance: 1),
|
1227
|
+
ModelWithPgSearch.create!(content: 'foo', importance: 3),
|
1228
|
+
ModelWithPgSearch.create!(content: 'foo', importance: 2)
|
1178
1229
|
]
|
1179
1230
|
|
1180
1231
|
results = ModelWithPgSearch.search_content_with_importance_as_rank("foo")
|
@@ -1184,12 +1235,13 @@ describe "an Active Record model which includes PgSearch" do
|
|
1184
1235
|
%w[tsearch trigram dmetaphone].each do |feature|
|
1185
1236
|
context "using the #{feature} ranking algorithm" do
|
1186
1237
|
let(:scope_name) { :"search_content_ranked_by_#{feature}" }
|
1238
|
+
|
1187
1239
|
before do
|
1188
1240
|
ModelWithPgSearch.pg_search_scope scope_name,
|
1189
|
-
|
1190
|
-
|
1241
|
+
against: :content,
|
1242
|
+
ranked_by: ":#{feature}"
|
1191
1243
|
|
1192
|
-
ModelWithPgSearch.create!(:
|
1244
|
+
ModelWithPgSearch.create!(content: 'foo')
|
1193
1245
|
end
|
1194
1246
|
|
1195
1247
|
context "when .with_pg_search_rank is chained after" do
|
@@ -1220,45 +1272,45 @@ describe "an Active Record model which includes PgSearch" do
|
|
1220
1272
|
end
|
1221
1273
|
end
|
1222
1274
|
|
1223
|
-
context "using the tsearch ranking algorithm" do
|
1275
|
+
context "when using the tsearch ranking algorithm" do
|
1224
1276
|
it "sorts results by the tsearch rank" do
|
1225
1277
|
ModelWithPgSearch.pg_search_scope :search_content_ranked_by_tsearch,
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1278
|
+
using: :tsearch,
|
1279
|
+
against: :content,
|
1280
|
+
ranked_by: ":tsearch"
|
1229
1281
|
|
1230
|
-
once = ModelWithPgSearch.create!(:
|
1231
|
-
twice = ModelWithPgSearch.create!(:
|
1282
|
+
once = ModelWithPgSearch.create!(content: 'foo bar')
|
1283
|
+
twice = ModelWithPgSearch.create!(content: 'foo foo')
|
1232
1284
|
|
1233
1285
|
results = ModelWithPgSearch.search_content_ranked_by_tsearch('foo')
|
1234
1286
|
expect(results.find_index(twice)).to be < results.find_index(once)
|
1235
1287
|
end
|
1236
1288
|
end
|
1237
1289
|
|
1238
|
-
context "using the trigram ranking algorithm" do
|
1290
|
+
context "when using the trigram ranking algorithm" do
|
1239
1291
|
it "sorts results by the trigram rank" do
|
1240
1292
|
ModelWithPgSearch.pg_search_scope :search_content_ranked_by_trigram,
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1293
|
+
using: :trigram,
|
1294
|
+
against: :content,
|
1295
|
+
ranked_by: ":trigram"
|
1244
1296
|
|
1245
|
-
close = ModelWithPgSearch.create!(:
|
1246
|
-
exact = ModelWithPgSearch.create!(:
|
1297
|
+
close = ModelWithPgSearch.create!(content: 'abcdef')
|
1298
|
+
exact = ModelWithPgSearch.create!(content: 'abc')
|
1247
1299
|
|
1248
1300
|
results = ModelWithPgSearch.search_content_ranked_by_trigram('abc')
|
1249
1301
|
expect(results.find_index(exact)).to be < results.find_index(close)
|
1250
1302
|
end
|
1251
1303
|
end
|
1252
1304
|
|
1253
|
-
context "using the dmetaphone ranking algorithm" do
|
1305
|
+
context "when using the dmetaphone ranking algorithm" do
|
1254
1306
|
it "sorts results by the dmetaphone rank" do
|
1255
1307
|
ModelWithPgSearch.pg_search_scope :search_content_ranked_by_dmetaphone,
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1308
|
+
using: :dmetaphone,
|
1309
|
+
against: :content,
|
1310
|
+
ranked_by: ":dmetaphone"
|
1259
1311
|
|
1260
|
-
once = ModelWithPgSearch.create!(:
|
1261
|
-
twice = ModelWithPgSearch.create!(:
|
1312
|
+
once = ModelWithPgSearch.create!(content: 'Phoo Bar')
|
1313
|
+
twice = ModelWithPgSearch.create!(content: 'Phoo Fu')
|
1262
1314
|
|
1263
1315
|
results = ModelWithPgSearch.search_content_ranked_by_dmetaphone('foo')
|
1264
1316
|
expect(results.find_index(twice)).to be < results.find_index(once)
|
@@ -1269,17 +1321,17 @@ describe "an Active Record model which includes PgSearch" do
|
|
1269
1321
|
context "when there is a sort only feature" do
|
1270
1322
|
it "excludes that feature from the conditions, but uses it in the sorting" do
|
1271
1323
|
ModelWithPgSearch.pg_search_scope :search_content_ranked_by_dmetaphone,
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
exact = ModelWithPgSearch.create!(:
|
1280
|
-
one_exact_one_close = ModelWithPgSearch.create!(:
|
1281
|
-
one_exact = ModelWithPgSearch.create!(:
|
1282
|
-
one_close = ModelWithPgSearch.create!(:
|
1324
|
+
against: :content,
|
1325
|
+
using: {
|
1326
|
+
tsearch: { any_word: true, prefix: true },
|
1327
|
+
dmetaphone: { any_word: true, prefix: true, sort_only: true }
|
1328
|
+
},
|
1329
|
+
ranked_by: ":tsearch + (0.5 * :dmetaphone)"
|
1330
|
+
|
1331
|
+
exact = ModelWithPgSearch.create!(content: "ash hines")
|
1332
|
+
one_exact_one_close = ModelWithPgSearch.create!(content: "ash heinz")
|
1333
|
+
one_exact = ModelWithPgSearch.create!(content: "ash smith")
|
1334
|
+
one_close = ModelWithPgSearch.create!(content: "leigh heinz")
|
1283
1335
|
|
1284
1336
|
results = ModelWithPgSearch.search_content_ranked_by_dmetaphone("ash hines")
|
1285
1337
|
expect(results).to eq [exact, one_exact_one_close, one_exact]
|
@@ -1287,3 +1339,4 @@ describe "an Active Record model which includes PgSearch" do
|
|
1287
1339
|
end
|
1288
1340
|
end
|
1289
1341
|
end
|
1342
|
+
# rubocop:enable RSpec/NestedGroups
|