pg_search 2.1.2 → 2.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.codeclimate.yml +1 -0
- data/.editorconfig +10 -0
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/ci.yml +75 -0
- data/.jrubyrc +1 -0
- data/.rubocop.yml +95 -10
- data/.travis.yml +26 -48
- data/CHANGELOG.md +179 -112
- data/CODE_OF_CONDUCT.md +76 -0
- data/CONTRIBUTING.md +5 -3
- data/Gemfile +6 -4
- data/LICENSE +1 -1
- data/README.md +307 -198
- data/Rakefile +7 -3
- data/lib/pg_search/configuration/association.rb +2 -0
- data/lib/pg_search/configuration/column.rb +2 -0
- data/lib/pg_search/configuration/foreign_column.rb +2 -0
- data/lib/pg_search/configuration.rb +20 -6
- data/lib/pg_search/document.rb +7 -5
- data/lib/pg_search/features/dmetaphone.rb +7 -7
- data/lib/pg_search/features/feature.rb +4 -2
- data/lib/pg_search/features/trigram.rb +31 -5
- data/lib/pg_search/features/tsearch.rb +18 -14
- data/lib/pg_search/features.rb +2 -0
- data/lib/pg_search/migration/dmetaphone_generator.rb +3 -1
- data/lib/pg_search/migration/generator.rb +4 -2
- data/lib/pg_search/migration/multisearch_generator.rb +2 -1
- data/lib/pg_search/migration/templates/add_pg_search_dmetaphone_support_functions.rb.erb +6 -6
- data/lib/pg_search/migration/templates/create_pg_search_documents.rb.erb +3 -3
- data/lib/pg_search/model.rb +57 -0
- data/lib/pg_search/multisearch/rebuilder.rb +14 -6
- data/lib/pg_search/multisearch.rb +23 -6
- data/lib/pg_search/multisearchable.rb +10 -6
- data/lib/pg_search/normalizer.rb +2 -0
- data/lib/pg_search/railtie.rb +2 -0
- data/lib/pg_search/scope_options.rb +26 -49
- data/lib/pg_search/tasks.rb +5 -1
- data/lib/pg_search/version.rb +3 -1
- data/lib/pg_search.rb +17 -55
- data/pg_search.gemspec +19 -11
- data/spec/.rubocop.yml +2 -2
- data/spec/integration/.rubocop.yml +11 -0
- data/spec/integration/associations_spec.rb +125 -162
- data/spec/integration/deprecation_spec.rb +33 -0
- data/spec/integration/pagination_spec.rb +10 -8
- data/spec/integration/pg_search_spec.rb +359 -306
- data/spec/integration/single_table_inheritance_spec.rb +18 -17
- data/spec/lib/pg_search/configuration/association_spec.rb +17 -13
- data/spec/lib/pg_search/configuration/column_spec.rb +2 -0
- data/spec/lib/pg_search/configuration/foreign_column_spec.rb +6 -4
- data/spec/lib/pg_search/features/dmetaphone_spec.rb +6 -4
- data/spec/lib/pg_search/features/trigram_spec.rb +51 -20
- data/spec/lib/pg_search/features/tsearch_spec.rb +29 -21
- data/spec/lib/pg_search/multisearch/rebuilder_spec.rb +151 -85
- data/spec/lib/pg_search/multisearch_spec.rb +67 -37
- data/spec/lib/pg_search/multisearchable_spec.rb +217 -123
- data/spec/lib/pg_search/normalizer_spec.rb +14 -10
- data/spec/lib/pg_search_spec.rb +102 -89
- data/spec/spec_helper.rb +25 -6
- data/spec/support/database.rb +19 -21
- data/spec/support/with_model.rb +2 -0
- metadata +106 -29
- data/.autotest +0 -5
- data/.rubocop_todo.yml +0 -163
- data/Guardfile +0 -6
data/README.md
CHANGED
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
# [pg_search](http://github.com/Casecommons/pg_search/)
|
|
2
2
|
|
|
3
3
|
[](https://rubygems.org/gems/pg_search)
|
|
4
|
-
[](https://codeclimate.com/github/Casecommons/pg_search)
|
|
6
|
-
[](https://codeclimate.com/github/Casecommons/pg_search/coverage)
|
|
7
|
-
[](https://gemnasium.com/Casecommons/pg_search)
|
|
8
|
-
[](http://inch-ci.org/github/Casecommons/pg_search)
|
|
4
|
+
[](https://github.com/Casecommons/pg_search/actions/workflows/ci.yml)
|
|
9
5
|
[](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
6
|
|
|
12
7
|
## DESCRIPTION
|
|
13
8
|
|
|
14
9
|
PgSearch builds named scopes that take advantage of PostgreSQL's full text
|
|
15
10
|
search.
|
|
16
11
|
|
|
17
|
-
Read the blog post introducing PgSearch at https://
|
|
12
|
+
Read the blog post introducing PgSearch at https://tanzu.vmware.com/content/blog/pg-search-how-i-learned-to-stop-worrying-and-love-postgresql-full-text-search
|
|
18
13
|
|
|
19
14
|
## REQUIREMENTS
|
|
20
15
|
|
|
21
|
-
* Ruby 2.
|
|
22
|
-
* ActiveRecord
|
|
16
|
+
* Ruby 2.6+
|
|
17
|
+
* ActiveRecord 5.2+
|
|
23
18
|
* PostgreSQL 9.2+
|
|
24
19
|
* [PostgreSQL extensions](https://github.com/Casecommons/pg_search/wiki/Installing-PostgreSQL-Extensions) for certain features
|
|
25
20
|
|
|
@@ -51,10 +46,51 @@ To add PgSearch to an Active Record model, simply include the PgSearch module.
|
|
|
51
46
|
|
|
52
47
|
```ruby
|
|
53
48
|
class Shape < ActiveRecord::Base
|
|
54
|
-
include PgSearch
|
|
49
|
+
include PgSearch::Model
|
|
55
50
|
end
|
|
56
51
|
```
|
|
57
52
|
|
|
53
|
+
### Contents
|
|
54
|
+
* [Multi-search vs. search scopes](#multi-search-vs-search-scopes)
|
|
55
|
+
* [Multi-search](#multi-search)
|
|
56
|
+
* [Setup](#setup)
|
|
57
|
+
* [`multisearchable`](#multisearchable)
|
|
58
|
+
* [More Options ](#more-options)
|
|
59
|
+
* [Multi-search associations](#multi-search-associations)
|
|
60
|
+
* [Searching in the global search index](#searching-in-the-global-search-index)
|
|
61
|
+
* [Chaining method calls onto the results](#chaining-method-calls-onto-the-results)
|
|
62
|
+
* [Configuring multi-search](#configuring-multi-search)
|
|
63
|
+
* [Rebuilding search documents for a given class](#rebuilding-search-documents-for-a-given-class)
|
|
64
|
+
* [Disabling multi-search indexing temporarily](#disabling-multi-search-indexing-temporarily)
|
|
65
|
+
* [`pg_search_scope`](#pg_search_scope)
|
|
66
|
+
* [Searching against one column](#searching-against-one-column)
|
|
67
|
+
* [Searching against multiple columns](#searching-against-multiple-columns)
|
|
68
|
+
* [Dynamic search scopes](#dynamic-search-scopes)
|
|
69
|
+
* [Searching through associations](#searching-through-associations)
|
|
70
|
+
* [Searching using different search features](#searching-using-different-search-features)
|
|
71
|
+
* [`:tsearch` (Full Text Search)](#tsearch-full-text-search)
|
|
72
|
+
* [Weighting](#weighting)
|
|
73
|
+
* [`:prefix` (PostgreSQL 8.4 and newer only)](#prefix-postgresql-84-and-newer-only)
|
|
74
|
+
* [`:negation`](#negation)
|
|
75
|
+
* [`:dictionary`](#dictionary)
|
|
76
|
+
* [`:normalization`](#normalization)
|
|
77
|
+
* [`:any_word`](#any_word)
|
|
78
|
+
* [`:sort_only`](#sort_only)
|
|
79
|
+
* [`:highlight`](#highlight)
|
|
80
|
+
* [`:dmetaphone` (Double Metaphone soundalike search)](#dmetaphone-double-metaphone-soundalike-search)
|
|
81
|
+
* [`:trigram` (Trigram search)](#trigram-trigram-search)
|
|
82
|
+
* [`:threshold`](#threshold)
|
|
83
|
+
* [`:word_similarity`](#word_similarity)
|
|
84
|
+
* [Limiting Fields When Combining Features](#limiting-fields-when-combining-features)
|
|
85
|
+
* [Ignoring accent marks](#ignoring-accent-marks)
|
|
86
|
+
* [Using tsvector columns](#using-tsvector-columns)
|
|
87
|
+
* [Combining multiple tsvectors](#combining-multiple-tsvectors)
|
|
88
|
+
* [Configuring ranking and ordering](#configuring-ranking-and-ordering)
|
|
89
|
+
* [`:ranked_by` (Choosing a ranking algorithm)](#ranked_by-choosing-a-ranking-algorithm)
|
|
90
|
+
* [`:order_within_rank` (Breaking ties)](#order_within_rank-breaking-ties)
|
|
91
|
+
* [`PgSearch#pg_search_rank` (Reading a record's rank as a Float)](#pgsearchpg_search_rank-reading-a-records-rank-as-a-float)
|
|
92
|
+
* [Search rank and chained scopes](#search-rank-and-chained-scopes)
|
|
93
|
+
|
|
58
94
|
### Multi-search vs. search scopes
|
|
59
95
|
|
|
60
96
|
pg_search supports two different techniques for searching, multi-search and
|
|
@@ -89,13 +125,13 @@ multisearchable in its class definition.
|
|
|
89
125
|
|
|
90
126
|
```ruby
|
|
91
127
|
class EpicPoem < ActiveRecord::Base
|
|
92
|
-
include PgSearch
|
|
93
|
-
multisearchable :
|
|
128
|
+
include PgSearch::Model
|
|
129
|
+
multisearchable against: [:title, :author]
|
|
94
130
|
end
|
|
95
131
|
|
|
96
132
|
class Flower < ActiveRecord::Base
|
|
97
|
-
include PgSearch
|
|
98
|
-
multisearchable :
|
|
133
|
+
include PgSearch::Model
|
|
134
|
+
multisearchable against: :color
|
|
99
135
|
end
|
|
100
136
|
```
|
|
101
137
|
|
|
@@ -114,15 +150,15 @@ particular record should be included.
|
|
|
114
150
|
|
|
115
151
|
```ruby
|
|
116
152
|
class Convertible < ActiveRecord::Base
|
|
117
|
-
include PgSearch
|
|
118
|
-
multisearchable :
|
|
119
|
-
:
|
|
153
|
+
include PgSearch::Model
|
|
154
|
+
multisearchable against: [:make, :model],
|
|
155
|
+
if: :available_in_red?
|
|
120
156
|
end
|
|
121
157
|
|
|
122
158
|
class Jalopy < ActiveRecord::Base
|
|
123
|
-
include PgSearch
|
|
124
|
-
multisearchable :
|
|
125
|
-
:
|
|
159
|
+
include PgSearch::Model
|
|
160
|
+
multisearchable against: [:make, :model],
|
|
161
|
+
if: lambda { |record| record.model_year > 1970 }
|
|
126
162
|
end
|
|
127
163
|
```
|
|
128
164
|
|
|
@@ -134,9 +170,9 @@ timestamp.
|
|
|
134
170
|
|
|
135
171
|
```ruby
|
|
136
172
|
class AntipatternExample
|
|
137
|
-
include PgSearch
|
|
138
|
-
multisearchable :
|
|
139
|
-
:
|
|
173
|
+
include PgSearch::Model
|
|
174
|
+
multisearchable against: [:contents],
|
|
175
|
+
if: :published?
|
|
140
176
|
|
|
141
177
|
def published?
|
|
142
178
|
published_at < Time.now
|
|
@@ -144,8 +180,8 @@ class AntipatternExample
|
|
|
144
180
|
end
|
|
145
181
|
|
|
146
182
|
problematic_record = AntipatternExample.create!(
|
|
147
|
-
:
|
|
148
|
-
:
|
|
183
|
+
contents: "Using :if with a timestamp",
|
|
184
|
+
published_at: 10.minutes.from_now
|
|
149
185
|
)
|
|
150
186
|
|
|
151
187
|
problematic_record.published? # => false
|
|
@@ -162,6 +198,51 @@ problematic_record.published? # => true
|
|
|
162
198
|
PgSearch.multisearch("timestamp") # => Includes problematic_record
|
|
163
199
|
```
|
|
164
200
|
|
|
201
|
+
#### More Options
|
|
202
|
+
|
|
203
|
+
**Conditionally update pg_search_documents**
|
|
204
|
+
|
|
205
|
+
You can specify an `:update_if` parameter to conditionally update pg_search documents. For example:
|
|
206
|
+
|
|
207
|
+
```ruby
|
|
208
|
+
multisearchable(
|
|
209
|
+
against: [:body],
|
|
210
|
+
update_if: :body_changed?
|
|
211
|
+
)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Specify additional attributes to be saved on the pg_search_documents table**
|
|
215
|
+
|
|
216
|
+
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.
|
|
217
|
+
|
|
218
|
+
First, we need to add a reference to author to the migration creating our `pg_search_documents` table.
|
|
219
|
+
|
|
220
|
+
```ruby
|
|
221
|
+
create_table :pg_search_documents do |t|
|
|
222
|
+
t.text :content
|
|
223
|
+
t.references :author, index: true
|
|
224
|
+
t.belongs_to :searchable, polymorphic: true, index: true
|
|
225
|
+
t.timestamps null: false
|
|
226
|
+
end
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Then, we can send in this additional attribute in a lambda
|
|
230
|
+
|
|
231
|
+
```ruby
|
|
232
|
+
multisearchable(
|
|
233
|
+
against: [:title, :body],
|
|
234
|
+
additional_attributes: -> (article) { { author_id: article.author_id } }
|
|
235
|
+
)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
This allows much faster searches without joins later on by doing something like:
|
|
239
|
+
|
|
240
|
+
```ruby
|
|
241
|
+
PgSearch.multisearch(params['search']).where(author_id: 2)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
*NOTE: You must currently manually call `record.update_pg_search_document` for the additional attribute to be included in the pg_search_documents table*
|
|
245
|
+
|
|
165
246
|
#### Multi-search associations
|
|
166
247
|
|
|
167
248
|
Two associations are built automatically. On the original record, there is a
|
|
@@ -170,7 +251,7 @@ record, and on the PgSearch::Document record there is a belongs_to :searchable
|
|
|
170
251
|
polymorphic association pointing back to the original record.
|
|
171
252
|
|
|
172
253
|
```ruby
|
|
173
|
-
odyssey = EpicPoem.create!(:
|
|
254
|
+
odyssey = EpicPoem.create!(title: "Odyssey", author: "Homer")
|
|
174
255
|
search_document = odyssey.pg_search_document #=> PgSearch::Document instance
|
|
175
256
|
search_document.searchable #=> #<EpicPoem id: 1, title: "Odyssey", author: "Homer">
|
|
176
257
|
```
|
|
@@ -181,8 +262,8 @@ To fetch the PgSearch::Document entries for all of the records that match a
|
|
|
181
262
|
given query, use PgSearch.multisearch.
|
|
182
263
|
|
|
183
264
|
```ruby
|
|
184
|
-
odyssey = EpicPoem.create!(:
|
|
185
|
-
rose = Flower.create!(:
|
|
265
|
+
odyssey = EpicPoem.create!(title: "Odyssey", author: "Homer")
|
|
266
|
+
rose = Flower.create!(color: "Red")
|
|
186
267
|
PgSearch.multisearch("Homer") #=> [#<PgSearch::Document searchable: odyssey>]
|
|
187
268
|
PgSearch.multisearch("Red") #=> [#<PgSearch::Document searchable: rose>]
|
|
188
269
|
```
|
|
@@ -196,7 +277,7 @@ receive SQL requests when necessary.
|
|
|
196
277
|
|
|
197
278
|
```ruby
|
|
198
279
|
PgSearch.multisearch("Bertha").limit(10)
|
|
199
|
-
PgSearch.multisearch("Juggler").where(:
|
|
280
|
+
PgSearch.multisearch("Juggler").where(searchable_type: "Occupation")
|
|
200
281
|
PgSearch.multisearch("Alamo").page(3).per(30)
|
|
201
282
|
PgSearch.multisearch("Diagonal").find_each do |document|
|
|
202
283
|
puts document.searchable.updated_at
|
|
@@ -213,8 +294,8 @@ PgSearch.multisearch_options in an initializer:
|
|
|
213
294
|
|
|
214
295
|
```ruby
|
|
215
296
|
PgSearch.multisearch_options = {
|
|
216
|
-
:
|
|
217
|
-
:
|
|
297
|
+
using: [:tsearch, :trigram],
|
|
298
|
+
ignoring: :accents
|
|
218
299
|
}
|
|
219
300
|
```
|
|
220
301
|
|
|
@@ -235,7 +316,7 @@ To remove all of the documents for a given class, you can simply delete all of
|
|
|
235
316
|
the PgSearch::Document records.
|
|
236
317
|
|
|
237
318
|
```ruby
|
|
238
|
-
PgSearch::Document.
|
|
319
|
+
PgSearch::Document.delete_by(searchable_type: "Animal")
|
|
239
320
|
```
|
|
240
321
|
|
|
241
322
|
To regenerate the documents for a given class, run:
|
|
@@ -251,7 +332,14 @@ is your base class. You can prevent ```rebuild``` from deleting your records
|
|
|
251
332
|
like so:
|
|
252
333
|
|
|
253
334
|
```ruby
|
|
254
|
-
PgSearch::Multisearch.rebuild(Product, false)
|
|
335
|
+
PgSearch::Multisearch.rebuild(Product, clean_up: false)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
```rebuild``` runs inside a single transaction. To run outside of a transaction,
|
|
339
|
+
you can pass ```transactional: false``` like so:
|
|
340
|
+
|
|
341
|
+
```ruby
|
|
342
|
+
PgSearch::Multisearch.rebuild(Product, transactional: false)
|
|
255
343
|
```
|
|
256
344
|
|
|
257
345
|
Rebuild is also available as a Rake task, for convenience.
|
|
@@ -271,7 +359,7 @@ the pg_search_documents table all at once. However, if you call any dynamic
|
|
|
271
359
|
methods in :against, the following strategy will be used:
|
|
272
360
|
|
|
273
361
|
```ruby
|
|
274
|
-
PgSearch::Document.delete_all(:
|
|
362
|
+
PgSearch::Document.delete_all(searchable_type: "Ingredient")
|
|
275
363
|
Ingredient.find_each { |record| record.update_pg_search_document }
|
|
276
364
|
```
|
|
277
365
|
|
|
@@ -295,11 +383,11 @@ class Movie < ActiveRecord::Base
|
|
|
295
383
|
|
|
296
384
|
# More sophisticated approach
|
|
297
385
|
def self.rebuild_pg_search_documents
|
|
298
|
-
connection.execute
|
|
386
|
+
connection.execute <<~SQL.squish
|
|
299
387
|
INSERT INTO pg_search_documents (searchable_type, searchable_id, content, created_at, updated_at)
|
|
300
388
|
SELECT 'Movie' AS searchable_type,
|
|
301
389
|
movies.id AS searchable_id,
|
|
302
|
-
(
|
|
390
|
+
CONCAT_WS(' ', movies.name, directors.name) AS content,
|
|
303
391
|
now() AS created_at,
|
|
304
392
|
now() AS updated_at
|
|
305
393
|
FROM movies
|
|
@@ -309,6 +397,7 @@ class Movie < ActiveRecord::Base
|
|
|
309
397
|
end
|
|
310
398
|
end
|
|
311
399
|
```
|
|
400
|
+
**Note:** If using PostgreSQL before 9.1, replace the `CONCAT_WS()` function call with double-pipe concatenation, eg. `(movies.name || ' ' || directors.name)`. However, now be aware that if *any* of the joined values is NULL then the final `content` value will also be NULL, whereas `CONCAT_WS()` will selectively ignore NULL values.
|
|
312
401
|
|
|
313
402
|
#### Disabling multi-search indexing temporarily
|
|
314
403
|
|
|
@@ -336,8 +425,8 @@ To search against a column, pass a symbol as the :against option.
|
|
|
336
425
|
|
|
337
426
|
```ruby
|
|
338
427
|
class BlogPost < ActiveRecord::Base
|
|
339
|
-
include PgSearch
|
|
340
|
-
pg_search_scope :search_by_title, :
|
|
428
|
+
include PgSearch::Model
|
|
429
|
+
pg_search_scope :search_by_title, against: :title
|
|
341
430
|
end
|
|
342
431
|
```
|
|
343
432
|
|
|
@@ -345,8 +434,8 @@ We now have an ActiveRecord scope named search_by_title on our BlogPost model.
|
|
|
345
434
|
It takes one parameter, a search query string.
|
|
346
435
|
|
|
347
436
|
```ruby
|
|
348
|
-
BlogPost.create!(:
|
|
349
|
-
BlogPost.create!(:
|
|
437
|
+
BlogPost.create!(title: "Recent Developments in the World of Pastrami")
|
|
438
|
+
BlogPost.create!(title: "Prosciutto and You: A Retrospective")
|
|
350
439
|
BlogPost.search_by_title("pastrami") # => [#<BlogPost id: 2, title: "Recent Developments in the World of Pastrami">]
|
|
351
440
|
```
|
|
352
441
|
|
|
@@ -356,16 +445,16 @@ Just pass an Array if you'd like to search more than one column.
|
|
|
356
445
|
|
|
357
446
|
```ruby
|
|
358
447
|
class Person < ActiveRecord::Base
|
|
359
|
-
include PgSearch
|
|
360
|
-
pg_search_scope :search_by_full_name, :
|
|
448
|
+
include PgSearch::Model
|
|
449
|
+
pg_search_scope :search_by_full_name, against: [:first_name, :last_name]
|
|
361
450
|
end
|
|
362
451
|
```
|
|
363
452
|
|
|
364
453
|
Now our search query can match either or both of the columns.
|
|
365
454
|
|
|
366
455
|
```ruby
|
|
367
|
-
person_1 = Person.create!(:
|
|
368
|
-
person_2 = Person.create!(:
|
|
456
|
+
person_1 = Person.create!(first_name: "Grant", last_name: "Hill")
|
|
457
|
+
person_2 = Person.create!(first_name: "Hugh", last_name: "Grant")
|
|
369
458
|
|
|
370
459
|
Person.search_by_full_name("Grant") # => [person_1, person_2]
|
|
371
460
|
Person.search_by_full_name("Grant Hill") # => [person_1]
|
|
@@ -383,18 +472,18 @@ value if you wanted.
|
|
|
383
472
|
|
|
384
473
|
```ruby
|
|
385
474
|
class Person < ActiveRecord::Base
|
|
386
|
-
include PgSearch
|
|
387
|
-
pg_search_scope :search_by_name, lambda
|
|
475
|
+
include PgSearch::Model
|
|
476
|
+
pg_search_scope :search_by_name, lambda { |name_part, query|
|
|
388
477
|
raise ArgumentError unless [:first, :last].include?(name_part)
|
|
389
478
|
{
|
|
390
|
-
:
|
|
391
|
-
:
|
|
479
|
+
against: name_part,
|
|
480
|
+
query: query
|
|
392
481
|
}
|
|
393
|
-
|
|
482
|
+
}
|
|
394
483
|
end
|
|
395
484
|
|
|
396
|
-
person_1 = Person.create!(:
|
|
397
|
-
person_2 = Person.create!(:
|
|
485
|
+
person_1 = Person.create!(first_name: "Grant", last_name: "Hill")
|
|
486
|
+
person_2 = Person.create!(first_name: "Hugh", last_name: "Grant")
|
|
398
487
|
|
|
399
488
|
Person.search_by_name :first, "Grant" # => [person_1]
|
|
400
489
|
Person.search_by_name :last, "Grant" # => [person_2]
|
|
@@ -421,14 +510,14 @@ class Cheese < ActiveRecord::Base
|
|
|
421
510
|
end
|
|
422
511
|
|
|
423
512
|
class Salami < ActiveRecord::Base
|
|
424
|
-
include PgSearch
|
|
513
|
+
include PgSearch::Model
|
|
425
514
|
|
|
426
515
|
belongs_to :cracker
|
|
427
|
-
has_many :cheeses, :
|
|
516
|
+
has_many :cheeses, through: :cracker
|
|
428
517
|
|
|
429
|
-
pg_search_scope :tasty_search, :
|
|
430
|
-
:
|
|
431
|
-
:
|
|
518
|
+
pg_search_scope :tasty_search, associated_against: {
|
|
519
|
+
cheeses: [:kind, :brand],
|
|
520
|
+
cracker: :kind
|
|
432
521
|
}
|
|
433
522
|
end
|
|
434
523
|
|
|
@@ -436,13 +525,13 @@ salami_1 = Salami.create!
|
|
|
436
525
|
salami_2 = Salami.create!
|
|
437
526
|
salami_3 = Salami.create!
|
|
438
527
|
|
|
439
|
-
limburger = Cheese.create!(:
|
|
440
|
-
brie = Cheese.create!(:
|
|
441
|
-
pepper_jack = Cheese.create!(:
|
|
528
|
+
limburger = Cheese.create!(kind: "Limburger")
|
|
529
|
+
brie = Cheese.create!(kind: "Brie")
|
|
530
|
+
pepper_jack = Cheese.create!(kind: "Pepper Jack")
|
|
442
531
|
|
|
443
|
-
Cracker.create!(:
|
|
444
|
-
Cracker.create!(:
|
|
445
|
-
Cracker.create!(:
|
|
532
|
+
Cracker.create!(kind: "Black Pepper", cheeses: [brie], salami: salami_1)
|
|
533
|
+
Cracker.create!(kind: "Ritz", cheeses: [limburger, pepper_jack], salami: salami_2)
|
|
534
|
+
Cracker.create!(kind: "Graham", cheeses: [limburger], salami: salami_3)
|
|
446
535
|
|
|
447
536
|
Salami.tasty_search("pepper") # => [salami_1, salami_2]
|
|
448
537
|
```
|
|
@@ -456,8 +545,8 @@ search techniques.
|
|
|
456
545
|
|
|
457
546
|
```ruby
|
|
458
547
|
class Beer < ActiveRecord::Base
|
|
459
|
-
include PgSearch
|
|
460
|
-
pg_search_scope :search_name, :
|
|
548
|
+
include PgSearch::Model
|
|
549
|
+
pg_search_scope :search_name, against: :name, using: [:tsearch, :trigram, :dmetaphone]
|
|
461
550
|
end
|
|
462
551
|
```
|
|
463
552
|
|
|
@@ -482,11 +571,11 @@ subtitle, and finally the content.
|
|
|
482
571
|
|
|
483
572
|
```ruby
|
|
484
573
|
class NewsArticle < ActiveRecord::Base
|
|
485
|
-
include PgSearch
|
|
486
|
-
pg_search_scope :search_full_text, :
|
|
487
|
-
:
|
|
488
|
-
:
|
|
489
|
-
:
|
|
574
|
+
include PgSearch::Model
|
|
575
|
+
pg_search_scope :search_full_text, against: {
|
|
576
|
+
title: 'A',
|
|
577
|
+
subtitle: 'B',
|
|
578
|
+
content: 'C'
|
|
490
579
|
}
|
|
491
580
|
end
|
|
492
581
|
```
|
|
@@ -497,8 +586,8 @@ weight. If you omit the weight, a default will be used.
|
|
|
497
586
|
|
|
498
587
|
```ruby
|
|
499
588
|
class NewsArticle < ActiveRecord::Base
|
|
500
|
-
include PgSearch
|
|
501
|
-
pg_search_scope :search_full_text, :
|
|
589
|
+
include PgSearch::Model
|
|
590
|
+
pg_search_scope :search_full_text, against: [
|
|
502
591
|
[:title, 'A'],
|
|
503
592
|
[:subtitle, 'B'],
|
|
504
593
|
[:content, 'C']
|
|
@@ -506,10 +595,10 @@ class NewsArticle < ActiveRecord::Base
|
|
|
506
595
|
end
|
|
507
596
|
|
|
508
597
|
class NewsArticle < ActiveRecord::Base
|
|
509
|
-
include PgSearch
|
|
510
|
-
pg_search_scope :search_full_text, :
|
|
598
|
+
include PgSearch::Model
|
|
599
|
+
pg_search_scope :search_full_text, against: [
|
|
511
600
|
[:title, 'A'],
|
|
512
|
-
{:
|
|
601
|
+
{subtitle: 'B'},
|
|
513
602
|
:content
|
|
514
603
|
]
|
|
515
604
|
end
|
|
@@ -524,17 +613,17 @@ shown in the following example.
|
|
|
524
613
|
|
|
525
614
|
```ruby
|
|
526
615
|
class Superhero < ActiveRecord::Base
|
|
527
|
-
include PgSearch
|
|
616
|
+
include PgSearch::Model
|
|
528
617
|
pg_search_scope :whose_name_starts_with,
|
|
529
|
-
:
|
|
530
|
-
:
|
|
531
|
-
:
|
|
618
|
+
against: :name,
|
|
619
|
+
using: {
|
|
620
|
+
tsearch: { prefix: true }
|
|
532
621
|
}
|
|
533
622
|
end
|
|
534
623
|
|
|
535
|
-
batman = Superhero.create :
|
|
536
|
-
batgirl = Superhero.create :
|
|
537
|
-
robin = Superhero.create :
|
|
624
|
+
batman = Superhero.create name: 'Batman'
|
|
625
|
+
batgirl = Superhero.create name: 'Batgirl'
|
|
626
|
+
robin = Superhero.create name: 'Robin'
|
|
538
627
|
|
|
539
628
|
Superhero.whose_name_starts_with("Bat") # => [batman, batgirl]
|
|
540
629
|
```
|
|
@@ -553,18 +642,18 @@ term that you were trying to exclude.
|
|
|
553
642
|
|
|
554
643
|
```ruby
|
|
555
644
|
class Animal < ActiveRecord::Base
|
|
556
|
-
include PgSearch
|
|
645
|
+
include PgSearch::Model
|
|
557
646
|
pg_search_scope :with_name_matching,
|
|
558
|
-
:
|
|
559
|
-
:
|
|
560
|
-
:
|
|
647
|
+
against: :name,
|
|
648
|
+
using: {
|
|
649
|
+
tsearch: {negation: true}
|
|
561
650
|
}
|
|
562
651
|
end
|
|
563
652
|
|
|
564
|
-
one_fish = Animal.create(:
|
|
565
|
-
two_fish = Animal.create(:
|
|
566
|
-
red_fish = Animal.create(:
|
|
567
|
-
blue_fish = Animal.create(:
|
|
653
|
+
one_fish = Animal.create(name: "one fish")
|
|
654
|
+
two_fish = Animal.create(name: "two fish")
|
|
655
|
+
red_fish = Animal.create(name: "red fish")
|
|
656
|
+
blue_fish = Animal.create(name: "blue fish")
|
|
568
657
|
|
|
569
658
|
Animal.with_name_matching("fish !red !blue") # => [one_fish, two_fish]
|
|
570
659
|
```
|
|
@@ -582,22 +671,22 @@ dictionary will be used.
|
|
|
582
671
|
|
|
583
672
|
```ruby
|
|
584
673
|
class BoringTweet < ActiveRecord::Base
|
|
585
|
-
include PgSearch
|
|
674
|
+
include PgSearch::Model
|
|
586
675
|
pg_search_scope :kinda_matching,
|
|
587
|
-
:
|
|
588
|
-
:
|
|
589
|
-
:
|
|
676
|
+
against: :text,
|
|
677
|
+
using: {
|
|
678
|
+
tsearch: {dictionary: "english"}
|
|
590
679
|
}
|
|
591
680
|
pg_search_scope :literally_matching,
|
|
592
|
-
:
|
|
593
|
-
:
|
|
594
|
-
:
|
|
681
|
+
against: :text,
|
|
682
|
+
using: {
|
|
683
|
+
tsearch: {dictionary: "simple"}
|
|
595
684
|
}
|
|
596
685
|
end
|
|
597
686
|
|
|
598
|
-
sleepy = BoringTweet.create! :
|
|
599
|
-
sleeping = BoringTweet.create! :
|
|
600
|
-
sleeper = BoringTweet.create! :
|
|
687
|
+
sleepy = BoringTweet.create! text: "I snoozed my alarm for fourteen hours today. I bet I can beat that tomorrow! #sleepy"
|
|
688
|
+
sleeping = BoringTweet.create! text: "You know what I like? Sleeping. That's what. #enjoyment"
|
|
689
|
+
sleeper = BoringTweet.create! text: "Have you seen Woody Allen's movie entitled Sleeper? Me neither. #boycott"
|
|
601
690
|
|
|
602
691
|
BoringTweet.kinda_matching("sleeping") # => [sleepy, sleeping, sleeper]
|
|
603
692
|
BoringTweet.literally_matching("sleeping") # => [sleeping]
|
|
@@ -626,18 +715,18 @@ their numbers together.
|
|
|
626
715
|
|
|
627
716
|
```ruby
|
|
628
717
|
class BigLongDocument < ActiveRecord::Base
|
|
629
|
-
include PgSearch
|
|
718
|
+
include PgSearch::Model
|
|
630
719
|
pg_search_scope :regular_search,
|
|
631
|
-
:
|
|
720
|
+
against: :text
|
|
632
721
|
|
|
633
722
|
pg_search_scope :short_search,
|
|
634
|
-
:
|
|
635
|
-
:
|
|
636
|
-
:
|
|
723
|
+
against: :text,
|
|
724
|
+
using: {
|
|
725
|
+
tsearch: {normalization: 2}
|
|
637
726
|
}
|
|
638
727
|
|
|
639
|
-
long = BigLongDocument.create!(:
|
|
640
|
-
short = BigLongDocument.create!(:
|
|
728
|
+
long = BigLongDocument.create!(text: "Four score and twenty years ago")
|
|
729
|
+
short = BigLongDocument.create!(text: "Four score")
|
|
641
730
|
|
|
642
731
|
BigLongDocument.regular_search("four score") #=> [long, short]
|
|
643
732
|
BigLongDocument.short_search("four score") #=> [short, long]
|
|
@@ -650,20 +739,20 @@ models containing any word in the search terms.
|
|
|
650
739
|
|
|
651
740
|
```ruby
|
|
652
741
|
class Number < ActiveRecord::Base
|
|
653
|
-
include PgSearch
|
|
742
|
+
include PgSearch::Model
|
|
654
743
|
pg_search_scope :search_any_word,
|
|
655
|
-
:
|
|
656
|
-
:
|
|
657
|
-
:
|
|
744
|
+
against: :text,
|
|
745
|
+
using: {
|
|
746
|
+
tsearch: {any_word: true}
|
|
658
747
|
}
|
|
659
748
|
|
|
660
749
|
pg_search_scope :search_all_words,
|
|
661
|
-
:
|
|
750
|
+
against: :text
|
|
662
751
|
end
|
|
663
752
|
|
|
664
|
-
one = Number.create! :
|
|
665
|
-
two = Number.create! :
|
|
666
|
-
three = Number.create! :
|
|
753
|
+
one = Number.create! text: 'one'
|
|
754
|
+
two = Number.create! text: 'two'
|
|
755
|
+
three = Number.create! text: 'three'
|
|
667
756
|
|
|
668
757
|
Number.search_any_word('one two three') # => [one, two, three]
|
|
669
758
|
Number.search_all_words('one two three') # => []
|
|
@@ -676,19 +765,19 @@ but will not include it in the query's WHERE condition.
|
|
|
676
765
|
|
|
677
766
|
```ruby
|
|
678
767
|
class Person < ActiveRecord::Base
|
|
679
|
-
include PgSearch
|
|
768
|
+
include PgSearch::Model
|
|
680
769
|
pg_search_scope :search,
|
|
681
|
-
:
|
|
682
|
-
:
|
|
683
|
-
:
|
|
684
|
-
:
|
|
770
|
+
against: :name,
|
|
771
|
+
using: {
|
|
772
|
+
tsearch: {any_word: true},
|
|
773
|
+
dmetaphone: {any_word: true, sort_only: true}
|
|
685
774
|
}
|
|
686
775
|
end
|
|
687
776
|
|
|
688
|
-
exact = Person.create!(:
|
|
689
|
-
one_exact_one_close = Person.create!(:
|
|
690
|
-
one_exact = Person.create!(:
|
|
691
|
-
one_close = Person.create!(:
|
|
777
|
+
exact = Person.create!(name: 'ash hines')
|
|
778
|
+
one_exact_one_close = Person.create!(name: 'ash heinz')
|
|
779
|
+
one_exact = Person.create!(name: 'ash smith')
|
|
780
|
+
one_close = Person.create!(name: 'leigh heinz')
|
|
692
781
|
|
|
693
782
|
Person.search('ash hines') # => [exact, one_exact_one_close, one_exact]
|
|
694
783
|
```
|
|
@@ -701,14 +790,14 @@ Adding .with_pg_search_highlight after the pg_search_scope you can access to
|
|
|
701
790
|
|
|
702
791
|
```ruby
|
|
703
792
|
class Person < ActiveRecord::Base
|
|
704
|
-
include PgSearch
|
|
793
|
+
include PgSearch::Model
|
|
705
794
|
pg_search_scope :search,
|
|
706
795
|
against: :bio,
|
|
707
796
|
using: {
|
|
708
797
|
tsearch: {
|
|
709
798
|
highlight: {
|
|
710
|
-
StartSel: '<
|
|
711
|
-
StopSel: '
|
|
799
|
+
StartSel: '<b>',
|
|
800
|
+
StopSel: '</b>',
|
|
712
801
|
MaxWords: 123,
|
|
713
802
|
MinWords: 456,
|
|
714
803
|
ShortWord: 4,
|
|
@@ -755,16 +844,16 @@ The following example shows how to use :dmetaphone.
|
|
|
755
844
|
|
|
756
845
|
```ruby
|
|
757
846
|
class Word < ActiveRecord::Base
|
|
758
|
-
include PgSearch
|
|
847
|
+
include PgSearch::Model
|
|
759
848
|
pg_search_scope :that_sounds_like,
|
|
760
|
-
:
|
|
761
|
-
:
|
|
849
|
+
against: :spelling,
|
|
850
|
+
using: :dmetaphone
|
|
762
851
|
end
|
|
763
852
|
|
|
764
|
-
four = Word.create! :
|
|
765
|
-
far = Word.create! :
|
|
766
|
-
fur = Word.create! :
|
|
767
|
-
five = Word.create! :
|
|
853
|
+
four = Word.create! spelling: 'four'
|
|
854
|
+
far = Word.create! spelling: 'far'
|
|
855
|
+
fur = Word.create! spelling: 'fur'
|
|
856
|
+
five = Word.create! spelling: 'five'
|
|
768
857
|
|
|
769
858
|
Word.that_sounds_like("fir") # => [four, far, fur]
|
|
770
859
|
```
|
|
@@ -786,16 +875,16 @@ feature can be used.
|
|
|
786
875
|
|
|
787
876
|
```ruby
|
|
788
877
|
class Website < ActiveRecord::Base
|
|
789
|
-
include PgSearch
|
|
878
|
+
include PgSearch::Model
|
|
790
879
|
pg_search_scope :kinda_spelled_like,
|
|
791
|
-
:
|
|
792
|
-
:
|
|
880
|
+
against: :name,
|
|
881
|
+
using: :trigram
|
|
793
882
|
end
|
|
794
883
|
|
|
795
|
-
yahooo = Website.create! :
|
|
796
|
-
yohoo = Website.create! :
|
|
797
|
-
gogle = Website.create! :
|
|
798
|
-
facebook = Website.create! :
|
|
884
|
+
yahooo = Website.create! name: "Yahooo!"
|
|
885
|
+
yohoo = Website.create! name: "Yohoo!"
|
|
886
|
+
gogle = Website.create! name: "Gogle"
|
|
887
|
+
facebook = Website.create! name: "Facebook"
|
|
799
888
|
|
|
800
889
|
Website.kinda_spelled_like("Yahoo!") # => [yahooo, yohoo]
|
|
801
890
|
```
|
|
@@ -811,26 +900,26 @@ threshold will force a table scan as the derived query uses the
|
|
|
811
900
|
|
|
812
901
|
```ruby
|
|
813
902
|
class Vegetable < ActiveRecord::Base
|
|
814
|
-
include PgSearch
|
|
903
|
+
include PgSearch::Model
|
|
815
904
|
|
|
816
905
|
pg_search_scope :strictly_spelled_like,
|
|
817
|
-
:
|
|
818
|
-
:
|
|
819
|
-
:
|
|
820
|
-
:
|
|
906
|
+
against: :name,
|
|
907
|
+
using: {
|
|
908
|
+
trigram: {
|
|
909
|
+
threshold: 0.5
|
|
821
910
|
}
|
|
822
911
|
}
|
|
823
912
|
|
|
824
913
|
pg_search_scope :roughly_spelled_like,
|
|
825
|
-
:
|
|
826
|
-
:
|
|
827
|
-
:
|
|
828
|
-
:
|
|
914
|
+
against: :name,
|
|
915
|
+
using: {
|
|
916
|
+
trigram: {
|
|
917
|
+
threshold: 0.1
|
|
829
918
|
}
|
|
830
919
|
}
|
|
831
920
|
end
|
|
832
921
|
|
|
833
|
-
cauliflower = Vegetable.create! :
|
|
922
|
+
cauliflower = Vegetable.create! name: "cauliflower"
|
|
834
923
|
|
|
835
924
|
Vegetable.roughly_spelled_like("couliflower") # => [cauliflower]
|
|
836
925
|
Vegetable.strictly_spelled_like("couliflower") # => [cauliflower]
|
|
@@ -839,6 +928,37 @@ Vegetable.roughly_spelled_like("collyflower") # => [cauliflower]
|
|
|
839
928
|
Vegetable.strictly_spelled_like("collyflower") # => []
|
|
840
929
|
```
|
|
841
930
|
|
|
931
|
+
##### :word_similarity
|
|
932
|
+
|
|
933
|
+
Allows you to match words in longer strings.
|
|
934
|
+
By default, trigram searches use `%` or `similarity()` as a similarity value.
|
|
935
|
+
Set `word_similarity` to `true` to opt for `<%` and `word_similarity` instead.
|
|
936
|
+
This causes the trigram search to use the similarity of the query term
|
|
937
|
+
and the word with greatest similarity.
|
|
938
|
+
|
|
939
|
+
```ruby
|
|
940
|
+
class Sentence < ActiveRecord::Base
|
|
941
|
+
include PgSearch::Model
|
|
942
|
+
|
|
943
|
+
pg_search_scope :similarity_like,
|
|
944
|
+
against: :name,
|
|
945
|
+
using: {
|
|
946
|
+
trigram: {
|
|
947
|
+
word_similarity: true
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
pg_search_scope :word_similarity_like,
|
|
952
|
+
against: :name,
|
|
953
|
+
using: [:trigram]
|
|
954
|
+
end
|
|
955
|
+
|
|
956
|
+
sentence = Sentence.create! name: "Those are two words."
|
|
957
|
+
|
|
958
|
+
Sentence.similarity_like("word") # => []
|
|
959
|
+
Sentence.word_similarity_like("word") # => [sentence]
|
|
960
|
+
```
|
|
961
|
+
|
|
842
962
|
### Limiting Fields When Combining Features
|
|
843
963
|
|
|
844
964
|
Sometimes when doing queries combining different features you
|
|
@@ -849,14 +969,14 @@ which fields using the 'only' option:
|
|
|
849
969
|
|
|
850
970
|
```ruby
|
|
851
971
|
class Image < ActiveRecord::Base
|
|
852
|
-
include PgSearch
|
|
972
|
+
include PgSearch::Model
|
|
853
973
|
|
|
854
974
|
pg_search_scope :combined_search,
|
|
855
|
-
:
|
|
856
|
-
:
|
|
857
|
-
:
|
|
858
|
-
:
|
|
859
|
-
:
|
|
975
|
+
against: [:file_name, :short_description, :long_description]
|
|
976
|
+
using: {
|
|
977
|
+
tsearch: { dictionary: 'english' },
|
|
978
|
+
trigram: {
|
|
979
|
+
only: [:file_name, :short_description]
|
|
860
980
|
}
|
|
861
981
|
}
|
|
862
982
|
|
|
@@ -884,15 +1004,15 @@ must be installed before this feature can be used.
|
|
|
884
1004
|
|
|
885
1005
|
```ruby
|
|
886
1006
|
class SpanishQuestion < ActiveRecord::Base
|
|
887
|
-
include PgSearch
|
|
1007
|
+
include PgSearch::Model
|
|
888
1008
|
pg_search_scope :gringo_search,
|
|
889
|
-
:
|
|
890
|
-
:
|
|
1009
|
+
against: :word,
|
|
1010
|
+
ignoring: :accents
|
|
891
1011
|
end
|
|
892
1012
|
|
|
893
|
-
what = SpanishQuestion.create(:
|
|
894
|
-
how_many = SpanishQuestion.create(:
|
|
895
|
-
how = SpanishQuestion.create(:
|
|
1013
|
+
what = SpanishQuestion.create(word: "Qué")
|
|
1014
|
+
how_many = SpanishQuestion.create(word: "Cuánto")
|
|
1015
|
+
how = SpanishQuestion.create(word: "Cómo")
|
|
896
1016
|
|
|
897
1017
|
SpanishQuestion.gringo_search("Que") # => [what]
|
|
898
1018
|
SpanishQuestion.gringo_search("Cüåñtô") # => [how_many]
|
|
@@ -929,8 +1049,8 @@ To use this functionality you'll need to do a few things:
|
|
|
929
1049
|
|
|
930
1050
|
```ruby
|
|
931
1051
|
pg_search_scope :fast_content_search,
|
|
932
|
-
:
|
|
933
|
-
:
|
|
1052
|
+
against: :content,
|
|
1053
|
+
using: {
|
|
934
1054
|
dmetaphone: {
|
|
935
1055
|
tsvector_column: 'tsvector_content_dmetaphone'
|
|
936
1056
|
},
|
|
@@ -941,13 +1061,6 @@ To use this functionality you'll need to do a few things:
|
|
|
941
1061
|
trigram: {} # trigram does not use tsvectors
|
|
942
1062
|
}
|
|
943
1063
|
```
|
|
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
1064
|
|
|
952
1065
|
Please note that the :against column is only used when the tsvector_column is
|
|
953
1066
|
not present for the search type.
|
|
@@ -958,26 +1071,26 @@ It's possible to search against more than one tsvector at a time. This could be
|
|
|
958
1071
|
|
|
959
1072
|
```ruby
|
|
960
1073
|
pg_search_scope :search_title,
|
|
961
|
-
:
|
|
962
|
-
:
|
|
963
|
-
:
|
|
964
|
-
:
|
|
1074
|
+
against: :title,
|
|
1075
|
+
using: {
|
|
1076
|
+
tsearch: {
|
|
1077
|
+
tsvector_column: "title_tsvector"
|
|
965
1078
|
}
|
|
966
1079
|
}
|
|
967
1080
|
|
|
968
1081
|
pg_search_scope :search_body,
|
|
969
|
-
:
|
|
970
|
-
:
|
|
971
|
-
:
|
|
972
|
-
:
|
|
1082
|
+
against: :body,
|
|
1083
|
+
using: {
|
|
1084
|
+
tsearch: {
|
|
1085
|
+
tsvector_column: "body_tsvector"
|
|
973
1086
|
}
|
|
974
1087
|
}
|
|
975
1088
|
|
|
976
1089
|
pg_search_scope :search_title_and_body,
|
|
977
|
-
:
|
|
978
|
-
:
|
|
979
|
-
:
|
|
980
|
-
:
|
|
1090
|
+
against: [:title, :body],
|
|
1091
|
+
using: {
|
|
1092
|
+
tsearch: {
|
|
1093
|
+
tsvector_column: ["title_tsvector", "body_tsvector"]
|
|
981
1094
|
}
|
|
982
1095
|
}
|
|
983
1096
|
```
|
|
@@ -992,9 +1105,9 @@ can pass a :ranked_by option to pg_search_scope.
|
|
|
992
1105
|
|
|
993
1106
|
```ruby
|
|
994
1107
|
pg_search_scope :search_by_tsearch_but_rank_by_trigram,
|
|
995
|
-
:
|
|
996
|
-
:
|
|
997
|
-
:
|
|
1108
|
+
against: :title,
|
|
1109
|
+
using: [:tsearch],
|
|
1110
|
+
ranked_by: ":trigram"
|
|
998
1111
|
```
|
|
999
1112
|
|
|
1000
1113
|
Note that :ranked_by using a String to represent the ranking expression. This
|
|
@@ -1004,10 +1117,10 @@ expressions.
|
|
|
1004
1117
|
|
|
1005
1118
|
```ruby
|
|
1006
1119
|
# Weighted ranking to balance multiple approaches
|
|
1007
|
-
:
|
|
1120
|
+
ranked_by: ":dmetaphone + (0.25 * :trigram)"
|
|
1008
1121
|
|
|
1009
1122
|
# A more complex example, where books.num_pages is an integer column in the table itself
|
|
1010
|
-
:
|
|
1123
|
+
ranked_by: "(books.num_pages * :trigram) + (:tsearch / 2.0)"
|
|
1011
1124
|
```
|
|
1012
1125
|
|
|
1013
1126
|
#### :order_within_rank (Breaking ties)
|
|
@@ -1039,8 +1152,8 @@ descending by updated_at, to rank the most recently updated records first.
|
|
|
1039
1152
|
|
|
1040
1153
|
```ruby
|
|
1041
1154
|
pg_search_scope :search_and_break_ties_by_latest_update,
|
|
1042
|
-
:
|
|
1043
|
-
:
|
|
1155
|
+
against: [:title, :content],
|
|
1156
|
+
order_within_rank: "blog_posts.updated_at DESC"
|
|
1044
1157
|
```
|
|
1045
1158
|
|
|
1046
1159
|
#### PgSearch#pg_search_rank (Reading a record's rank as a Float)
|
|
@@ -1077,20 +1190,16 @@ shirt_brands = ShirtBrand.search_by_name("Penguin")
|
|
|
1077
1190
|
|
|
1078
1191
|
PgSearch would not have been possible without inspiration from texticle (now renamed
|
|
1079
1192
|
[textacular](https://github.com/textacular/textacular)). Thanks to [Aaron
|
|
1080
|
-
Patterson](http://tenderlovemaking.com/) for the original version!
|
|
1193
|
+
Patterson](http://tenderlovemaking.com/) for the original version and to Casebook PBC (https://www.casebook.net) for gifting the community with it!
|
|
1081
1194
|
|
|
1082
1195
|
## CONTRIBUTIONS AND FEEDBACK
|
|
1083
1196
|
|
|
1084
|
-
|
|
1085
|
-
project](https://www.pivotaltracker.com/projects/228645) where we manage new
|
|
1086
|
-
feature ideas and bugs.
|
|
1197
|
+
Please read our [CONTRIBUTING guide](https://github.com/Casecommons/pg_search/blob/master/CONTRIBUTING.md).
|
|
1087
1198
|
|
|
1088
1199
|
We also have a [Google Group](http://groups.google.com/group/casecommons-dev)
|
|
1089
|
-
for discussing pg_search and other
|
|
1090
|
-
|
|
1091
|
-
Please read our [CONTRIBUTING guide](https://github.com/Casecommons/pg_search/blob/master/CONTRIBUTING.md).
|
|
1200
|
+
for discussing pg_search and other Casebook PBC open source projects.
|
|
1092
1201
|
|
|
1093
1202
|
## LICENSE
|
|
1094
1203
|
|
|
1095
|
-
Copyright © 2010–
|
|
1204
|
+
Copyright © 2010–2021 [Casebook PBC](http://www.casebook.net).
|
|
1096
1205
|
Licensed under the MIT license, see [LICENSE](/LICENSE) file.
|