pg_search 2.1.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +11 -7
  3. data/.travis.yml +33 -42
  4. data/CHANGELOG.md +140 -112
  5. data/CONTRIBUTING.md +5 -3
  6. data/Gemfile +6 -4
  7. data/LICENSE +1 -1
  8. data/README.md +221 -159
  9. data/Rakefile +3 -5
  10. data/lib/pg_search/configuration/association.rb +2 -0
  11. data/lib/pg_search/configuration/column.rb +2 -0
  12. data/lib/pg_search/configuration/foreign_column.rb +2 -0
  13. data/lib/pg_search/configuration.rb +8 -4
  14. data/lib/pg_search/document.rb +5 -3
  15. data/lib/pg_search/features/dmetaphone.rb +3 -1
  16. data/lib/pg_search/features/feature.rb +4 -2
  17. data/lib/pg_search/features/trigram.rb +31 -5
  18. data/lib/pg_search/features/tsearch.rb +4 -1
  19. data/lib/pg_search/features.rb +2 -0
  20. data/lib/pg_search/migration/dmetaphone_generator.rb +3 -1
  21. data/lib/pg_search/migration/generator.rb +4 -2
  22. data/lib/pg_search/migration/multisearch_generator.rb +2 -1
  23. data/lib/pg_search/migration/templates/create_pg_search_documents.rb.erb +1 -1
  24. data/lib/pg_search/multisearch/rebuilder.rb +7 -3
  25. data/lib/pg_search/multisearch.rb +4 -4
  26. data/lib/pg_search/multisearchable.rb +10 -6
  27. data/lib/pg_search/normalizer.rb +2 -0
  28. data/lib/pg_search/railtie.rb +2 -0
  29. data/lib/pg_search/scope_options.rb +21 -41
  30. data/lib/pg_search/tasks.rb +3 -0
  31. data/lib/pg_search/version.rb +3 -1
  32. data/lib/pg_search.rb +10 -5
  33. data/pg_search.gemspec +8 -7
  34. data/spec/integration/associations_spec.rb +103 -101
  35. data/spec/integration/pagination_spec.rb +9 -7
  36. data/spec/integration/pg_search_spec.rb +266 -255
  37. data/spec/integration/single_table_inheritance_spec.rb +16 -15
  38. data/spec/lib/pg_search/configuration/association_spec.rb +7 -5
  39. data/spec/lib/pg_search/configuration/column_spec.rb +2 -0
  40. data/spec/lib/pg_search/configuration/foreign_column_spec.rb +5 -3
  41. data/spec/lib/pg_search/features/dmetaphone_spec.rb +6 -4
  42. data/spec/lib/pg_search/features/trigram_spec.rb +39 -12
  43. data/spec/lib/pg_search/features/tsearch_spec.rb +23 -21
  44. data/spec/lib/pg_search/multisearch/rebuilder_spec.rb +32 -11
  45. data/spec/lib/pg_search/multisearch_spec.rb +9 -7
  46. data/spec/lib/pg_search/multisearchable_spec.rb +68 -27
  47. data/spec/lib/pg_search/normalizer_spec.rb +7 -5
  48. data/spec/lib/pg_search_spec.rb +33 -31
  49. data/spec/spec_helper.rb +3 -1
  50. data/spec/support/database.rb +16 -20
  51. data/spec/support/with_model.rb +2 -0
  52. metadata +13 -30
  53. data/.rubocop_todo.yml +0 -163
  54. data/Guardfile +0 -6
data/README.md CHANGED
@@ -2,12 +2,10 @@
2
2
 
3
3
  [![Gem Version](https://img.shields.io/gem/v/pg_search.svg?style=flat)](https://rubygems.org/gems/pg_search)
4
4
  [![Build Status](https://secure.travis-ci.org/Casecommons/pg_search.svg?branch=master)](https://travis-ci.org/Casecommons/pg_search)
5
- [![Code Climate](https://img.shields.io/codeclimate/github/Casecommons/pg_search.svg?style=flat)](https://codeclimate.com/github/Casecommons/pg_search)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/ae1a7c021e473e9b2486/maintainability)](https://codeclimate.com/github/Casecommons/pg_search/maintainability)
6
6
  [![Test Coverage](https://codeclimate.com/github/Casecommons/pg_search/badges/coverage.svg)](https://codeclimate.com/github/Casecommons/pg_search/coverage)
7
- [![Dependency Status](https://img.shields.io/gemnasium/Casecommons/pg_search.svg?style=flat)](https://gemnasium.com/Casecommons/pg_search)
8
7
  [![Inline docs](http://inch-ci.org/github/Casecommons/pg_search.svg?branch=master&style=flat)](http://inch-ci.org/github/Casecommons/pg_search)
9
8
  [![Join the chat at https://gitter.im/Casecommons/pg_search](https://img.shields.io/badge/gitter-join%20chat-blue.svg)](https://gitter.im/Casecommons/pg_search?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
10
- [![Stories in Ready](https://img.shields.io/waffle/label/Casecommons/pg_search/in%20progress.svg)](https://waffle.io/Casecommons/pg_search)
11
9
 
12
10
  ## DESCRIPTION
13
11
 
@@ -18,7 +16,7 @@ Read the blog post introducing PgSearch at https://content.pivotal.io/blog/pg-se
18
16
 
19
17
  ## REQUIREMENTS
20
18
 
21
- * Ruby 2.1+
19
+ * Ruby 2.4+
22
20
  * ActiveRecord 4.2+
23
21
  * PostgreSQL 9.2+
24
22
  * [PostgreSQL extensions](https://github.com/Casecommons/pg_search/wiki/Installing-PostgreSQL-Extensions) for certain features
@@ -90,12 +88,12 @@ multisearchable in its class definition.
90
88
  ```ruby
91
89
  class EpicPoem < ActiveRecord::Base
92
90
  include PgSearch
93
- multisearchable :against => [:title, :author]
91
+ multisearchable against: [:title, :author]
94
92
  end
95
93
 
96
94
  class Flower < ActiveRecord::Base
97
95
  include PgSearch
98
- multisearchable :against => :color
96
+ multisearchable against: :color
99
97
  end
100
98
  ```
101
99
 
@@ -115,14 +113,14 @@ particular record should be included.
115
113
  ```ruby
116
114
  class Convertible < ActiveRecord::Base
117
115
  include PgSearch
118
- multisearchable :against => [:make, :model],
119
- :if => :available_in_red?
116
+ multisearchable against: [:make, :model],
117
+ if: :available_in_red?
120
118
  end
121
119
 
122
120
  class Jalopy < ActiveRecord::Base
123
121
  include PgSearch
124
- multisearchable :against => [:make, :model],
125
- :if => lambda { |record| record.model_year > 1970 }
122
+ multisearchable against: [:make, :model],
123
+ if: lambda { |record| record.model_year > 1970 }
126
124
  end
127
125
  ```
128
126
 
@@ -135,8 +133,8 @@ timestamp.
135
133
  ```ruby
136
134
  class AntipatternExample
137
135
  include PgSearch
138
- multisearchable :against => [:contents],
139
- :if => :published?
136
+ multisearchable against: [:contents],
137
+ if: :published?
140
138
 
141
139
  def published?
142
140
  published_at < Time.now
@@ -144,8 +142,8 @@ class AntipatternExample
144
142
  end
145
143
 
146
144
  problematic_record = AntipatternExample.create!(
147
- :contents => "Using :if with a timestamp",
148
- :published_at => 10.minutes.from_now
145
+ contents: "Using :if with a timestamp",
146
+ published_at: 10.minutes.from_now
149
147
  )
150
148
 
151
149
  problematic_record.published? # => false
@@ -162,6 +160,46 @@ problematic_record.published? # => true
162
160
  PgSearch.multisearch("timestamp") # => Includes problematic_record
163
161
  ```
164
162
 
163
+ #### More Options
164
+
165
+ **Conditionally update pg_search_documents**
166
+
167
+ You can specify an `:update_if` parameter to conditionally update pg_search documents. For example:
168
+
169
+ ```ruby
170
+ multisearchable(
171
+ against: [:body],
172
+ update_if: :body_changed?
173
+ )
174
+ ```
175
+
176
+ **Specify additional attributes to be saved on the pg_search_documents table**
177
+
178
+ You can specify `:additional_attributes` to be saved within the pg_search_documents table. For example, perhaps you are indexing a book model and an article model and wanted to include the author_id.
179
+
180
+ First, we need to add `author_id` to the migration creating our pg_search_documents table.
181
+
182
+ ```ruby
183
+ create_table :pg_search_documents do |t|
184
+ t.text :content
185
+ t.integer :author_id
186
+ t.belongs_to :searchable, polymorphic: true, index: true
187
+ ```
188
+
189
+ Then, we can send in this additional attribute in a lambda
190
+
191
+ ```ruby
192
+ multisearchable(
193
+ against: [:title, :body],
194
+ additional_attributes: -> (article) { { author_id: article.author_id } }
195
+ )
196
+ ```
197
+
198
+ This allows much faster searches without joins later on by doing something like:
199
+ `PgSearch.multisearch(params['search']).where(author_id: 2)`
200
+
201
+ *NOTE: You must currently manually call `record.update_pg_search_document` for the additional attribute to be included in the pg_search_documents table*
202
+
165
203
  #### Multi-search associations
166
204
 
167
205
  Two associations are built automatically. On the original record, there is a
@@ -170,7 +208,7 @@ record, and on the PgSearch::Document record there is a belongs_to :searchable
170
208
  polymorphic association pointing back to the original record.
171
209
 
172
210
  ```ruby
173
- odyssey = EpicPoem.create!(:title => "Odyssey", :author => "Homer")
211
+ odyssey = EpicPoem.create!(title: "Odyssey", author: "Homer")
174
212
  search_document = odyssey.pg_search_document #=> PgSearch::Document instance
175
213
  search_document.searchable #=> #<EpicPoem id: 1, title: "Odyssey", author: "Homer">
176
214
  ```
@@ -181,8 +219,8 @@ To fetch the PgSearch::Document entries for all of the records that match a
181
219
  given query, use PgSearch.multisearch.
182
220
 
183
221
  ```ruby
184
- odyssey = EpicPoem.create!(:title => "Odyssey", :author => "Homer")
185
- rose = Flower.create!(:color => "Red")
222
+ odyssey = EpicPoem.create!(title: "Odyssey", author: "Homer")
223
+ rose = Flower.create!(color: "Red")
186
224
  PgSearch.multisearch("Homer") #=> [#<PgSearch::Document searchable: odyssey>]
187
225
  PgSearch.multisearch("Red") #=> [#<PgSearch::Document searchable: rose>]
188
226
  ```
@@ -196,7 +234,7 @@ receive SQL requests when necessary.
196
234
 
197
235
  ```ruby
198
236
  PgSearch.multisearch("Bertha").limit(10)
199
- PgSearch.multisearch("Juggler").where(:searchable_type => "Occupation")
237
+ PgSearch.multisearch("Juggler").where(searchable_type: "Occupation")
200
238
  PgSearch.multisearch("Alamo").page(3).per(30)
201
239
  PgSearch.multisearch("Diagonal").find_each do |document|
202
240
  puts document.searchable.updated_at
@@ -213,8 +251,8 @@ PgSearch.multisearch_options in an initializer:
213
251
 
214
252
  ```ruby
215
253
  PgSearch.multisearch_options = {
216
- :using => [:tsearch, :trigram],
217
- :ignoring => :accents
254
+ using: [:tsearch, :trigram],
255
+ ignoring: :accents
218
256
  }
219
257
  ```
220
258
 
@@ -235,7 +273,7 @@ To remove all of the documents for a given class, you can simply delete all of
235
273
  the PgSearch::Document records.
236
274
 
237
275
  ```ruby
238
- PgSearch::Document.delete_all(:searchable_type => "Animal")
276
+ PgSearch::Document.delete_all(searchable_type: "Animal")
239
277
  ```
240
278
 
241
279
  To regenerate the documents for a given class, run:
@@ -271,7 +309,7 @@ the pg_search_documents table all at once. However, if you call any dynamic
271
309
  methods in :against, the following strategy will be used:
272
310
 
273
311
  ```ruby
274
- PgSearch::Document.delete_all(:searchable_type => "Ingredient")
312
+ PgSearch::Document.delete_all(searchable_type: "Ingredient")
275
313
  Ingredient.find_each { |record| record.update_pg_search_document }
276
314
  ```
277
315
 
@@ -337,7 +375,7 @@ To search against a column, pass a symbol as the :against option.
337
375
  ```ruby
338
376
  class BlogPost < ActiveRecord::Base
339
377
  include PgSearch
340
- pg_search_scope :search_by_title, :against => :title
378
+ pg_search_scope :search_by_title, against: :title
341
379
  end
342
380
  ```
343
381
 
@@ -345,8 +383,8 @@ We now have an ActiveRecord scope named search_by_title on our BlogPost model.
345
383
  It takes one parameter, a search query string.
346
384
 
347
385
  ```ruby
348
- BlogPost.create!(:title => "Recent Developments in the World of Pastrami")
349
- BlogPost.create!(:title => "Prosciutto and You: A Retrospective")
386
+ BlogPost.create!(title: "Recent Developments in the World of Pastrami")
387
+ BlogPost.create!(title: "Prosciutto and You: A Retrospective")
350
388
  BlogPost.search_by_title("pastrami") # => [#<BlogPost id: 2, title: "Recent Developments in the World of Pastrami">]
351
389
  ```
352
390
 
@@ -357,15 +395,15 @@ Just pass an Array if you'd like to search more than one column.
357
395
  ```ruby
358
396
  class Person < ActiveRecord::Base
359
397
  include PgSearch
360
- pg_search_scope :search_by_full_name, :against => [:first_name, :last_name]
398
+ pg_search_scope :search_by_full_name, against: [:first_name, :last_name]
361
399
  end
362
400
  ```
363
401
 
364
402
  Now our search query can match either or both of the columns.
365
403
 
366
404
  ```ruby
367
- person_1 = Person.create!(:first_name => "Grant", :last_name => "Hill")
368
- person_2 = Person.create!(:first_name => "Hugh", :last_name => "Grant")
405
+ person_1 = Person.create!(first_name: "Grant", last_name: "Hill")
406
+ person_2 = Person.create!(first_name: "Hugh", last_name: "Grant")
369
407
 
370
408
  Person.search_by_full_name("Grant") # => [person_1, person_2]
371
409
  Person.search_by_full_name("Grant Hill") # => [person_1]
@@ -384,17 +422,17 @@ value if you wanted.
384
422
  ```ruby
385
423
  class Person < ActiveRecord::Base
386
424
  include PgSearch
387
- pg_search_scope :search_by_name, lambda do |name_part, query|
425
+ pg_search_scope :search_by_name, lambda { |name_part, query|
388
426
  raise ArgumentError unless [:first, :last].include?(name_part)
389
427
  {
390
- :against => name_part,
391
- :query => query
428
+ against: name_part,
429
+ query: query
392
430
  }
393
- end
431
+ }
394
432
  end
395
433
 
396
- person_1 = Person.create!(:first_name => "Grant", :last_name => "Hill")
397
- person_2 = Person.create!(:first_name => "Hugh", :last_name => "Grant")
434
+ person_1 = Person.create!(first_name: "Grant", last_name: "Hill")
435
+ person_2 = Person.create!(first_name: "Hugh", last_name: "Grant")
398
436
 
399
437
  Person.search_by_name :first, "Grant" # => [person_1]
400
438
  Person.search_by_name :last, "Grant" # => [person_2]
@@ -424,11 +462,11 @@ class Salami < ActiveRecord::Base
424
462
  include PgSearch
425
463
 
426
464
  belongs_to :cracker
427
- has_many :cheeses, :through => :cracker
465
+ has_many :cheeses, through: :cracker
428
466
 
429
- pg_search_scope :tasty_search, :associated_against => {
430
- :cheeses => [:kind, :brand],
431
- :cracker => :kind
467
+ pg_search_scope :tasty_search, associated_against: {
468
+ cheeses: [:kind, :brand],
469
+ cracker: :kind
432
470
  }
433
471
  end
434
472
 
@@ -436,13 +474,13 @@ salami_1 = Salami.create!
436
474
  salami_2 = Salami.create!
437
475
  salami_3 = Salami.create!
438
476
 
439
- limburger = Cheese.create!(:kind => "Limburger")
440
- brie = Cheese.create!(:kind => "Brie")
441
- pepper_jack = Cheese.create!(:kind => "Pepper Jack")
477
+ limburger = Cheese.create!(kind: "Limburger")
478
+ brie = Cheese.create!(kind: "Brie")
479
+ pepper_jack = Cheese.create!(kind: "Pepper Jack")
442
480
 
443
- Cracker.create!(:kind => "Black Pepper", :cheeses => [brie], :salami => salami_1)
444
- Cracker.create!(:kind => "Ritz", :cheeses => [limburger, pepper_jack], :salami => salami_2)
445
- Cracker.create!(:kind => "Graham", :cheeses => [limburger], :salami => salami_3)
481
+ Cracker.create!(kind: "Black Pepper", cheeses: [brie], salami: salami_1)
482
+ Cracker.create!(kind: "Ritz", cheeses: [limburger, pepper_jack], salami: salami_2)
483
+ Cracker.create!(kind: "Graham", cheeses: [limburger], salami: salami_3)
446
484
 
447
485
  Salami.tasty_search("pepper") # => [salami_1, salami_2]
448
486
  ```
@@ -457,7 +495,7 @@ search techniques.
457
495
  ```ruby
458
496
  class Beer < ActiveRecord::Base
459
497
  include PgSearch
460
- pg_search_scope :search_name, :against => :name, :using => [:tsearch, :trigram, :dmetaphone]
498
+ pg_search_scope :search_name, against: :name, using: [:tsearch, :trigram, :dmetaphone]
461
499
  end
462
500
  ```
463
501
 
@@ -483,10 +521,10 @@ subtitle, and finally the content.
483
521
  ```ruby
484
522
  class NewsArticle < ActiveRecord::Base
485
523
  include PgSearch
486
- pg_search_scope :search_full_text, :against => {
487
- :title => 'A',
488
- :subtitle => 'B',
489
- :content => 'C'
524
+ pg_search_scope :search_full_text, against: {
525
+ title: 'A',
526
+ subtitle: 'B',
527
+ content: 'C'
490
528
  }
491
529
  end
492
530
  ```
@@ -498,7 +536,7 @@ weight. If you omit the weight, a default will be used.
498
536
  ```ruby
499
537
  class NewsArticle < ActiveRecord::Base
500
538
  include PgSearch
501
- pg_search_scope :search_full_text, :against => [
539
+ pg_search_scope :search_full_text, against: [
502
540
  [:title, 'A'],
503
541
  [:subtitle, 'B'],
504
542
  [:content, 'C']
@@ -507,9 +545,9 @@ end
507
545
 
508
546
  class NewsArticle < ActiveRecord::Base
509
547
  include PgSearch
510
- pg_search_scope :search_full_text, :against => [
548
+ pg_search_scope :search_full_text, against: [
511
549
  [:title, 'A'],
512
- {:subtitle => 'B'},
550
+ {subtitle: 'B'},
513
551
  :content
514
552
  ]
515
553
  end
@@ -526,15 +564,15 @@ shown in the following example.
526
564
  class Superhero < ActiveRecord::Base
527
565
  include PgSearch
528
566
  pg_search_scope :whose_name_starts_with,
529
- :against => :name,
530
- :using => {
531
- :tsearch => {:prefix => true}
567
+ against: :name,
568
+ using: {
569
+ tsearch: { prefix: true }
532
570
  }
533
571
  end
534
572
 
535
- batman = Superhero.create :name => 'Batman'
536
- batgirl = Superhero.create :name => 'Batgirl'
537
- robin = Superhero.create :name => 'Robin'
573
+ batman = Superhero.create name: 'Batman'
574
+ batgirl = Superhero.create name: 'Batgirl'
575
+ robin = Superhero.create name: 'Robin'
538
576
 
539
577
  Superhero.whose_name_starts_with("Bat") # => [batman, batgirl]
540
578
  ```
@@ -555,16 +593,16 @@ term that you were trying to exclude.
555
593
  class Animal < ActiveRecord::Base
556
594
  include PgSearch
557
595
  pg_search_scope :with_name_matching,
558
- :against => :name,
559
- :using => {
560
- :tsearch => {:negation => true}
596
+ against: :name,
597
+ using: {
598
+ tsearch: {negation: true}
561
599
  }
562
600
  end
563
601
 
564
- one_fish = Animal.create(:name => "one fish")
565
- two_fish = Animal.create(:name => "two fish")
566
- red_fish = Animal.create(:name => "red fish")
567
- blue_fish = Animal.create(:name => "blue fish")
602
+ one_fish = Animal.create(name: "one fish")
603
+ two_fish = Animal.create(name: "two fish")
604
+ red_fish = Animal.create(name: "red fish")
605
+ blue_fish = Animal.create(name: "blue fish")
568
606
 
569
607
  Animal.with_name_matching("fish !red !blue") # => [one_fish, two_fish]
570
608
  ```
@@ -584,20 +622,20 @@ dictionary will be used.
584
622
  class BoringTweet < ActiveRecord::Base
585
623
  include PgSearch
586
624
  pg_search_scope :kinda_matching,
587
- :against => :text,
588
- :using => {
589
- :tsearch => {:dictionary => "english"}
625
+ against: :text,
626
+ using: {
627
+ tsearch: {dictionary: "english"}
590
628
  }
591
629
  pg_search_scope :literally_matching,
592
- :against => :text,
593
- :using => {
594
- :tsearch => {:dictionary => "simple"}
630
+ against: :text,
631
+ using: {
632
+ tsearch: {dictionary: "simple"}
595
633
  }
596
634
  end
597
635
 
598
- sleepy = BoringTweet.create! :text => "I snoozed my alarm for fourteen hours today. I bet I can beat that tomorrow! #sleepy"
599
- sleeping = BoringTweet.create! :text => "You know what I like? Sleeping. That's what. #enjoyment"
600
- sleeper = BoringTweet.create! :text => "Have you seen Woody Allen's movie entitled Sleeper? Me neither. #boycott"
636
+ sleepy = BoringTweet.create! text: "I snoozed my alarm for fourteen hours today. I bet I can beat that tomorrow! #sleepy"
637
+ sleeping = BoringTweet.create! text: "You know what I like? Sleeping. That's what. #enjoyment"
638
+ sleeper = BoringTweet.create! text: "Have you seen Woody Allen's movie entitled Sleeper? Me neither. #boycott"
601
639
 
602
640
  BoringTweet.kinda_matching("sleeping") # => [sleepy, sleeping, sleeper]
603
641
  BoringTweet.literally_matching("sleeping") # => [sleeping]
@@ -628,16 +666,16 @@ their numbers together.
628
666
  class BigLongDocument < ActiveRecord::Base
629
667
  include PgSearch
630
668
  pg_search_scope :regular_search,
631
- :against => :text
669
+ against: :text
632
670
 
633
671
  pg_search_scope :short_search,
634
- :against => :text,
635
- :using => {
636
- :tsearch => {:normalization => 2}
672
+ against: :text,
673
+ using: {
674
+ tsearch: {normalization: 2}
637
675
  }
638
676
 
639
- long = BigLongDocument.create!(:text => "Four score and twenty years ago")
640
- short = BigLongDocument.create!(:text => "Four score")
677
+ long = BigLongDocument.create!(text: "Four score and twenty years ago")
678
+ short = BigLongDocument.create!(text: "Four score")
641
679
 
642
680
  BigLongDocument.regular_search("four score") #=> [long, short]
643
681
  BigLongDocument.short_search("four score") #=> [short, long]
@@ -652,18 +690,18 @@ models containing any word in the search terms.
652
690
  class Number < ActiveRecord::Base
653
691
  include PgSearch
654
692
  pg_search_scope :search_any_word,
655
- :against => :text,
656
- :using => {
657
- :tsearch => {:any_word => true}
693
+ against: :text,
694
+ using: {
695
+ tsearch: {any_word: true}
658
696
  }
659
697
 
660
698
  pg_search_scope :search_all_words,
661
- :against => :text
699
+ against: :text
662
700
  end
663
701
 
664
- one = Number.create! :text => 'one'
665
- two = Number.create! :text => 'two'
666
- three = Number.create! :text => 'three'
702
+ one = Number.create! text: 'one'
703
+ two = Number.create! text: 'two'
704
+ three = Number.create! text: 'three'
667
705
 
668
706
  Number.search_any_word('one two three') # => [one, two, three]
669
707
  Number.search_all_words('one two three') # => []
@@ -678,17 +716,17 @@ but will not include it in the query's WHERE condition.
678
716
  class Person < ActiveRecord::Base
679
717
  include PgSearch
680
718
  pg_search_scope :search,
681
- :against => :name,
682
- :using => {
683
- :tsearch => {:any_word => true}
684
- :dmetaphone => {:any_word => true, :sort_only => true}
719
+ against: :name,
720
+ using: {
721
+ tsearch: {any_word: true}
722
+ dmetaphone: {any_word: true, sort_only: true}
685
723
  }
686
724
  end
687
725
 
688
- exact = Person.create!(:name => 'ash hines')
689
- one_exact_one_close = Person.create!(:name => 'ash heinz')
690
- one_exact = Person.create!(:name => 'ash smith')
691
- one_close = Person.create!(:name => 'leigh heinz')
726
+ exact = Person.create!(name: 'ash hines')
727
+ one_exact_one_close = Person.create!(name: 'ash heinz')
728
+ one_exact = Person.create!(name: 'ash smith')
729
+ one_close = Person.create!(name: 'leigh heinz')
692
730
 
693
731
  Person.search('ash hines') # => [exact, one_exact_one_close, one_exact]
694
732
  ```
@@ -707,8 +745,8 @@ class Person < ActiveRecord::Base
707
745
  using: {
708
746
  tsearch: {
709
747
  highlight: {
710
- StartSel: '<start>',
711
- StopSel: '<stop>',
748
+ StartSel: '<b>',
749
+ StopSel: '</b>',
712
750
  MaxWords: 123,
713
751
  MinWords: 456,
714
752
  ShortWord: 4,
@@ -757,14 +795,14 @@ The following example shows how to use :dmetaphone.
757
795
  class Word < ActiveRecord::Base
758
796
  include PgSearch
759
797
  pg_search_scope :that_sounds_like,
760
- :against => :spelling,
761
- :using => :dmetaphone
798
+ against: :spelling,
799
+ using: :dmetaphone
762
800
  end
763
801
 
764
- four = Word.create! :spelling => 'four'
765
- far = Word.create! :spelling => 'far'
766
- fur = Word.create! :spelling => 'fur'
767
- five = Word.create! :spelling => 'five'
802
+ four = Word.create! spelling: 'four'
803
+ far = Word.create! spelling: 'far'
804
+ fur = Word.create! spelling: 'fur'
805
+ five = Word.create! spelling: 'five'
768
806
 
769
807
  Word.that_sounds_like("fir") # => [four, far, fur]
770
808
  ```
@@ -788,14 +826,14 @@ feature can be used.
788
826
  class Website < ActiveRecord::Base
789
827
  include PgSearch
790
828
  pg_search_scope :kinda_spelled_like,
791
- :against => :name,
792
- :using => :trigram
829
+ against: :name,
830
+ using: :trigram
793
831
  end
794
832
 
795
- yahooo = Website.create! :name => "Yahooo!"
796
- yohoo = Website.create! :name => "Yohoo!"
797
- gogle = Website.create! :name => "Gogle"
798
- facebook = Website.create! :name => "Facebook"
833
+ yahooo = Website.create! name: "Yahooo!"
834
+ yohoo = Website.create! name: "Yohoo!"
835
+ gogle = Website.create! name: "Gogle"
836
+ facebook = Website.create! name: "Facebook"
799
837
 
800
838
  Website.kinda_spelled_like("Yahoo!") # => [yahooo, yohoo]
801
839
  ```
@@ -814,23 +852,23 @@ class Vegetable < ActiveRecord::Base
814
852
  include PgSearch
815
853
 
816
854
  pg_search_scope :strictly_spelled_like,
817
- :against => :name,
818
- :using => {
819
- :trigram => {
820
- :threshold => 0.5
855
+ against: :name,
856
+ using: {
857
+ trigram: {
858
+ threshold: 0.5
821
859
  }
822
860
  }
823
861
 
824
862
  pg_search_scope :roughly_spelled_like,
825
- :against => :name,
826
- :using => {
827
- :trigram => {
828
- :threshold => 0.1
863
+ against: :name,
864
+ using: {
865
+ trigram: {
866
+ threshold: 0.1
829
867
  }
830
868
  }
831
869
  end
832
870
 
833
- cauliflower = Vegetable.create! :name => "cauliflower"
871
+ cauliflower = Vegetable.create! name: "cauliflower"
834
872
 
835
873
  Vegetable.roughly_spelled_like("couliflower") # => [cauliflower]
836
874
  Vegetable.strictly_spelled_like("couliflower") # => [cauliflower]
@@ -839,6 +877,37 @@ Vegetable.roughly_spelled_like("collyflower") # => [cauliflower]
839
877
  Vegetable.strictly_spelled_like("collyflower") # => []
840
878
  ```
841
879
 
880
+ ##### :word_similarity
881
+
882
+ Allows you to match words in longer strings.
883
+ By default, trigram searches use `%` or `similarity()` as a similarity value.
884
+ Set `word_similarity` to `true` to opt for `<%` and `word_similarity` instead.
885
+ This causes the trigram search to use the similarity of the query term
886
+ and the word with greatest similarity.
887
+
888
+ ```ruby
889
+ class Sentence < ActiveRecord::Base
890
+ include PgSearch
891
+
892
+ pg_search_scope :similarity_like,
893
+ against: :words,
894
+ using: {
895
+ trigram: {
896
+ word_similarity: true
897
+ }
898
+ }
899
+
900
+ pg_search_scope :word_similarity_like,
901
+ against: :words,
902
+ using: [:trigram]
903
+ end
904
+
905
+ sentence = Sentence.create! name: "Those are two words."
906
+
907
+ Sentence.similarity_like("word") # => []
908
+ Sentence.word_similarity_like("word") # => [sentence]
909
+ ```
910
+
842
911
  ### Limiting Fields When Combining Features
843
912
 
844
913
  Sometimes when doing queries combining different features you
@@ -852,11 +921,11 @@ class Image < ActiveRecord::Base
852
921
  include PgSearch
853
922
 
854
923
  pg_search_scope :combined_search,
855
- :against => [:file_name, :short_description, :long_description]
856
- :using => {
857
- :tsearch => { :dictionary => 'english' },
858
- :trigram => {
859
- :only => [:file_name, :short_description]
924
+ against: [:file_name, :short_description, :long_description]
925
+ using: {
926
+ tsearch: { dictionary: 'english' },
927
+ trigram: {
928
+ only: [:file_name, :short_description]
860
929
  }
861
930
  }
862
931
 
@@ -886,13 +955,13 @@ must be installed before this feature can be used.
886
955
  class SpanishQuestion < ActiveRecord::Base
887
956
  include PgSearch
888
957
  pg_search_scope :gringo_search,
889
- :against => :word,
890
- :ignoring => :accents
958
+ against: :word,
959
+ ignoring: :accents
891
960
  end
892
961
 
893
- what = SpanishQuestion.create(:word => "Qué")
894
- how_many = SpanishQuestion.create(:word => "Cuánto")
895
- how = SpanishQuestion.create(:word => "Cómo")
962
+ what = SpanishQuestion.create(word: "Qué")
963
+ how_many = SpanishQuestion.create(word: "Cuánto")
964
+ how = SpanishQuestion.create(word: "Cómo")
896
965
 
897
966
  SpanishQuestion.gringo_search("Que") # => [what]
898
967
  SpanishQuestion.gringo_search("Cüåñtô") # => [how_many]
@@ -929,8 +998,8 @@ To use this functionality you'll need to do a few things:
929
998
 
930
999
  ```ruby
931
1000
  pg_search_scope :fast_content_search,
932
- :against => :content,
933
- :using => {
1001
+ against: :content,
1002
+ using: {
934
1003
  dmetaphone: {
935
1004
  tsvector_column: 'tsvector_content_dmetaphone'
936
1005
  },
@@ -941,13 +1010,6 @@ To use this functionality you'll need to do a few things:
941
1010
  trigram: {} # trigram does not use tsvectors
942
1011
  }
943
1012
  ```
944
- * You cannot dump a `tsvector` column to `schema.rb`. Instead, you need to switch to using the native PostgreSQL SQL format schema dump.
945
- In your `config/application.rb` you should set
946
-
947
- config.active_record.schema_format = :sql
948
-
949
- Read more about it here: http://guides.rubyonrails.org/migrations.html#types-of-schema-dumps
950
-
951
1013
 
952
1014
  Please note that the :against column is only used when the tsvector_column is
953
1015
  not present for the search type.
@@ -958,26 +1020,26 @@ It's possible to search against more than one tsvector at a time. This could be
958
1020
 
959
1021
  ```ruby
960
1022
  pg_search_scope :search_title,
961
- :against => :title,
962
- :using => {
963
- :tsearch => {
964
- :tsvector_column => "title_tsvector"
1023
+ against: :title,
1024
+ using: {
1025
+ tsearch: {
1026
+ tsvector_column: "title_tsvector"
965
1027
  }
966
1028
  }
967
1029
 
968
1030
  pg_search_scope :search_body,
969
- :against => :body,
970
- :using => {
971
- :tsearch => {
972
- :tsvector_column => "body_tsvector"
1031
+ against: :body,
1032
+ using: {
1033
+ tsearch: {
1034
+ tsvector_column: "body_tsvector"
973
1035
  }
974
1036
  }
975
1037
 
976
1038
  pg_search_scope :search_title_and_body,
977
- :against => [:title, :body],
978
- :using => {
979
- :tsearch => {
980
- :tsvector_column => ["title_tsvector", "body_tsvector"]
1039
+ against: [:title, :body],
1040
+ using: {
1041
+ tsearch: {
1042
+ tsvector_column: ["title_tsvector", "body_tsvector"]
981
1043
  }
982
1044
  }
983
1045
  ```
@@ -992,9 +1054,9 @@ can pass a :ranked_by option to pg_search_scope.
992
1054
 
993
1055
  ```ruby
994
1056
  pg_search_scope :search_by_tsearch_but_rank_by_trigram,
995
- :against => :title,
996
- :using => [:tsearch],
997
- :ranked_by => ":trigram"
1057
+ against: :title,
1058
+ using: [:tsearch],
1059
+ ranked_by: ":trigram"
998
1060
  ```
999
1061
 
1000
1062
  Note that :ranked_by using a String to represent the ranking expression. This
@@ -1004,10 +1066,10 @@ expressions.
1004
1066
 
1005
1067
  ```ruby
1006
1068
  # Weighted ranking to balance multiple approaches
1007
- :ranked_by => ":dmetaphone + (0.25 * :trigram)"
1069
+ ranked_by: ":dmetaphone + (0.25 * :trigram)"
1008
1070
 
1009
1071
  # A more complex example, where books.num_pages is an integer column in the table itself
1010
- :ranked_by => "(books.num_pages * :trigram) + (:tsearch / 2.0)"
1072
+ ranked_by: "(books.num_pages * :trigram) + (:tsearch / 2.0)"
1011
1073
  ```
1012
1074
 
1013
1075
  #### :order_within_rank (Breaking ties)
@@ -1039,8 +1101,8 @@ descending by updated_at, to rank the most recently updated records first.
1039
1101
 
1040
1102
  ```ruby
1041
1103
  pg_search_scope :search_and_break_ties_by_latest_update,
1042
- :against => [:title, :content],
1043
- :order_within_rank => "blog_posts.updated_at DESC"
1104
+ against: [:title, :content],
1105
+ order_within_rank: "blog_posts.updated_at DESC"
1044
1106
  ```
1045
1107
 
1046
1108
  #### PgSearch#pg_search_rank (Reading a record's rank as a Float)
@@ -1077,7 +1139,7 @@ shirt_brands = ShirtBrand.search_by_name("Penguin")
1077
1139
 
1078
1140
  PgSearch would not have been possible without inspiration from texticle (now renamed
1079
1141
  [textacular](https://github.com/textacular/textacular)). Thanks to [Aaron
1080
- Patterson](http://tenderlovemaking.com/) for the original version!
1142
+ Patterson](http://tenderlovemaking.com/) for the original version and to Casebook PBC (https://www.casebook.net) for gifting the community with it!
1081
1143
 
1082
1144
  ## CONTRIBUTIONS AND FEEDBACK
1083
1145
 
@@ -1086,11 +1148,11 @@ project](https://www.pivotaltracker.com/projects/228645) where we manage new
1086
1148
  feature ideas and bugs.
1087
1149
 
1088
1150
  We also have a [Google Group](http://groups.google.com/group/casecommons-dev)
1089
- for discussing pg_search and other Case Commons open source projects.
1151
+ for discussing pg_search and other Casebook PBC open source projects.
1090
1152
 
1091
1153
  Please read our [CONTRIBUTING guide](https://github.com/Casecommons/pg_search/blob/master/CONTRIBUTING.md).
1092
1154
 
1093
1155
  ## LICENSE
1094
1156
 
1095
- Copyright © 2010–2017 [Case Commons, Inc](http://casecommons.org).
1157
+ Copyright © 2010–2019 [Casebook PBC](http://www.casebook.net).
1096
1158
  Licensed under the MIT license, see [LICENSE](/LICENSE) file.