pg_search 2.1.2 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +11 -7
- data/.travis.yml +33 -42
- data/CHANGELOG.md +140 -112
- data/CONTRIBUTING.md +5 -3
- data/Gemfile +6 -4
- data/LICENSE +1 -1
- data/README.md +221 -159
- data/Rakefile +3 -5
- 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 +8 -4
- data/lib/pg_search/document.rb +5 -3
- data/lib/pg_search/features/dmetaphone.rb +3 -1
- 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 +4 -1
- 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/create_pg_search_documents.rb.erb +1 -1
- data/lib/pg_search/multisearch/rebuilder.rb +7 -3
- data/lib/pg_search/multisearch.rb +4 -4
- 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 +21 -41
- data/lib/pg_search/tasks.rb +3 -0
- data/lib/pg_search/version.rb +3 -1
- data/lib/pg_search.rb +10 -5
- data/pg_search.gemspec +8 -7
- data/spec/integration/associations_spec.rb +103 -101
- data/spec/integration/pagination_spec.rb +9 -7
- data/spec/integration/pg_search_spec.rb +266 -255
- data/spec/integration/single_table_inheritance_spec.rb +16 -15
- data/spec/lib/pg_search/configuration/association_spec.rb +7 -5
- data/spec/lib/pg_search/configuration/column_spec.rb +2 -0
- data/spec/lib/pg_search/configuration/foreign_column_spec.rb +5 -3
- data/spec/lib/pg_search/features/dmetaphone_spec.rb +6 -4
- data/spec/lib/pg_search/features/trigram_spec.rb +39 -12
- data/spec/lib/pg_search/features/tsearch_spec.rb +23 -21
- data/spec/lib/pg_search/multisearch/rebuilder_spec.rb +32 -11
- data/spec/lib/pg_search/multisearch_spec.rb +9 -7
- data/spec/lib/pg_search/multisearchable_spec.rb +68 -27
- data/spec/lib/pg_search/normalizer_spec.rb +7 -5
- data/spec/lib/pg_search_spec.rb +33 -31
- data/spec/spec_helper.rb +3 -1
- data/spec/support/database.rb +16 -20
- data/spec/support/with_model.rb +2 -0
- metadata +13 -30
- data/.rubocop_todo.yml +0 -163
- data/Guardfile +0 -6
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
5
|
describe "an Active Record model which includes PgSearch" do
|
@@ -28,7 +30,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
28
30
|
|
29
31
|
describe ".pg_search_scope" do
|
30
32
|
it "builds a chainable scope" do
|
31
|
-
ModelWithPgSearch.pg_search_scope "matching_query", :
|
33
|
+
ModelWithPgSearch.pg_search_scope "matching_query", against: []
|
32
34
|
scope = ModelWithPgSearch.where("1 = 1").matching_query("foo").where("1 = 1")
|
33
35
|
expect(scope).to be_an ActiveRecord::Relation
|
34
36
|
end
|
@@ -36,15 +38,15 @@ describe "an Active Record model which includes PgSearch" do
|
|
36
38
|
context "when passed a lambda" do
|
37
39
|
it "builds a dynamic scope" do
|
38
40
|
ModelWithPgSearch.pg_search_scope :search_title_or_content,
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
lambda { |query, pick_content|
|
42
|
+
{
|
43
|
+
query: query.gsub("-remove-", ""),
|
44
|
+
against: pick_content ? :content : :title
|
45
|
+
}
|
46
|
+
}
|
45
47
|
|
46
|
-
included = ModelWithPgSearch.create!(:
|
47
|
-
excluded = ModelWithPgSearch.create!(:
|
48
|
+
included = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
|
49
|
+
excluded = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
|
48
50
|
|
49
51
|
expect(ModelWithPgSearch.search_title_or_content('fo-remove-o', false)).to eq([included])
|
50
52
|
expect(ModelWithPgSearch.search_title_or_content('b-remove-ar', true)).to eq([included])
|
@@ -54,8 +56,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
54
56
|
context "when an unknown option is passed in" do
|
55
57
|
it "raises an exception when invoked" do
|
56
58
|
ModelWithPgSearch.pg_search_scope :with_unknown_option,
|
57
|
-
|
58
|
-
|
59
|
+
against: :content,
|
60
|
+
foo: :bar
|
59
61
|
|
60
62
|
expect {
|
61
63
|
ModelWithPgSearch.with_unknown_option("foo")
|
@@ -65,7 +67,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
65
67
|
context "dynamically" do
|
66
68
|
it "raises an exception when invoked" do
|
67
69
|
ModelWithPgSearch.pg_search_scope :with_unknown_option,
|
68
|
-
|
70
|
+
->(*) { { against: :content, foo: :bar } }
|
69
71
|
|
70
72
|
expect {
|
71
73
|
ModelWithPgSearch.with_unknown_option("foo")
|
@@ -77,8 +79,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
77
79
|
context "when an unknown :using is passed" do
|
78
80
|
it "raises an exception when invoked" do
|
79
81
|
ModelWithPgSearch.pg_search_scope :with_unknown_using,
|
80
|
-
|
81
|
-
|
82
|
+
against: :content,
|
83
|
+
using: :foo
|
82
84
|
|
83
85
|
expect {
|
84
86
|
ModelWithPgSearch.with_unknown_using("foo")
|
@@ -88,7 +90,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
88
90
|
context "dynamically" do
|
89
91
|
it "raises an exception when invoked" do
|
90
92
|
ModelWithPgSearch.pg_search_scope :with_unknown_using,
|
91
|
-
|
93
|
+
->(*) { { against: :content, using: :foo } }
|
92
94
|
|
93
95
|
expect {
|
94
96
|
ModelWithPgSearch.with_unknown_using("foo")
|
@@ -100,8 +102,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
100
102
|
context "when an unknown :ignoring is passed" do
|
101
103
|
it "raises an exception when invoked" do
|
102
104
|
ModelWithPgSearch.pg_search_scope :with_unknown_ignoring,
|
103
|
-
|
104
|
-
|
105
|
+
against: :content,
|
106
|
+
ignoring: :foo
|
105
107
|
|
106
108
|
expect {
|
107
109
|
ModelWithPgSearch.with_unknown_ignoring("foo")
|
@@ -111,7 +113,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
111
113
|
context "dynamically" do
|
112
114
|
it "raises an exception when invoked" do
|
113
115
|
ModelWithPgSearch.pg_search_scope :with_unknown_ignoring,
|
114
|
-
|
116
|
+
->(*) { { against: :content, ignoring: :foo } }
|
115
117
|
|
116
118
|
expect {
|
117
119
|
ModelWithPgSearch.with_unknown_ignoring("foo")
|
@@ -130,7 +132,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
130
132
|
|
131
133
|
context "dynamically" do
|
132
134
|
it "raises an exception when invoked" do
|
133
|
-
ModelWithPgSearch.pg_search_scope :with_unknown_ignoring, ->(*){ {} }
|
135
|
+
ModelWithPgSearch.pg_search_scope :with_unknown_ignoring, ->(*) { {} }
|
134
136
|
|
135
137
|
expect {
|
136
138
|
ModelWithPgSearch.with_unknown_ignoring("foo")
|
@@ -144,7 +146,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
144
146
|
describe "a search scope" do
|
145
147
|
context "against a single column" do
|
146
148
|
before do
|
147
|
-
ModelWithPgSearch.pg_search_scope :search_content, :
|
149
|
+
ModelWithPgSearch.pg_search_scope :search_content, against: :content
|
148
150
|
end
|
149
151
|
|
150
152
|
context "when chained after a select() scope" do
|
@@ -222,7 +224,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
222
224
|
has_many :houses
|
223
225
|
pg_search_scope :named, against: [:name]
|
224
226
|
scope :with_house_in_city, lambda { |city|
|
225
|
-
joins(:houses).where(House.table_name.to_sym => {city: city})
|
227
|
+
joins(:houses).where(House.table_name.to_sym => { city: city })
|
226
228
|
}
|
227
229
|
scope :house_search_city, lambda { |query|
|
228
230
|
joins(:houses).merge(House.search_city(query))
|
@@ -286,15 +288,15 @@ describe "an Active Record model which includes PgSearch" do
|
|
286
288
|
end
|
287
289
|
|
288
290
|
it "returns an empty array when a blank query is passed in" do
|
289
|
-
ModelWithPgSearch.create!(:
|
291
|
+
ModelWithPgSearch.create!(content: 'foo')
|
290
292
|
|
291
293
|
results = ModelWithPgSearch.search_content('')
|
292
294
|
expect(results).to eq([])
|
293
295
|
end
|
294
296
|
|
295
297
|
it "returns rows where the column contains the term in the query" do
|
296
|
-
included = ModelWithPgSearch.create!(:
|
297
|
-
excluded = ModelWithPgSearch.create!(:
|
298
|
+
included = ModelWithPgSearch.create!(content: 'foo')
|
299
|
+
excluded = ModelWithPgSearch.create!(content: 'bar')
|
298
300
|
|
299
301
|
results = ModelWithPgSearch.search_content('foo')
|
300
302
|
expect(results).to include(included)
|
@@ -302,24 +304,24 @@ describe "an Active Record model which includes PgSearch" do
|
|
302
304
|
end
|
303
305
|
|
304
306
|
it "returns the correct count" do
|
305
|
-
ModelWithPgSearch.create!(:
|
306
|
-
ModelWithPgSearch.create!(:
|
307
|
+
ModelWithPgSearch.create!(content: 'foo')
|
308
|
+
ModelWithPgSearch.create!(content: 'bar')
|
307
309
|
|
308
310
|
results = ModelWithPgSearch.search_content('foo')
|
309
311
|
expect(results.count).to eq 1
|
310
312
|
end
|
311
313
|
|
312
314
|
it "returns the correct count(:all)" do
|
313
|
-
ModelWithPgSearch.create!(:
|
314
|
-
ModelWithPgSearch.create!(:
|
315
|
+
ModelWithPgSearch.create!(content: 'foo')
|
316
|
+
ModelWithPgSearch.create!(content: 'bar')
|
315
317
|
|
316
318
|
results = ModelWithPgSearch.search_content('foo')
|
317
319
|
expect(results.count(:all)).to eq 1
|
318
320
|
end
|
319
321
|
|
320
322
|
it "supports #select" do
|
321
|
-
record = ModelWithPgSearch.create!(:
|
322
|
-
other_record = ModelWithPgSearch.create!(:
|
323
|
+
record = ModelWithPgSearch.create!(content: 'foo')
|
324
|
+
other_record = ModelWithPgSearch.create!(content: 'bar')
|
323
325
|
|
324
326
|
records_with_only_id = ModelWithPgSearch.search_content('foo').select('id')
|
325
327
|
expect(records_with_only_id.length).to eq 1
|
@@ -330,27 +332,27 @@ describe "an Active Record model which includes PgSearch" do
|
|
330
332
|
end
|
331
333
|
|
332
334
|
it "supports #pluck" do
|
333
|
-
record = ModelWithPgSearch.create!(:
|
334
|
-
other_record = ModelWithPgSearch.create!(:
|
335
|
+
record = ModelWithPgSearch.create!(content: 'foo')
|
336
|
+
other_record = ModelWithPgSearch.create!(content: 'bar')
|
335
337
|
|
336
338
|
ids = ModelWithPgSearch.search_content('foo').pluck('id')
|
337
339
|
expect(ids).to eq [record.id]
|
338
340
|
end
|
339
341
|
|
340
342
|
it "supports adding where clauses using the pg_search.rank" do
|
341
|
-
once = ModelWithPgSearch.create!(:
|
342
|
-
twice = ModelWithPgSearch.create!(:
|
343
|
+
once = ModelWithPgSearch.create!(content: 'foo bar')
|
344
|
+
twice = ModelWithPgSearch.create!(content: 'foo foo')
|
343
345
|
|
344
346
|
records = ModelWithPgSearch.search_content('foo')
|
345
|
-
|
347
|
+
.where("#{PgSearch::Configuration.alias(ModelWithPgSearch.table_name)}.rank > 0.07")
|
346
348
|
|
347
349
|
expect(records).to eq [twice]
|
348
350
|
end
|
349
351
|
|
350
352
|
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!(:
|
353
|
+
included = [ModelWithPgSearch.create!(content: 'foo bar'),
|
354
|
+
ModelWithPgSearch.create!(content: 'bar foo')]
|
355
|
+
excluded = ModelWithPgSearch.create!(content: 'foo')
|
354
356
|
|
355
357
|
results = ModelWithPgSearch.search_content('foo bar')
|
356
358
|
expect(results).to match_array(included)
|
@@ -358,8 +360,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
358
360
|
end
|
359
361
|
|
360
362
|
it "returns rows that match the query but not its case" do
|
361
|
-
included = [ModelWithPgSearch.create!(:
|
362
|
-
ModelWithPgSearch.create!(:
|
363
|
+
included = [ModelWithPgSearch.create!(content: "foo"),
|
364
|
+
ModelWithPgSearch.create!(content: "FOO")]
|
363
365
|
|
364
366
|
results = ModelWithPgSearch.search_content("Foo")
|
365
367
|
expect(results).to match_array(included)
|
@@ -369,8 +371,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
369
371
|
# \303\241 is a with acute accent
|
370
372
|
# \303\251 is e with acute accent
|
371
373
|
|
372
|
-
included = ModelWithPgSearch.create!(:
|
373
|
-
excluded = ModelWithPgSearch.create!(:
|
374
|
+
included = ModelWithPgSearch.create!(content: "abcd\303\251f")
|
375
|
+
excluded = ModelWithPgSearch.create!(content: "\303\241bcdef")
|
374
376
|
|
375
377
|
results = ModelWithPgSearch.search_content("abcd\303\251f")
|
376
378
|
expect(results).to eq([included])
|
@@ -378,8 +380,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
378
380
|
end
|
379
381
|
|
380
382
|
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!(:
|
383
|
+
included = ModelWithPgSearch.create!(content: 'pre')
|
384
|
+
excluded = ModelWithPgSearch.create!(content: 'prefix')
|
383
385
|
|
384
386
|
results = ModelWithPgSearch.search_content("pre")
|
385
387
|
expect(results).to eq([included])
|
@@ -387,17 +389,17 @@ describe "an Active Record model which includes PgSearch" do
|
|
387
389
|
end
|
388
390
|
|
389
391
|
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!(:
|
392
|
+
included = ModelWithPgSearch.create!(content: "jumped")
|
393
|
+
excluded = [ModelWithPgSearch.create!(content: "jump"),
|
394
|
+
ModelWithPgSearch.create!(content: "jumping")]
|
393
395
|
|
394
396
|
results = ModelWithPgSearch.search_content("jumped")
|
395
397
|
expect(results).to eq([included])
|
396
398
|
end
|
397
399
|
|
398
400
|
it "returns rows that match sorted by rank" do
|
399
|
-
loser = ModelWithPgSearch.create!(:
|
400
|
-
winner = ModelWithPgSearch.create!(:
|
401
|
+
loser = ModelWithPgSearch.create!(content: 'foo')
|
402
|
+
winner = ModelWithPgSearch.create!(content: 'foo foo')
|
401
403
|
|
402
404
|
results = ModelWithPgSearch.search_content("foo").with_pg_search_rank
|
403
405
|
expect(results[0].pg_search_rank).to be > results[1].pg_search_rank
|
@@ -407,8 +409,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
407
409
|
it 'allows pg_search_rank along with a join' do
|
408
410
|
parent_1 = ParentModel.create!(id: 98)
|
409
411
|
parent_2 = ParentModel.create!(id: 99)
|
410
|
-
loser = ModelWithPgSearch.create!(:
|
411
|
-
winner = ModelWithPgSearch.create!(:
|
412
|
+
loser = ModelWithPgSearch.create!(content: 'foo', parent_model: parent_2)
|
413
|
+
winner = ModelWithPgSearch.create!(content: 'foo foo', parent_model: parent_1)
|
412
414
|
|
413
415
|
results = ModelWithPgSearch.joins(:parent_model).merge(ParentModel.active).search_content("foo").with_pg_search_rank
|
414
416
|
expect(results.map(&:id)).to eq [winner.id, loser.id]
|
@@ -417,8 +419,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
417
419
|
end
|
418
420
|
|
419
421
|
it "returns results that match sorted by primary key for records that rank the same" do
|
420
|
-
sorted_results = [ModelWithPgSearch.create!(:
|
421
|
-
ModelWithPgSearch.create!(:
|
422
|
+
sorted_results = [ModelWithPgSearch.create!(content: 'foo'),
|
423
|
+
ModelWithPgSearch.create!(content: 'foo')].sort_by(&:id)
|
422
424
|
|
423
425
|
results = ModelWithPgSearch.search_content("foo")
|
424
426
|
expect(results).to eq(sorted_results)
|
@@ -426,13 +428,13 @@ describe "an Active Record model which includes PgSearch" do
|
|
426
428
|
|
427
429
|
it "returns results that match a query with multiple space-separated search terms" do
|
428
430
|
included = [
|
429
|
-
ModelWithPgSearch.create!(:
|
430
|
-
ModelWithPgSearch.create!(:
|
431
|
-
ModelWithPgSearch.create!(:
|
431
|
+
ModelWithPgSearch.create!(content: 'foo bar'),
|
432
|
+
ModelWithPgSearch.create!(content: 'bar foo'),
|
433
|
+
ModelWithPgSearch.create!(content: 'bar foo baz')
|
432
434
|
]
|
433
435
|
excluded = [
|
434
|
-
ModelWithPgSearch.create!(:
|
435
|
-
ModelWithPgSearch.create!(:
|
436
|
+
ModelWithPgSearch.create!(content: 'foo'),
|
437
|
+
ModelWithPgSearch.create!(content: 'foo baz')
|
436
438
|
]
|
437
439
|
|
438
440
|
results = ModelWithPgSearch.search_content('foo bar')
|
@@ -441,15 +443,15 @@ describe "an Active Record model which includes PgSearch" do
|
|
441
443
|
end
|
442
444
|
|
443
445
|
it "returns rows that match a query with characters that are invalid in a tsquery expression" do
|
444
|
-
included = ModelWithPgSearch.create!(:
|
446
|
+
included = ModelWithPgSearch.create!(content: "(:Foo.) Bar?, \\")
|
445
447
|
|
446
448
|
results = ModelWithPgSearch.search_content("foo :bar .,?() \\")
|
447
449
|
expect(results).to eq([included])
|
448
450
|
end
|
449
451
|
|
450
452
|
it "accepts non-string queries and calls #to_s on them" do
|
451
|
-
foo = ModelWithPgSearch.create!(:
|
452
|
-
not_a_string = double(:
|
453
|
+
foo = ModelWithPgSearch.create!(content: "foo")
|
454
|
+
not_a_string = double(to_s: "foo")
|
453
455
|
expect(ModelWithPgSearch.search_content(not_a_string)).to eq([foo])
|
454
456
|
end
|
455
457
|
|
@@ -465,7 +467,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
465
467
|
# WARNING: searching timestamps is not something PostgreSQL
|
466
468
|
# full-text search is good at. Use at your own risk.
|
467
469
|
pg_search_scope :search_timestamps,
|
468
|
-
|
470
|
+
against: %i[created_at updated_at]
|
469
471
|
end
|
470
472
|
end
|
471
473
|
|
@@ -481,17 +483,17 @@ describe "an Active Record model which includes PgSearch" do
|
|
481
483
|
|
482
484
|
context "against multiple columns" do
|
483
485
|
before do
|
484
|
-
ModelWithPgSearch.pg_search_scope :search_title_and_content, :
|
486
|
+
ModelWithPgSearch.pg_search_scope :search_title_and_content, against: %i[title content]
|
485
487
|
end
|
486
488
|
|
487
489
|
it "returns rows whose columns contain all of the terms in the query across columns" do
|
488
490
|
included = [
|
489
|
-
ModelWithPgSearch.create!(:
|
490
|
-
ModelWithPgSearch.create!(:
|
491
|
+
ModelWithPgSearch.create!(title: 'foo', content: 'bar'),
|
492
|
+
ModelWithPgSearch.create!(title: 'bar', content: 'foo')
|
491
493
|
]
|
492
494
|
excluded = [
|
493
|
-
ModelWithPgSearch.create!(:
|
494
|
-
ModelWithPgSearch.create!(:
|
495
|
+
ModelWithPgSearch.create!(title: 'foo', content: 'foo'),
|
496
|
+
ModelWithPgSearch.create!(title: 'bar', content: 'bar')
|
495
497
|
]
|
496
498
|
|
497
499
|
results = ModelWithPgSearch.search_title_and_content('foo bar')
|
@@ -503,8 +505,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
503
505
|
end
|
504
506
|
|
505
507
|
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!(:
|
508
|
+
in_title = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
|
509
|
+
in_content = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
|
508
510
|
|
509
511
|
results = ModelWithPgSearch.search_title_and_content('foo')
|
510
512
|
expect(results).to match_array([in_title, in_content])
|
@@ -512,7 +514,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
512
514
|
|
513
515
|
# Searching with a NULL column will prevent any matches unless we coalesce it.
|
514
516
|
it "returns rows where at one column contains all of the terms in the query and another is NULL" do
|
515
|
-
included = ModelWithPgSearch.create!(:
|
517
|
+
included = ModelWithPgSearch.create!(title: 'foo', content: nil)
|
516
518
|
results = ModelWithPgSearch.search_title_and_content('foo')
|
517
519
|
expect(results).to eq([included])
|
518
520
|
end
|
@@ -520,31 +522,31 @@ describe "an Active Record model which includes PgSearch" do
|
|
520
522
|
|
521
523
|
context "using trigram" do
|
522
524
|
before do
|
523
|
-
ModelWithPgSearch.pg_search_scope :with_trigrams, :
|
525
|
+
ModelWithPgSearch.pg_search_scope :with_trigrams, against: %i[title content], using: :trigram
|
524
526
|
end
|
525
527
|
|
526
528
|
it "returns rows where one searchable column and the query share enough trigrams" do
|
527
|
-
included = ModelWithPgSearch.create!(:
|
529
|
+
included = ModelWithPgSearch.create!(title: 'abcdefghijkl', content: nil)
|
528
530
|
results = ModelWithPgSearch.with_trigrams('cdefhijkl')
|
529
531
|
expect(results).to eq([included])
|
530
532
|
end
|
531
533
|
|
532
534
|
it "returns rows where multiple searchable columns and the query share enough trigrams" do
|
533
|
-
included = ModelWithPgSearch.create!(:
|
535
|
+
included = ModelWithPgSearch.create!(title: 'abcdef', content: 'ghijkl')
|
534
536
|
results = ModelWithPgSearch.with_trigrams('cdefhijkl')
|
535
537
|
expect(results).to eq([included])
|
536
538
|
end
|
537
539
|
|
538
540
|
context "when a threshold is specified" do
|
539
541
|
before do
|
540
|
-
ModelWithPgSearch.pg_search_scope :with_strict_trigrams, :
|
541
|
-
ModelWithPgSearch.pg_search_scope :with_permissive_trigrams, :
|
542
|
+
ModelWithPgSearch.pg_search_scope :with_strict_trigrams, against: %i[title content], using: { trigram: { threshold: 0.5 } }
|
543
|
+
ModelWithPgSearch.pg_search_scope :with_permissive_trigrams, against: %i[title content], using: { trigram: { threshold: 0.1 } }
|
542
544
|
end
|
543
545
|
|
544
546
|
it "uses the threshold in the trigram expression" do
|
545
|
-
low_similarity = ModelWithPgSearch.create!(:
|
546
|
-
medium_similarity = ModelWithPgSearch.create!(:
|
547
|
-
high_similarity = ModelWithPgSearch.create!(:
|
547
|
+
low_similarity = ModelWithPgSearch.create!(title: "a")
|
548
|
+
medium_similarity = ModelWithPgSearch.create!(title: "abc")
|
549
|
+
high_similarity = ModelWithPgSearch.create!(title: "abcdefghijkl")
|
548
550
|
|
549
551
|
results = ModelWithPgSearch.with_strict_trigrams("abcdefg")
|
550
552
|
expect(results).to include(high_similarity)
|
@@ -563,16 +565,16 @@ describe "an Active Record model which includes PgSearch" do
|
|
563
565
|
context "using tsearch" do
|
564
566
|
before do
|
565
567
|
ModelWithPgSearch.pg_search_scope :search_title_with_prefixes,
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
568
|
+
against: :title,
|
569
|
+
using: {
|
570
|
+
tsearch: { prefix: true }
|
571
|
+
}
|
570
572
|
end
|
571
573
|
|
572
|
-
context "with :
|
574
|
+
context "with prefix: true" do
|
573
575
|
it "returns rows that match the query and that are prefixed by the query" do
|
574
|
-
included = ModelWithPgSearch.create!(:
|
575
|
-
excluded = ModelWithPgSearch.create!(:
|
576
|
+
included = ModelWithPgSearch.create!(title: 'prefix')
|
577
|
+
excluded = ModelWithPgSearch.create!(title: 'postfix')
|
576
578
|
|
577
579
|
results = ModelWithPgSearch.search_title_with_prefixes("pre")
|
578
580
|
expect(results).to eq([included])
|
@@ -580,8 +582,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
580
582
|
end
|
581
583
|
|
582
584
|
it "returns rows that match the query when the query has a hyphen" do
|
583
|
-
included = ModelWithPgSearch.create!(:
|
584
|
-
excluded = ModelWithPgSearch.create!(:
|
585
|
+
included = ModelWithPgSearch.create!(title: 'foo-bar')
|
586
|
+
excluded = ModelWithPgSearch.create!(title: 'foo bar')
|
585
587
|
|
586
588
|
results = ModelWithPgSearch.search_title_with_prefixes("foo-bar")
|
587
589
|
expect(results).to include(included)
|
@@ -592,16 +594,16 @@ describe "an Active Record model which includes PgSearch" do
|
|
592
594
|
context "with the english dictionary" do
|
593
595
|
before do
|
594
596
|
ModelWithPgSearch.pg_search_scope :search_content_with_english,
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
597
|
+
against: :content,
|
598
|
+
using: {
|
599
|
+
tsearch: { dictionary: :english }
|
600
|
+
}
|
599
601
|
end
|
600
602
|
|
601
603
|
it "returns rows that match the query when stemmed by the english dictionary" do
|
602
|
-
included = [ModelWithPgSearch.create!(:
|
603
|
-
ModelWithPgSearch.create!(:
|
604
|
-
ModelWithPgSearch.create!(:
|
604
|
+
included = [ModelWithPgSearch.create!(content: "jump"),
|
605
|
+
ModelWithPgSearch.create!(content: "jumped"),
|
606
|
+
ModelWithPgSearch.create!(content: "jumping")]
|
605
607
|
|
606
608
|
results = ModelWithPgSearch.search_content_with_english("jump")
|
607
609
|
expect(results).to match_array(included)
|
@@ -611,14 +613,14 @@ describe "an Active Record model which includes PgSearch" do
|
|
611
613
|
describe "highlighting" do
|
612
614
|
before do
|
613
615
|
["Strip Down", "Down", "Down and Out", "Won't Let You Down"].each do |name|
|
614
|
-
ModelWithPgSearch.create! :
|
616
|
+
ModelWithPgSearch.create! content: name
|
615
617
|
end
|
616
618
|
end
|
617
619
|
|
618
620
|
context "with highlight turned on" do
|
619
621
|
before do
|
620
622
|
ModelWithPgSearch.pg_search_scope :search_content,
|
621
|
-
|
623
|
+
against: :content
|
622
624
|
end
|
623
625
|
|
624
626
|
it "adds a #pg_search_highlight method to each returned model record" do
|
@@ -636,22 +638,22 @@ describe "an Active Record model which includes PgSearch" do
|
|
636
638
|
|
637
639
|
context "with custom highlighting options" do
|
638
640
|
before do
|
639
|
-
ModelWithPgSearch.create! :
|
641
|
+
ModelWithPgSearch.create! content: "#{'text ' * 2}Let #{'text ' * 2}Let #{'text ' * 2}"
|
640
642
|
|
641
643
|
ModelWithPgSearch.pg_search_scope :search_content,
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
644
|
+
against: :content,
|
645
|
+
using: {
|
646
|
+
tsearch: {
|
647
|
+
highlight: {
|
648
|
+
StartSel: '<mark class="highlight">',
|
649
|
+
StopSel: '</mark>',
|
650
|
+
FragmentDelimiter: '<delim class="my_delim">',
|
651
|
+
MaxFragments: 2,
|
652
|
+
MaxWords: 2,
|
653
|
+
MinWords: 1
|
654
|
+
}
|
655
|
+
}
|
656
|
+
}
|
655
657
|
end
|
656
658
|
|
657
659
|
it "applies the options to the excerpts" do
|
@@ -665,12 +667,12 @@ describe "an Active Record model which includes PgSearch" do
|
|
665
667
|
describe "ranking" do
|
666
668
|
before do
|
667
669
|
["Strip Down", "Down", "Down and Out", "Won't Let You Down"].each do |name|
|
668
|
-
ModelWithPgSearch.create! :
|
670
|
+
ModelWithPgSearch.create! content: name
|
669
671
|
end
|
670
672
|
end
|
671
673
|
|
672
674
|
it "adds a #pg_search_rank method to each returned model record" do
|
673
|
-
ModelWithPgSearch.pg_search_scope :search_content, :
|
675
|
+
ModelWithPgSearch.pg_search_scope :search_content, against: :content
|
674
676
|
|
675
677
|
result = ModelWithPgSearch.search_content("Strip Down").with_pg_search_rank.first
|
676
678
|
|
@@ -680,10 +682,10 @@ describe "an Active Record model which includes PgSearch" do
|
|
680
682
|
context "with a normalization specified" do
|
681
683
|
before do
|
682
684
|
ModelWithPgSearch.pg_search_scope :search_content_with_normalization,
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
685
|
+
against: :content,
|
686
|
+
using: {
|
687
|
+
tsearch: { normalization: 2 }
|
688
|
+
}
|
687
689
|
end
|
688
690
|
|
689
691
|
it "ranks the results for documents with less text higher" do
|
@@ -697,8 +699,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
697
699
|
context "with no normalization" do
|
698
700
|
before do
|
699
701
|
ModelWithPgSearch.pg_search_scope :search_content_without_normalization,
|
700
|
-
|
701
|
-
|
702
|
+
against: :content,
|
703
|
+
using: :tsearch
|
702
704
|
end
|
703
705
|
|
704
706
|
it "ranks the results equally" do
|
@@ -713,12 +715,12 @@ describe "an Active Record model which includes PgSearch" do
|
|
713
715
|
context "against columns ranked with arrays" do
|
714
716
|
before do
|
715
717
|
ModelWithPgSearch.pg_search_scope :search_weighted_by_array_of_arrays,
|
716
|
-
|
718
|
+
against: [[:content, 'B'], [:title, 'A']]
|
717
719
|
end
|
718
720
|
|
719
721
|
it "returns results sorted by weighted rank" do
|
720
|
-
loser = ModelWithPgSearch.create!(:
|
721
|
-
winner = ModelWithPgSearch.create!(:
|
722
|
+
loser = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
|
723
|
+
winner = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
|
722
724
|
|
723
725
|
results = ModelWithPgSearch.search_weighted_by_array_of_arrays('foo').with_pg_search_rank
|
724
726
|
expect(results[0].pg_search_rank).to be > results[1].pg_search_rank
|
@@ -729,12 +731,12 @@ describe "an Active Record model which includes PgSearch" do
|
|
729
731
|
context "against columns ranked with a hash" do
|
730
732
|
before do
|
731
733
|
ModelWithPgSearch.pg_search_scope :search_weighted_by_hash,
|
732
|
-
|
734
|
+
against: { content: 'B', title: 'A' }
|
733
735
|
end
|
734
736
|
|
735
737
|
it "returns results sorted by weighted rank" do
|
736
|
-
loser = ModelWithPgSearch.create!(:
|
737
|
-
winner = ModelWithPgSearch.create!(:
|
738
|
+
loser = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
|
739
|
+
winner = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
|
738
740
|
|
739
741
|
results = ModelWithPgSearch.search_weighted_by_hash('foo').with_pg_search_rank
|
740
742
|
expect(results[0].pg_search_rank).to be > results[1].pg_search_rank
|
@@ -745,12 +747,12 @@ describe "an Active Record model which includes PgSearch" do
|
|
745
747
|
context "against columns of which only some are ranked" do
|
746
748
|
before do
|
747
749
|
ModelWithPgSearch.pg_search_scope :search_weighted,
|
748
|
-
|
750
|
+
against: [:content, [:title, 'A']]
|
749
751
|
end
|
750
752
|
|
751
753
|
it "returns results sorted by weighted rank using an implied low rank for unranked columns" do
|
752
|
-
loser = ModelWithPgSearch.create!(:
|
753
|
-
winner = ModelWithPgSearch.create!(:
|
754
|
+
loser = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
|
755
|
+
winner = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
|
754
756
|
|
755
757
|
results = ModelWithPgSearch.search_weighted('foo').with_pg_search_rank
|
756
758
|
expect(results[0].pg_search_rank).to be > results[1].pg_search_rank
|
@@ -761,17 +763,17 @@ describe "an Active Record model which includes PgSearch" do
|
|
761
763
|
context "searching any_word option" do
|
762
764
|
before do
|
763
765
|
ModelWithPgSearch.pg_search_scope :search_title_with_any_word,
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
766
|
+
against: :title,
|
767
|
+
using: {
|
768
|
+
tsearch: { any_word: true }
|
769
|
+
}
|
768
770
|
|
769
771
|
ModelWithPgSearch.pg_search_scope :search_title_with_all_words,
|
770
|
-
|
772
|
+
against: :title
|
771
773
|
end
|
772
774
|
|
773
775
|
it "returns all results containing any word in their title" do
|
774
|
-
numbers = %w[one two three four].map { |number| ModelWithPgSearch.create!(:
|
776
|
+
numbers = %w[one two three four].map { |number| ModelWithPgSearch.create!(title: number) }
|
775
777
|
|
776
778
|
results = ModelWithPgSearch.search_title_with_any_word("one two three four")
|
777
779
|
|
@@ -786,21 +788,21 @@ describe "an Active Record model which includes PgSearch" do
|
|
786
788
|
context "with :negation" do
|
787
789
|
before do
|
788
790
|
ModelWithPgSearch.pg_search_scope :search_with_negation,
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
791
|
+
against: :title,
|
792
|
+
using: {
|
793
|
+
tsearch: { negation: true }
|
794
|
+
}
|
793
795
|
end
|
794
796
|
|
795
797
|
it "doesn't return results that contain terms prepended with '!'" do
|
796
798
|
included = [
|
797
|
-
ModelWithPgSearch.create!(:
|
798
|
-
ModelWithPgSearch.create!(:
|
799
|
+
ModelWithPgSearch.create!(title: "one fish"),
|
800
|
+
ModelWithPgSearch.create!(title: "two fish")
|
799
801
|
]
|
800
802
|
|
801
803
|
excluded = [
|
802
|
-
ModelWithPgSearch.create!(:
|
803
|
-
ModelWithPgSearch.create!(:
|
804
|
+
ModelWithPgSearch.create!(title: "red fish"),
|
805
|
+
ModelWithPgSearch.create!(title: "blue fish")
|
804
806
|
]
|
805
807
|
|
806
808
|
results = ModelWithPgSearch.search_with_negation("fish !red !blue")
|
@@ -813,19 +815,19 @@ describe "an Active Record model which includes PgSearch" do
|
|
813
815
|
context "without :negation" do
|
814
816
|
before do
|
815
817
|
ModelWithPgSearch.pg_search_scope :search_without_negation,
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
818
|
+
against: :title,
|
819
|
+
using: {
|
820
|
+
tsearch: {}
|
821
|
+
}
|
820
822
|
end
|
821
823
|
|
822
824
|
it "return results that contain terms prepended with '!'" do
|
823
825
|
included = [
|
824
|
-
ModelWithPgSearch.create!(:
|
826
|
+
ModelWithPgSearch.create!(title: "!bang")
|
825
827
|
]
|
826
828
|
|
827
829
|
excluded = [
|
828
|
-
ModelWithPgSearch.create!(:
|
830
|
+
ModelWithPgSearch.create!(title: "?question")
|
829
831
|
]
|
830
832
|
|
831
833
|
results = ModelWithPgSearch.search_without_negation("!bang")
|
@@ -839,27 +841,27 @@ describe "an Active Record model which includes PgSearch" do
|
|
839
841
|
context "using dmetaphone" do
|
840
842
|
before do
|
841
843
|
ModelWithPgSearch.pg_search_scope :with_dmetaphones,
|
842
|
-
|
843
|
-
|
844
|
+
against: %i[title content],
|
845
|
+
using: :dmetaphone
|
844
846
|
end
|
845
847
|
|
846
848
|
it "returns rows where one searchable column and the query share enough dmetaphones" do
|
847
|
-
included = ModelWithPgSearch.create!(:
|
848
|
-
excluded = ModelWithPgSearch.create!(:
|
849
|
+
included = ModelWithPgSearch.create!(title: 'Geoff', content: nil)
|
850
|
+
excluded = ModelWithPgSearch.create!(title: 'Bob', content: nil)
|
849
851
|
results = ModelWithPgSearch.with_dmetaphones('Jeff')
|
850
852
|
expect(results).to eq([included])
|
851
853
|
end
|
852
854
|
|
853
855
|
it "returns rows where multiple searchable columns and the query share enough dmetaphones" do
|
854
|
-
included = ModelWithPgSearch.create!(:
|
855
|
-
excluded = ModelWithPgSearch.create!(:
|
856
|
+
included = ModelWithPgSearch.create!(title: 'Geoff', content: 'George')
|
857
|
+
excluded = ModelWithPgSearch.create!(title: 'Bob', content: 'Jones')
|
856
858
|
results = ModelWithPgSearch.with_dmetaphones('Jeff Jorge')
|
857
859
|
expect(results).to eq([included])
|
858
860
|
end
|
859
861
|
|
860
862
|
it "returns rows that match dmetaphones that are English stopwords" do
|
861
|
-
included = ModelWithPgSearch.create!(:
|
862
|
-
excluded = ModelWithPgSearch.create!(:
|
863
|
+
included = ModelWithPgSearch.create!(title: 'White', content: nil)
|
864
|
+
excluded = ModelWithPgSearch.create!(title: 'Black', content: nil)
|
863
865
|
results = ModelWithPgSearch.with_dmetaphones('Wight')
|
864
866
|
expect(results).to eq([included])
|
865
867
|
end
|
@@ -867,8 +869,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
867
869
|
it "can handle terms that do not have a dmetaphone equivalent" do
|
868
870
|
term_with_blank_metaphone = "w"
|
869
871
|
|
870
|
-
included = ModelWithPgSearch.create!(:
|
871
|
-
excluded = ModelWithPgSearch.create!(:
|
872
|
+
included = ModelWithPgSearch.create!(title: 'White', content: nil)
|
873
|
+
excluded = ModelWithPgSearch.create!(title: 'Black', content: nil)
|
872
874
|
|
873
875
|
results = ModelWithPgSearch.with_dmetaphones('Wight W')
|
874
876
|
expect(results).to eq([included])
|
@@ -878,39 +880,39 @@ describe "an Active Record model which includes PgSearch" do
|
|
878
880
|
context "using multiple features" do
|
879
881
|
before do
|
880
882
|
ModelWithPgSearch.pg_search_scope :with_tsearch,
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
883
|
+
against: :title,
|
884
|
+
using: [
|
885
|
+
[:tsearch, { dictionary: 'english' }]
|
886
|
+
]
|
885
887
|
|
886
888
|
ModelWithPgSearch.pg_search_scope :with_trigram,
|
887
|
-
|
888
|
-
|
889
|
+
against: :title,
|
890
|
+
using: :trigram
|
889
891
|
|
890
892
|
ModelWithPgSearch.pg_search_scope :with_trigram_and_ignoring_accents,
|
891
|
-
|
892
|
-
|
893
|
-
|
893
|
+
against: :title,
|
894
|
+
ignoring: :accents,
|
895
|
+
using: :trigram
|
894
896
|
|
895
897
|
ModelWithPgSearch.pg_search_scope :with_tsearch_and_trigram,
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
898
|
+
against: :title,
|
899
|
+
using: [
|
900
|
+
[:tsearch, { dictionary: 'english' }],
|
901
|
+
:trigram
|
902
|
+
]
|
901
903
|
|
902
904
|
ModelWithPgSearch.pg_search_scope :complex_search,
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
905
|
+
against: %i[content title],
|
906
|
+
ignoring: :accents,
|
907
|
+
using: {
|
908
|
+
tsearch: { dictionary: 'english' },
|
909
|
+
dmetaphone: {},
|
910
|
+
trigram: {}
|
911
|
+
}
|
910
912
|
end
|
911
913
|
|
912
914
|
it "returns rows that match using any of the features" do
|
913
|
-
record = ModelWithPgSearch.create!(:
|
915
|
+
record = ModelWithPgSearch.create!(title: "tiling is grouty")
|
914
916
|
|
915
917
|
# matches trigram only
|
916
918
|
trigram_query = "ling is grouty"
|
@@ -949,21 +951,21 @@ describe "an Active Record model which includes PgSearch" do
|
|
949
951
|
|
950
952
|
context "with feature-specific configuration" do
|
951
953
|
before do
|
952
|
-
@tsearch_config = tsearch_config = {:
|
953
|
-
@trigram_config = trigram_config = {:
|
954
|
+
@tsearch_config = tsearch_config = { dictionary: 'english' }
|
955
|
+
@trigram_config = trigram_config = { foo: 'bar' }
|
954
956
|
|
955
957
|
ModelWithPgSearch.pg_search_scope :with_tsearch_and_trigram_using_hash,
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
958
|
+
against: :title,
|
959
|
+
using: {
|
960
|
+
tsearch: tsearch_config,
|
961
|
+
trigram: trigram_config
|
962
|
+
}
|
961
963
|
end
|
962
964
|
|
963
965
|
it "should pass the custom configuration down to the specified feature" do
|
964
966
|
stub_feature = double(
|
965
|
-
:
|
966
|
-
:
|
967
|
+
conditions: Arel::Nodes::Grouping.new(Arel.sql("1 = 1")),
|
968
|
+
rank: Arel::Nodes::Grouping.new(Arel.sql("1.0"))
|
967
969
|
)
|
968
970
|
|
969
971
|
expect(PgSearch::Features::TSearch).to receive(:new).with(anything, @tsearch_config, anything, anything, anything).at_least(:once).and_return(stub_feature)
|
@@ -1011,13 +1013,13 @@ describe "an Active Record model which includes PgSearch" do
|
|
1011
1013
|
unexpected.comments.create(body: 'commentwo')
|
1012
1014
|
|
1013
1015
|
Post.pg_search_scope :search_by_content_with_tsvector,
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1016
|
+
associated_against: { comments: [:body] },
|
1017
|
+
using: {
|
1018
|
+
tsearch: {
|
1019
|
+
tsvector_column: 'content_tsvector',
|
1020
|
+
dictionary: 'english'
|
1021
|
+
}
|
1022
|
+
}
|
1021
1023
|
end
|
1022
1024
|
|
1023
1025
|
it "should find by the tsvector column" do
|
@@ -1039,13 +1041,13 @@ describe "an Active Record model which includes PgSearch" do
|
|
1039
1041
|
include PgSearch
|
1040
1042
|
|
1041
1043
|
pg_search_scope :search_by_multiple_tsvector_columns,
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1044
|
+
against: ['content', 'message'],
|
1045
|
+
using: {
|
1046
|
+
tsearch: {
|
1047
|
+
tsvector_column: ['content_tsvector', 'message_tsvector'],
|
1048
|
+
dictionary: 'english'
|
1049
|
+
}
|
1050
|
+
}
|
1049
1051
|
end
|
1050
1052
|
end
|
1051
1053
|
|
@@ -1067,8 +1069,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
1067
1069
|
model { include PgSearch }
|
1068
1070
|
end
|
1069
1071
|
|
1070
|
-
let!(:expected) { ModelWithTsvector.create!(:
|
1071
|
-
let!(:unexpected) { ModelWithTsvector.create!(:
|
1072
|
+
let!(:expected) { ModelWithTsvector.create!(content: 'tiling is grouty') }
|
1073
|
+
let!(:unexpected) { ModelWithTsvector.create!(content: 'longcat is looooooooong') }
|
1072
1074
|
|
1073
1075
|
before do
|
1074
1076
|
ActiveRecord::Base.connection.execute <<-SQL.strip_heredoc
|
@@ -1077,13 +1079,13 @@ describe "an Active Record model which includes PgSearch" do
|
|
1077
1079
|
SQL
|
1078
1080
|
|
1079
1081
|
ModelWithTsvector.pg_search_scope :search_by_content_with_tsvector,
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1082
|
+
against: :content,
|
1083
|
+
using: {
|
1084
|
+
tsearch: {
|
1085
|
+
tsvector_column: 'content_tsvector',
|
1086
|
+
dictionary: 'english'
|
1087
|
+
}
|
1088
|
+
}
|
1087
1089
|
end
|
1088
1090
|
|
1089
1091
|
it "should not use to_tsvector in the query" do
|
@@ -1117,43 +1119,52 @@ describe "an Active Record model which includes PgSearch" do
|
|
1117
1119
|
context "ignoring accents" do
|
1118
1120
|
before do
|
1119
1121
|
ModelWithPgSearch.pg_search_scope :search_title_without_accents,
|
1120
|
-
|
1121
|
-
|
1122
|
+
against: :title,
|
1123
|
+
ignoring: :accents
|
1122
1124
|
end
|
1123
1125
|
|
1124
1126
|
it "returns rows that match the query but not its accents" do
|
1125
1127
|
# \303\241 is a with acute accent
|
1126
1128
|
# \303\251 is e with acute accent
|
1127
1129
|
|
1128
|
-
included = ModelWithPgSearch.create!(:
|
1130
|
+
included = ModelWithPgSearch.create!(title: "\303\241bcdef")
|
1129
1131
|
|
1130
1132
|
results = ModelWithPgSearch.search_title_without_accents("abcd\303\251f")
|
1131
1133
|
expect(results).to eq([included])
|
1132
1134
|
end
|
1135
|
+
|
1136
|
+
context "when the query includes accents" do
|
1137
|
+
it "does not create an erroneous tsquery expression" do
|
1138
|
+
included = ModelWithPgSearch.create!(title: "Weird L‘Content")
|
1139
|
+
|
1140
|
+
results = ModelWithPgSearch.search_title_without_accents("L‘Content")
|
1141
|
+
expect(results).to eq([included])
|
1142
|
+
end
|
1143
|
+
end
|
1133
1144
|
end
|
1134
1145
|
|
1135
1146
|
context "when passed a :ranked_by expression" do
|
1136
1147
|
before do
|
1137
1148
|
ModelWithPgSearch.pg_search_scope :search_content_with_default_rank,
|
1138
|
-
|
1149
|
+
against: :content
|
1139
1150
|
|
1140
1151
|
ModelWithPgSearch.pg_search_scope :search_content_with_importance_as_rank,
|
1141
|
-
|
1142
|
-
|
1152
|
+
against: :content,
|
1153
|
+
ranked_by: "importance"
|
1143
1154
|
|
1144
1155
|
ModelWithPgSearch.pg_search_scope :search_content_with_importance_as_rank_multiplier,
|
1145
|
-
|
1146
|
-
|
1156
|
+
against: :content,
|
1157
|
+
ranked_by: ":tsearch * importance"
|
1147
1158
|
end
|
1148
1159
|
|
1149
1160
|
it "should return records with a rank attribute equal to the :ranked_by expression" do
|
1150
|
-
ModelWithPgSearch.create!(:
|
1161
|
+
ModelWithPgSearch.create!(content: 'foo', importance: 10)
|
1151
1162
|
results = ModelWithPgSearch.search_content_with_importance_as_rank("foo").with_pg_search_rank
|
1152
1163
|
expect(results.first.pg_search_rank).to eq(10)
|
1153
1164
|
end
|
1154
1165
|
|
1155
1166
|
it "should substitute :tsearch with the tsearch rank expression in the :ranked_by expression" do
|
1156
|
-
ModelWithPgSearch.create!(:
|
1167
|
+
ModelWithPgSearch.create!(content: 'foo', importance: 10)
|
1157
1168
|
|
1158
1169
|
tsearch_result =
|
1159
1170
|
ModelWithPgSearch.search_content_with_default_rank("foo").with_pg_search_rank.first
|
@@ -1162,8 +1173,8 @@ describe "an Active Record model which includes PgSearch" do
|
|
1162
1173
|
|
1163
1174
|
multiplied_result =
|
1164
1175
|
ModelWithPgSearch.search_content_with_importance_as_rank_multiplier("foo")
|
1165
|
-
|
1166
|
-
|
1176
|
+
.with_pg_search_rank
|
1177
|
+
.first
|
1167
1178
|
|
1168
1179
|
multiplied_rank = multiplied_result.pg_search_rank
|
1169
1180
|
|
@@ -1172,9 +1183,9 @@ describe "an Active Record model which includes PgSearch" do
|
|
1172
1183
|
|
1173
1184
|
it "should return results in descending order of the value of the rank expression" do
|
1174
1185
|
records = [
|
1175
|
-
ModelWithPgSearch.create!(:
|
1176
|
-
ModelWithPgSearch.create!(:
|
1177
|
-
ModelWithPgSearch.create!(:
|
1186
|
+
ModelWithPgSearch.create!(content: 'foo', importance: 1),
|
1187
|
+
ModelWithPgSearch.create!(content: 'foo', importance: 3),
|
1188
|
+
ModelWithPgSearch.create!(content: 'foo', importance: 2)
|
1178
1189
|
]
|
1179
1190
|
|
1180
1191
|
results = ModelWithPgSearch.search_content_with_importance_as_rank("foo")
|
@@ -1186,10 +1197,10 @@ describe "an Active Record model which includes PgSearch" do
|
|
1186
1197
|
let(:scope_name) { :"search_content_ranked_by_#{feature}" }
|
1187
1198
|
before do
|
1188
1199
|
ModelWithPgSearch.pg_search_scope scope_name,
|
1189
|
-
|
1190
|
-
|
1200
|
+
against: :content,
|
1201
|
+
ranked_by: ":#{feature}"
|
1191
1202
|
|
1192
|
-
ModelWithPgSearch.create!(:
|
1203
|
+
ModelWithPgSearch.create!(content: 'foo')
|
1193
1204
|
end
|
1194
1205
|
|
1195
1206
|
context "when .with_pg_search_rank is chained after" do
|
@@ -1223,12 +1234,12 @@ describe "an Active Record model which includes PgSearch" do
|
|
1223
1234
|
context "using the tsearch ranking algorithm" do
|
1224
1235
|
it "sorts results by the tsearch rank" do
|
1225
1236
|
ModelWithPgSearch.pg_search_scope :search_content_ranked_by_tsearch,
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1237
|
+
using: :tsearch,
|
1238
|
+
against: :content,
|
1239
|
+
ranked_by: ":tsearch"
|
1229
1240
|
|
1230
|
-
once = ModelWithPgSearch.create!(:
|
1231
|
-
twice = ModelWithPgSearch.create!(:
|
1241
|
+
once = ModelWithPgSearch.create!(content: 'foo bar')
|
1242
|
+
twice = ModelWithPgSearch.create!(content: 'foo foo')
|
1232
1243
|
|
1233
1244
|
results = ModelWithPgSearch.search_content_ranked_by_tsearch('foo')
|
1234
1245
|
expect(results.find_index(twice)).to be < results.find_index(once)
|
@@ -1238,12 +1249,12 @@ describe "an Active Record model which includes PgSearch" do
|
|
1238
1249
|
context "using the trigram ranking algorithm" do
|
1239
1250
|
it "sorts results by the trigram rank" do
|
1240
1251
|
ModelWithPgSearch.pg_search_scope :search_content_ranked_by_trigram,
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1252
|
+
using: :trigram,
|
1253
|
+
against: :content,
|
1254
|
+
ranked_by: ":trigram"
|
1244
1255
|
|
1245
|
-
close = ModelWithPgSearch.create!(:
|
1246
|
-
exact = ModelWithPgSearch.create!(:
|
1256
|
+
close = ModelWithPgSearch.create!(content: 'abcdef')
|
1257
|
+
exact = ModelWithPgSearch.create!(content: 'abc')
|
1247
1258
|
|
1248
1259
|
results = ModelWithPgSearch.search_content_ranked_by_trigram('abc')
|
1249
1260
|
expect(results.find_index(exact)).to be < results.find_index(close)
|
@@ -1253,12 +1264,12 @@ describe "an Active Record model which includes PgSearch" do
|
|
1253
1264
|
context "using the dmetaphone ranking algorithm" do
|
1254
1265
|
it "sorts results by the dmetaphone rank" do
|
1255
1266
|
ModelWithPgSearch.pg_search_scope :search_content_ranked_by_dmetaphone,
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1267
|
+
using: :dmetaphone,
|
1268
|
+
against: :content,
|
1269
|
+
ranked_by: ":dmetaphone"
|
1259
1270
|
|
1260
|
-
once = ModelWithPgSearch.create!(:
|
1261
|
-
twice = ModelWithPgSearch.create!(:
|
1271
|
+
once = ModelWithPgSearch.create!(content: 'Phoo Bar')
|
1272
|
+
twice = ModelWithPgSearch.create!(content: 'Phoo Fu')
|
1262
1273
|
|
1263
1274
|
results = ModelWithPgSearch.search_content_ranked_by_dmetaphone('foo')
|
1264
1275
|
expect(results.find_index(twice)).to be < results.find_index(once)
|
@@ -1269,17 +1280,17 @@ describe "an Active Record model which includes PgSearch" do
|
|
1269
1280
|
context "when there is a sort only feature" do
|
1270
1281
|
it "excludes that feature from the conditions, but uses it in the sorting" do
|
1271
1282
|
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!(:
|
1283
|
+
against: :content,
|
1284
|
+
using: {
|
1285
|
+
tsearch: { any_word: true, prefix: true },
|
1286
|
+
dmetaphone: { any_word: true, prefix: true, sort_only: true }
|
1287
|
+
},
|
1288
|
+
ranked_by: ":tsearch + (0.5 * :dmetaphone)"
|
1289
|
+
|
1290
|
+
exact = ModelWithPgSearch.create!(content: "ash hines")
|
1291
|
+
one_exact_one_close = ModelWithPgSearch.create!(content: "ash heinz")
|
1292
|
+
one_exact = ModelWithPgSearch.create!(content: "ash smith")
|
1293
|
+
one_close = ModelWithPgSearch.create!(content: "leigh heinz")
|
1283
1294
|
|
1284
1295
|
results = ModelWithPgSearch.search_content_ranked_by_dmetaphone("ash hines")
|
1285
1296
|
expect(results).to eq [exact, one_exact_one_close, one_exact]
|