pg_search 2.3.5 → 2.3.7

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