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.
- 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
data/README.md
CHANGED
@@ -2,12 +2,10 @@
|
|
2
2
|
|
3
3
|
[](https://rubygems.org/gems/pg_search)
|
4
4
|
[](https://travis-ci.org/Casecommons/pg_search)
|
5
|
-
[](https://codeclimate.com/github/Casecommons/pg_search/maintainability)
|
6
6
|
[](https://codeclimate.com/github/Casecommons/pg_search/coverage)
|
7
|
-
[](https://gemnasium.com/Casecommons/pg_search)
|
8
7
|
[](http://inch-ci.org/github/Casecommons/pg_search)
|
9
8
|
[](https://gitter.im/Casecommons/pg_search?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
10
|
-
[](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.
|
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 :
|
91
|
+
multisearchable against: [:title, :author]
|
94
92
|
end
|
95
93
|
|
96
94
|
class Flower < ActiveRecord::Base
|
97
95
|
include PgSearch
|
98
|
-
multisearchable :
|
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 :
|
119
|
-
:
|
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 :
|
125
|
-
:
|
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 :
|
139
|
-
:
|
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
|
-
:
|
148
|
-
:
|
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!(:
|
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!(:
|
185
|
-
rose = Flower.create!(:
|
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(:
|
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
|
-
:
|
217
|
-
:
|
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(:
|
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(:
|
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, :
|
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!(:
|
349
|
-
BlogPost.create!(:
|
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, :
|
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!(:
|
368
|
-
person_2 = Person.create!(:
|
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
|
425
|
+
pg_search_scope :search_by_name, lambda { |name_part, query|
|
388
426
|
raise ArgumentError unless [:first, :last].include?(name_part)
|
389
427
|
{
|
390
|
-
:
|
391
|
-
:
|
428
|
+
against: name_part,
|
429
|
+
query: query
|
392
430
|
}
|
393
|
-
|
431
|
+
}
|
394
432
|
end
|
395
433
|
|
396
|
-
person_1 = Person.create!(:
|
397
|
-
person_2 = Person.create!(:
|
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, :
|
465
|
+
has_many :cheeses, through: :cracker
|
428
466
|
|
429
|
-
pg_search_scope :tasty_search, :
|
430
|
-
:
|
431
|
-
:
|
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!(:
|
440
|
-
brie = Cheese.create!(:
|
441
|
-
pepper_jack = Cheese.create!(:
|
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!(:
|
444
|
-
Cracker.create!(:
|
445
|
-
Cracker.create!(:
|
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, :
|
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, :
|
487
|
-
:
|
488
|
-
:
|
489
|
-
:
|
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, :
|
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, :
|
548
|
+
pg_search_scope :search_full_text, against: [
|
511
549
|
[:title, 'A'],
|
512
|
-
{:
|
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
|
-
:
|
530
|
-
:
|
531
|
-
:
|
567
|
+
against: :name,
|
568
|
+
using: {
|
569
|
+
tsearch: { prefix: true }
|
532
570
|
}
|
533
571
|
end
|
534
572
|
|
535
|
-
batman = Superhero.create :
|
536
|
-
batgirl = Superhero.create :
|
537
|
-
robin = Superhero.create :
|
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
|
-
:
|
559
|
-
:
|
560
|
-
:
|
596
|
+
against: :name,
|
597
|
+
using: {
|
598
|
+
tsearch: {negation: true}
|
561
599
|
}
|
562
600
|
end
|
563
601
|
|
564
|
-
one_fish = Animal.create(:
|
565
|
-
two_fish = Animal.create(:
|
566
|
-
red_fish = Animal.create(:
|
567
|
-
blue_fish = Animal.create(:
|
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
|
-
:
|
588
|
-
:
|
589
|
-
:
|
625
|
+
against: :text,
|
626
|
+
using: {
|
627
|
+
tsearch: {dictionary: "english"}
|
590
628
|
}
|
591
629
|
pg_search_scope :literally_matching,
|
592
|
-
:
|
593
|
-
:
|
594
|
-
:
|
630
|
+
against: :text,
|
631
|
+
using: {
|
632
|
+
tsearch: {dictionary: "simple"}
|
595
633
|
}
|
596
634
|
end
|
597
635
|
|
598
|
-
sleepy = BoringTweet.create! :
|
599
|
-
sleeping = BoringTweet.create! :
|
600
|
-
sleeper = BoringTweet.create! :
|
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
|
-
:
|
669
|
+
against: :text
|
632
670
|
|
633
671
|
pg_search_scope :short_search,
|
634
|
-
:
|
635
|
-
:
|
636
|
-
:
|
672
|
+
against: :text,
|
673
|
+
using: {
|
674
|
+
tsearch: {normalization: 2}
|
637
675
|
}
|
638
676
|
|
639
|
-
long = BigLongDocument.create!(:
|
640
|
-
short = BigLongDocument.create!(:
|
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
|
-
:
|
656
|
-
:
|
657
|
-
:
|
693
|
+
against: :text,
|
694
|
+
using: {
|
695
|
+
tsearch: {any_word: true}
|
658
696
|
}
|
659
697
|
|
660
698
|
pg_search_scope :search_all_words,
|
661
|
-
:
|
699
|
+
against: :text
|
662
700
|
end
|
663
701
|
|
664
|
-
one = Number.create! :
|
665
|
-
two = Number.create! :
|
666
|
-
three = Number.create! :
|
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
|
-
:
|
682
|
-
:
|
683
|
-
:
|
684
|
-
:
|
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!(:
|
689
|
-
one_exact_one_close = Person.create!(:
|
690
|
-
one_exact = Person.create!(:
|
691
|
-
one_close = Person.create!(:
|
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: '<
|
711
|
-
StopSel: '
|
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
|
-
:
|
761
|
-
:
|
798
|
+
against: :spelling,
|
799
|
+
using: :dmetaphone
|
762
800
|
end
|
763
801
|
|
764
|
-
four = Word.create! :
|
765
|
-
far = Word.create! :
|
766
|
-
fur = Word.create! :
|
767
|
-
five = Word.create! :
|
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
|
-
:
|
792
|
-
:
|
829
|
+
against: :name,
|
830
|
+
using: :trigram
|
793
831
|
end
|
794
832
|
|
795
|
-
yahooo = Website.create! :
|
796
|
-
yohoo = Website.create! :
|
797
|
-
gogle = Website.create! :
|
798
|
-
facebook = Website.create! :
|
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
|
-
:
|
818
|
-
:
|
819
|
-
:
|
820
|
-
:
|
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
|
-
:
|
826
|
-
:
|
827
|
-
:
|
828
|
-
:
|
863
|
+
against: :name,
|
864
|
+
using: {
|
865
|
+
trigram: {
|
866
|
+
threshold: 0.1
|
829
867
|
}
|
830
868
|
}
|
831
869
|
end
|
832
870
|
|
833
|
-
cauliflower = Vegetable.create! :
|
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
|
-
:
|
856
|
-
:
|
857
|
-
:
|
858
|
-
:
|
859
|
-
:
|
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
|
-
:
|
890
|
-
:
|
958
|
+
against: :word,
|
959
|
+
ignoring: :accents
|
891
960
|
end
|
892
961
|
|
893
|
-
what = SpanishQuestion.create(:
|
894
|
-
how_many = SpanishQuestion.create(:
|
895
|
-
how = SpanishQuestion.create(:
|
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
|
-
:
|
933
|
-
:
|
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
|
-
:
|
962
|
-
:
|
963
|
-
:
|
964
|
-
:
|
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
|
-
:
|
970
|
-
:
|
971
|
-
:
|
972
|
-
:
|
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
|
-
:
|
978
|
-
:
|
979
|
-
:
|
980
|
-
:
|
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
|
-
:
|
996
|
-
:
|
997
|
-
:
|
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
|
-
:
|
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
|
-
:
|
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
|
-
:
|
1043
|
-
:
|
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
|
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–
|
1157
|
+
Copyright © 2010–2019 [Casebook PBC](http://www.casebook.net).
|
1096
1158
|
Licensed under the MIT license, see [LICENSE](/LICENSE) file.
|