pg_search 1.0.5 → 1.0.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 +4 -4
- data/.rubocop_todo.yml +111 -28
- data/.travis.yml +4 -3
- data/CHANGELOG.md +7 -2
- data/Gemfile +0 -7
- data/README.md +36 -3
- data/lib/pg_search.rb +12 -1
- data/lib/pg_search/document.rb +0 -11
- data/lib/pg_search/features/tsearch.rb +43 -8
- data/lib/pg_search/multisearchable.rb +30 -1
- data/lib/pg_search/scope_options.rb +28 -1
- data/lib/pg_search/version.rb +1 -1
- data/pg_search.gemspec +2 -2
- data/spec/integration/pg_search_spec.rb +27 -0
- data/spec/lib/pg_search/multisearch/rebuilder_spec.rb +13 -0
- data/spec/lib/pg_search/multisearchable_spec.rb +142 -0
- data/spec/support/database.rb +2 -2
- metadata +5 -7
- data/spec/lib/pg_search/document_spec.rb +0 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 55ed2cdc0eaecdb0dcb05ee5fcf7e7460556669d
|
4
|
+
data.tar.gz: 8ff17add1a0a7c7a16dd6501b6335e5d364b07b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1acf5bc208de87a781c1c2ddaa3b44b33609afb8927cd8bbbb256a9a9caf099f3c2507232a0d9f2a9ad45acaf997e3590bbcbac6d3cb4936515e6874c3cb4ea1
|
7
|
+
data.tar.gz: d569da3e3f8aa7b205596fbce5d1bd72e0db4dab9576ac9331b810b4dbcefd80f77918a8ce51cfb3e944f63520f4166e86bd5db883b01c0e2b8f97e3bff7018d
|
data/.rubocop_todo.yml
CHANGED
@@ -1,37 +1,84 @@
|
|
1
|
-
# This configuration was generated by
|
2
|
-
#
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2016-01-25 14:11:25 -0600 using RuboCop version 0.36.0.
|
3
4
|
# The point is for the user to remove these configuration records
|
4
5
|
# one by one as the offenses are removed from the code base.
|
5
6
|
# Note that changes in the inspected code, or installation of new
|
6
7
|
# versions of RuboCop, may require this file to be generated again.
|
7
8
|
|
9
|
+
# Offense count: 2
|
10
|
+
# Cop supports --auto-correct.
|
11
|
+
Lint/UnneededDisable:
|
12
|
+
Exclude:
|
13
|
+
- 'lib/pg_search/features/tsearch.rb'
|
14
|
+
- 'lib/pg_search/scope_options.rb'
|
15
|
+
|
16
|
+
# Offense count: 1
|
17
|
+
# Cop supports --auto-correct.
|
18
|
+
Performance/Casecmp:
|
19
|
+
Exclude:
|
20
|
+
- 'spec/support/database.rb'
|
21
|
+
|
8
22
|
# Offense count: 27
|
9
23
|
Style/Documentation:
|
10
24
|
Enabled: false
|
11
25
|
|
12
|
-
# Offense count:
|
26
|
+
# Offense count: 3
|
13
27
|
# Cop supports --auto-correct.
|
14
|
-
|
15
|
-
|
28
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
29
|
+
# SupportedStyles: when_needed, always
|
30
|
+
Style/FrozenStringLiteralComment:
|
31
|
+
Exclude:
|
32
|
+
- 'lib/pg_search/migration/associated_against_generator.rb'
|
33
|
+
- 'lib/pg_search/migration/dmetaphone_generator.rb'
|
34
|
+
- 'lib/pg_search/version.rb'
|
16
35
|
|
17
|
-
# Offense count:
|
36
|
+
# Offense count: 1
|
37
|
+
Style/IfInsideElse:
|
38
|
+
Exclude:
|
39
|
+
- 'lib/pg_search/multisearchable.rb'
|
40
|
+
|
41
|
+
# Offense count: 26
|
18
42
|
# Cop supports --auto-correct.
|
19
|
-
# Configuration parameters:
|
20
|
-
|
43
|
+
# Configuration parameters: SupportedStyles, IndentationWidth.
|
44
|
+
# SupportedStyles: special_inside_parentheses, consistent, align_brackets
|
45
|
+
Style/IndentArray:
|
46
|
+
EnforcedStyle: consistent
|
47
|
+
|
48
|
+
# Offense count: 3
|
49
|
+
# Cop supports --auto-correct.
|
50
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
51
|
+
# SupportedStyles: aligned, indented
|
52
|
+
Style/MultilineMethodCallIndentation:
|
21
53
|
Enabled: false
|
22
54
|
|
55
|
+
# Offense count: 2
|
56
|
+
# Cop supports --auto-correct.
|
57
|
+
Style/MutableConstant:
|
58
|
+
Exclude:
|
59
|
+
- 'lib/pg_search/configuration.rb'
|
60
|
+
- 'lib/pg_search/scope_options.rb'
|
61
|
+
|
62
|
+
# Offense count: 16
|
63
|
+
# Cop supports --auto-correct.
|
64
|
+
Style/NumericLiterals:
|
65
|
+
MinDigits: 6
|
66
|
+
|
23
67
|
# Offense count: 1
|
24
68
|
# Cop supports --auto-correct.
|
25
69
|
Style/PerlBackrefs:
|
26
|
-
|
70
|
+
Exclude:
|
71
|
+
- 'lib/pg_search/scope_options.rb'
|
27
72
|
|
28
73
|
# Offense count: 1
|
29
74
|
# Cop supports --auto-correct.
|
30
75
|
Style/Proc:
|
31
|
-
|
76
|
+
Exclude:
|
77
|
+
- 'lib/pg_search/document.rb'
|
32
78
|
|
33
|
-
# Offense count:
|
79
|
+
# Offense count: 5
|
34
80
|
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
81
|
+
# SupportedStyles: compact, exploded
|
35
82
|
Style/RaiseArgs:
|
36
83
|
Enabled: false
|
37
84
|
|
@@ -39,78 +86,114 @@ Style/RaiseArgs:
|
|
39
86
|
# Cop supports --auto-correct.
|
40
87
|
# Configuration parameters: AllowMultipleReturnValues.
|
41
88
|
Style/RedundantReturn:
|
42
|
-
|
89
|
+
Exclude:
|
90
|
+
- 'lib/pg_search/scope_options.rb'
|
43
91
|
|
44
|
-
# Offense count:
|
92
|
+
# Offense count: 11
|
45
93
|
# Cop supports --auto-correct.
|
46
94
|
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
95
|
+
# SupportedStyles: only_raise, only_fail, semantic
|
47
96
|
Style/SignalException:
|
48
|
-
|
97
|
+
Exclude:
|
98
|
+
- 'lib/pg_search.rb'
|
99
|
+
- 'lib/pg_search/configuration.rb'
|
100
|
+
- 'lib/pg_search/features/tsearch.rb'
|
101
|
+
- 'lib/pg_search/multisearch/rebuilder.rb'
|
102
|
+
- 'lib/pg_search/normalizer.rb'
|
103
|
+
- 'lib/pg_search/scope_options.rb'
|
104
|
+
- 'lib/pg_search/tasks.rb'
|
105
|
+
- 'spec/lib/pg_search_spec.rb'
|
106
|
+
- 'spec/support/database.rb'
|
49
107
|
|
50
108
|
# Offense count: 2
|
51
109
|
# Cop supports --auto-correct.
|
52
110
|
Style/SpaceAfterComma:
|
53
|
-
|
111
|
+
Exclude:
|
112
|
+
- 'lib/pg_search/configuration.rb'
|
113
|
+
- 'lib/pg_search/tasks.rb'
|
54
114
|
|
55
115
|
# Offense count: 1
|
56
116
|
# Cop supports --auto-correct.
|
57
117
|
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
118
|
+
# SupportedStyles: space, no_space
|
58
119
|
Style/SpaceAroundEqualsInParameterDefault:
|
59
120
|
Enabled: false
|
60
121
|
|
61
|
-
# Offense count:
|
122
|
+
# Offense count: 4
|
62
123
|
# Cop supports --auto-correct.
|
63
124
|
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
125
|
+
# SupportedStyles: space, no_space
|
64
126
|
Style/SpaceBeforeBlockBraces:
|
65
127
|
Enabled: false
|
66
128
|
|
67
|
-
# Offense count:
|
129
|
+
# Offense count: 3
|
68
130
|
# Cop supports --auto-correct.
|
69
131
|
# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
|
132
|
+
# SupportedStyles: space, no_space
|
70
133
|
Style/SpaceInsideBlockBraces:
|
71
134
|
Enabled: false
|
72
135
|
|
73
136
|
# Offense count: 76
|
74
137
|
# Cop supports --auto-correct.
|
75
138
|
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SupportedStyles.
|
139
|
+
# SupportedStyles: space, no_space
|
76
140
|
Style/SpaceInsideHashLiteralBraces:
|
77
141
|
Enabled: false
|
78
142
|
|
79
143
|
# Offense count: 2
|
80
144
|
# Cop supports --auto-correct.
|
81
145
|
Style/SpaceInsideParens:
|
82
|
-
|
146
|
+
Exclude:
|
147
|
+
- 'spec/lib/pg_search/multisearch/rebuilder_spec.rb'
|
83
148
|
|
84
149
|
# Offense count: 3
|
85
150
|
# Cop supports --auto-correct.
|
151
|
+
# Configuration parameters: SupportedStyles.
|
152
|
+
# SupportedStyles: use_perl_names, use_english_names
|
86
153
|
Style/SpecialGlobalVars:
|
87
|
-
|
154
|
+
EnforcedStyle: use_english_names
|
88
155
|
|
89
156
|
# Offense count: 2
|
90
157
|
# Cop supports --auto-correct.
|
91
158
|
# Configuration parameters: IgnoredMethods.
|
159
|
+
# IgnoredMethods: respond_to
|
92
160
|
Style/SymbolProc:
|
93
|
-
|
161
|
+
Exclude:
|
162
|
+
- 'lib/pg_search/features/feature.rb'
|
163
|
+
- 'lib/pg_search/multisearch/rebuilder.rb'
|
94
164
|
|
95
|
-
# Offense count:
|
165
|
+
# Offense count: 5
|
96
166
|
# Cop supports --auto-correct.
|
97
167
|
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
168
|
+
# SupportedStyles: final_newline, final_blank_line
|
98
169
|
Style/TrailingBlankLines:
|
99
|
-
|
170
|
+
Exclude:
|
171
|
+
- 'Guardfile'
|
172
|
+
- 'lib/pg_search/extensions/arel.rb'
|
173
|
+
- 'lib/pg_search/migration/multisearch_generator.rb'
|
174
|
+
- 'lib/pg_search/multisearch.rb'
|
175
|
+
- 'spec/integration/single_table_inheritance_spec.rb'
|
100
176
|
|
101
177
|
# Offense count: 9
|
102
178
|
# Cop supports --auto-correct.
|
103
179
|
# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
|
104
|
-
|
105
|
-
|
180
|
+
# SupportedStyles: comma, consistent_comma, no_comma
|
181
|
+
Style/TrailingCommaInLiteral:
|
182
|
+
Exclude:
|
183
|
+
- 'spec/integration/pg_search_spec.rb'
|
184
|
+
- 'spec/lib/pg_search/features/dmetaphone_spec.rb'
|
185
|
+
- 'spec/lib/pg_search/features/tsearch_spec.rb'
|
106
186
|
|
107
|
-
# Offense count:
|
187
|
+
# Offense count: 2
|
108
188
|
# Cop supports --auto-correct.
|
109
189
|
Style/UnneededPercentQ:
|
110
|
-
|
190
|
+
Exclude:
|
191
|
+
- 'pg_search.gemspec'
|
111
192
|
|
112
193
|
# Offense count: 3
|
113
194
|
# Cop supports --auto-correct.
|
114
|
-
# Configuration parameters: WordRegex.
|
195
|
+
# Configuration parameters: SupportedStyles, WordRegex.
|
196
|
+
# SupportedStyles: percent, brackets
|
115
197
|
Style/WordArray:
|
116
|
-
|
198
|
+
EnforcedStyle: percent
|
199
|
+
MinSize: 3
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
# pg_search changelog
|
2
2
|
|
3
|
+
## 1.0.6
|
4
|
+
* Add support for highlighting the matching portion of a search result. (Jose Galisteo)
|
5
|
+
* Add `:update_if` option to control when PgSearch::Document gets updated. (Adam Becker)
|
6
|
+
* Add `:additional_attributes` option for adding additional attributes to PgSearch::Document
|
7
|
+
|
3
8
|
## 1.0.5
|
4
9
|
|
5
|
-
* Clean up rank table aliasing (Adam Milligan)
|
6
|
-
* Fix issue when using `#with_pg_search_rank` across a join (Reid Lynch)
|
10
|
+
* Clean up rank table aliasing. (Adam Milligan)
|
11
|
+
* Fix issue when using `#with_pg_search_rank` across a join. (Reid Lynch)
|
7
12
|
|
8
13
|
## 1.0.4
|
9
14
|
|
data/Gemfile
CHANGED
@@ -12,13 +12,6 @@ end
|
|
12
12
|
|
13
13
|
gem 'activerecord', ENV['ACTIVE_RECORD_VERSION'] if ENV['ACTIVE_RECORD_VERSION']
|
14
14
|
|
15
|
-
group :development do
|
16
|
-
gem 'guard-rspec', :require => false
|
17
|
-
gem 'rb-inotify', :require => false
|
18
|
-
gem 'rb-fsevent', :require => false
|
19
|
-
gem 'rb-fchange', :require => false
|
20
|
-
end
|
21
|
-
|
22
15
|
group :test do
|
23
16
|
gem "codeclimate-test-reporter", require: nil
|
24
17
|
end
|
data/README.md
CHANGED
@@ -7,6 +7,7 @@
|
|
7
7
|
[](https://gemnasium.com/Casecommons/pg_search)
|
8
8
|
[](http://inch-ci.org/github/Casecommons/pg_search)
|
9
9
|
[](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)
|
10
11
|
|
11
12
|
## DESCRIPTION
|
12
13
|
|
@@ -239,10 +240,12 @@ receive SQL requests when necessary.
|
|
239
240
|
```ruby
|
240
241
|
PgSearch.multisearch("Bertha").limit(10)
|
241
242
|
PgSearch.multisearch("Juggler").where(:searchable_type => "Occupation")
|
242
|
-
PgSearch.multisearch("Alamo").page(3).
|
243
|
+
PgSearch.multisearch("Alamo").page(3).per(30)
|
243
244
|
PgSearch.multisearch("Diagonal").find_each do |document|
|
244
245
|
puts document.searchable.updated_at
|
245
246
|
end
|
247
|
+
PgSearch.multisearch("Moro").reorder("").group(:searchable_type).count(:all)
|
248
|
+
PgSearch.multisearch("Square").includes(:searchable)
|
246
249
|
```
|
247
250
|
|
248
251
|
#### Configuring multi-search
|
@@ -733,6 +736,34 @@ one_close = Person.create!(:name => 'leigh heinz')
|
|
733
736
|
Person.search('ash hines') # => [exact, one_exact_one_close, one_exact]
|
734
737
|
```
|
735
738
|
|
739
|
+
##### :highlight (PostgreSQL 9.0 and newer only)
|
740
|
+
|
741
|
+
Adding .with_pg_search_highlight after the pg_search_scope you can access to
|
742
|
+
`pg_highlight` attribute for each object.
|
743
|
+
|
744
|
+
|
745
|
+
```ruby
|
746
|
+
class Person < ActiveRecord::Base
|
747
|
+
include PgSearch
|
748
|
+
pg_search_scope :search,
|
749
|
+
:against => :bio,
|
750
|
+
:using => {
|
751
|
+
:tsearch => {
|
752
|
+
:start_sel => '<b>',
|
753
|
+
:stop_sel => '</b>'
|
754
|
+
}
|
755
|
+
}
|
756
|
+
end
|
757
|
+
|
758
|
+
Person.create!(:bio => "Born in rural Alberta, where the buffalo roam.")
|
759
|
+
|
760
|
+
first_match = Person.search("Alberta").with_pg_search_highlight.first
|
761
|
+
first_match.pg_search_highlight # => "Born in rural <b>Alberta</b>, where the buffalo roam."
|
762
|
+
```
|
763
|
+
|
764
|
+
By default, it will add the delimiters `<b>` and `</b>` around the matched text. You can customize these delimiters and the number of fragments returned with the `:start_sel`, `stop_sel`, and `max_fragments` options.
|
765
|
+
|
766
|
+
|
736
767
|
#### :dmetaphone (Double Metaphone soundalike search)
|
737
768
|
|
738
769
|
[Double Metaphone](http://en.wikipedia.org/wiki/Double_Metaphone) is an
|
@@ -804,7 +835,9 @@ Website.kinda_spelled_like("Yahoo!") # => [yahooo, yohoo]
|
|
804
835
|
By default, trigram searches find records which have a similarity of at least 0.3
|
805
836
|
using pg_trgm's calculations. You may specify a custom threshold if you prefer.
|
806
837
|
Higher numbers match more strictly, and thus return fewer results. Lower numbers
|
807
|
-
match more permissively, letting in more results.
|
838
|
+
match more permissively, letting in more results. Please note that setting a trigram
|
839
|
+
threshold will force a table scan as the derived query uses the
|
840
|
+
`similarity()` function instead of the `%` operator.
|
808
841
|
|
809
842
|
```ruby
|
810
843
|
class Vegetable < ActiveRecord::Base
|
@@ -1068,7 +1101,7 @@ queried table.
|
|
1068
1101
|
```ruby
|
1069
1102
|
shirt_brands = ShirtBrand.search_by_name("Penguin")
|
1070
1103
|
.joins(:shirt_sizes)
|
1071
|
-
.
|
1104
|
+
.group("shirt_brands.id, #{PgSearch::Configuration.alias('shirt_brands')}.rank")
|
1072
1105
|
```
|
1073
1106
|
|
1074
1107
|
## ATTRIBUTIONS
|
data/lib/pg_search.rb
CHANGED
@@ -73,6 +73,9 @@ module PgSearch
|
|
73
73
|
when :pg_search_rank
|
74
74
|
raise PgSearchRankNotSelected.new unless respond_to?(:pg_search_rank)
|
75
75
|
read_attribute(:pg_search_rank).to_f
|
76
|
+
when :pg_search_highlight
|
77
|
+
raise PgSearchHighlightNotSelected.new unless respond_to?(:pg_search_highlight)
|
78
|
+
read_attribute(:pg_search_highlight)
|
76
79
|
else
|
77
80
|
super
|
78
81
|
end
|
@@ -82,6 +85,8 @@ module PgSearch
|
|
82
85
|
case symbol
|
83
86
|
when :pg_search_rank
|
84
87
|
attributes.key?(:pg_search_rank)
|
88
|
+
when :pg_search_highlight
|
89
|
+
attributes.key?(:pg_search_highlight)
|
85
90
|
else
|
86
91
|
super
|
87
92
|
end
|
@@ -90,9 +95,15 @@ module PgSearch
|
|
90
95
|
class NotSupportedForPostgresqlVersion < StandardError; end
|
91
96
|
|
92
97
|
class PgSearchRankNotSelected < StandardError
|
98
|
+
def message
|
99
|
+
"You must chain .with_pg_search_rank after the pg_search_scope to access the pg_search_rank attribute on returned records" # rubocop:disable Metrics/LineLength
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class PgSearchHighlightNotSelected < StandardError
|
93
104
|
# rubocop:disable Metrics/LineLength
|
94
105
|
def message
|
95
|
-
"You must chain .
|
106
|
+
"You must chain .with_pg_search_highlight after the pg_search_scope to access the pg_search_highlight attribute on returned records"
|
96
107
|
end
|
97
108
|
end
|
98
109
|
end
|
data/lib/pg_search/document.rb
CHANGED
@@ -7,9 +7,6 @@ module PgSearch
|
|
7
7
|
self.table_name = 'pg_search_documents'
|
8
8
|
belongs_to :searchable, :polymorphic => true
|
9
9
|
|
10
|
-
before_validation :update_content,
|
11
|
-
:unless => Proc.new { |doc| doc.searchable.nil? }
|
12
|
-
|
13
10
|
# The logger might not have loaded yet.
|
14
11
|
# https://github.com/Casecommons/pg_search/issues/26
|
15
12
|
def self.logger
|
@@ -25,13 +22,5 @@ module PgSearch
|
|
25
22
|
|
26
23
|
{:against => :content}.merge(options)
|
27
24
|
}
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def update_content
|
32
|
-
methods = Array(searchable.pg_search_multisearchable_options[:against])
|
33
|
-
searchable_text = methods.map { |symbol| searchable.send(symbol) }.join(" ")
|
34
|
-
self.content = searchable_text
|
35
|
-
end
|
36
25
|
end
|
37
26
|
end
|
@@ -3,19 +3,15 @@ require "active_support/core_ext/module/delegation"
|
|
3
3
|
|
4
4
|
module PgSearch
|
5
5
|
module Features
|
6
|
-
class TSearch < Feature
|
6
|
+
class TSearch < Feature # rubocop:disable Metrics/ClassLength
|
7
7
|
def self.valid_options
|
8
|
-
super + [:dictionary, :prefix, :negation, :any_word, :normalization, :tsvector_column]
|
8
|
+
super + [:dictionary, :prefix, :negation, :any_word, :normalization, :tsvector_column, :highlight]
|
9
9
|
end
|
10
10
|
|
11
11
|
def initialize(*args)
|
12
12
|
super
|
13
|
-
|
14
|
-
|
15
|
-
raise PgSearch::NotSupportedForPostgresqlVersion.new(<<-MESSAGE.strip_heredoc)
|
16
|
-
Sorry, {:using => {:tsearch => {:prefix => true}}} only works in PostgreSQL 8.4 and above.")
|
17
|
-
MESSAGE
|
18
|
-
end
|
13
|
+
checks_for_highlight
|
14
|
+
checks_for_prefix
|
19
15
|
end
|
20
16
|
|
21
17
|
def conditions
|
@@ -28,8 +24,47 @@ module PgSearch
|
|
28
24
|
arel_wrap(tsearch_rank)
|
29
25
|
end
|
30
26
|
|
27
|
+
def highlight
|
28
|
+
arel_wrap(ts_headline)
|
29
|
+
end
|
30
|
+
|
31
31
|
private
|
32
32
|
|
33
|
+
def checks_for_prefix
|
34
|
+
if options[:prefix] && model.connection.send(:postgresql_version) < 80400
|
35
|
+
raise PgSearch::NotSupportedForPostgresqlVersion.new(<<-MESSAGE.strip_heredoc)
|
36
|
+
Sorry, {:using => {:tsearch => {:prefix => true}}} only works in PostgreSQL 8.4 and above.")
|
37
|
+
MESSAGE
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def checks_for_highlight
|
42
|
+
if options[:highlight] && model.connection.send(:postgresql_version) < 90000
|
43
|
+
raise PgSearch::NotSupportedForPostgresqlVersion.new(<<-MESSAGE.strip_heredoc)
|
44
|
+
Sorry, {:using => {:tsearch => {:highlight => true}}} only works in PostgreSQL 9.0 and above.")
|
45
|
+
MESSAGE
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def ts_headline
|
50
|
+
"ts_headline((#{document}), (#{tsquery}), '#{ts_headline_options}')"
|
51
|
+
end
|
52
|
+
|
53
|
+
def ts_headline_options
|
54
|
+
return nil unless options[:highlight].is_a?(Hash)
|
55
|
+
|
56
|
+
headline_options = map_headline_options
|
57
|
+
headline_options.map{|key, value| "#{key} = #{value}" if value }.compact.join(", ")
|
58
|
+
end
|
59
|
+
|
60
|
+
def map_headline_options
|
61
|
+
{
|
62
|
+
"StartSel" => options[:highlight][:start_sel],
|
63
|
+
"StopSel" => options[:highlight][:stop_sel],
|
64
|
+
"MaxFragments" => options[:highlight][:max_fragments]
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
33
68
|
DISALLOWED_TSQUERY_CHARACTERS = /['?\\:]/
|
34
69
|
|
35
70
|
def tsquery_for_term(unsanitized_term) # rubocop:disable Metrics/AbcSize
|
@@ -14,6 +14,27 @@ module PgSearch
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
def searchable_text
|
18
|
+
Array(pg_search_multisearchable_options[:against])
|
19
|
+
.map { |symbol| send(symbol) }
|
20
|
+
.join(" ")
|
21
|
+
end
|
22
|
+
|
23
|
+
def pg_search_document_attrs
|
24
|
+
{
|
25
|
+
content: searchable_text
|
26
|
+
}.tap do |h|
|
27
|
+
if (attrs = pg_search_multisearchable_options[:additional_attributes])
|
28
|
+
h.merge! attrs.to_proc.call(self)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def should_update_pg_search_document?
|
34
|
+
conditions = Array(pg_search_multisearchable_options[:update_if])
|
35
|
+
conditions.all? { |condition| condition.to_proc.call(self) }
|
36
|
+
end
|
37
|
+
|
17
38
|
def update_pg_search_document # rubocop:disable Metrics/AbcSize
|
18
39
|
if_conditions = Array(pg_search_multisearchable_options[:if])
|
19
40
|
unless_conditions = Array(pg_search_multisearchable_options[:unless])
|
@@ -23,10 +44,18 @@ module PgSearch
|
|
23
44
|
unless_conditions.all? { |condition| !condition.to_proc.call(self) }
|
24
45
|
|
25
46
|
if should_have_document
|
26
|
-
|
47
|
+
create_or_update_pg_search_document
|
27
48
|
else
|
28
49
|
pg_search_document.destroy if pg_search_document
|
29
50
|
end
|
30
51
|
end
|
52
|
+
|
53
|
+
def create_or_update_pg_search_document
|
54
|
+
if !pg_search_document
|
55
|
+
create_pg_search_document(pg_search_document_attrs)
|
56
|
+
elsif should_update_pg_search_document?
|
57
|
+
pg_search_document.update_attributes(pg_search_document_attrs)
|
58
|
+
end
|
59
|
+
end
|
31
60
|
end
|
32
61
|
end
|
@@ -21,6 +21,7 @@ module PgSearch
|
|
21
21
|
.order("#{rank_table_alias}.rank DESC, #{order_within_rank}")
|
22
22
|
.extend(DisableEagerLoading)
|
23
23
|
.extend(WithPgSearchRank)
|
24
|
+
.extend(WithPgSearchHighlight[feature_for(:tsearch)])
|
24
25
|
end
|
25
26
|
|
26
27
|
# workaround for https://github.com/Casecommons/pg_search/issues/14
|
@@ -30,6 +31,32 @@ module PgSearch
|
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
34
|
+
module WithPgSearchHighlight
|
35
|
+
def self.[](tsearch)
|
36
|
+
Module.new do
|
37
|
+
include WithPgSearchHighlight
|
38
|
+
define_method(:tsearch) { tsearch }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def tsearch
|
43
|
+
raise TypeError.new("You need to instantiate this module with []")
|
44
|
+
end
|
45
|
+
|
46
|
+
def with_pg_search_highlight
|
47
|
+
scope = self
|
48
|
+
scope.select(pg_search_highlight_field)
|
49
|
+
end
|
50
|
+
|
51
|
+
def pg_search_highlight_field
|
52
|
+
"(#{highlight}) AS pg_search_highlight, #{table_name}.*"
|
53
|
+
end
|
54
|
+
|
55
|
+
def highlight
|
56
|
+
tsearch.highlight.to_sql
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
33
60
|
module WithPgSearchRank
|
34
61
|
def with_pg_search_rank
|
35
62
|
scope = self
|
@@ -103,7 +130,7 @@ module PgSearch
|
|
103
130
|
end
|
104
131
|
|
105
132
|
def subquery_join
|
106
|
-
if config.associations.any?
|
133
|
+
if config.associations.any?
|
107
134
|
config.associations.map do |association|
|
108
135
|
association.join(primary_key)
|
109
136
|
end.join(' ')
|
data/lib/pg_search/version.rb
CHANGED
data/pg_search.gemspec
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
2
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
3
3
|
require "pg_search/version"
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
@@ -25,7 +25,7 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.add_development_dependency 'pry'
|
26
26
|
s.add_development_dependency 'rspec', '>= 3.3'
|
27
27
|
s.add_development_dependency 'with_model', '>= 1.2'
|
28
|
-
s.add_development_dependency 'rubocop', '>= 0.
|
28
|
+
s.add_development_dependency 'rubocop', '>= 0.36'
|
29
29
|
|
30
30
|
s.required_ruby_version = ">= 1.9.2"
|
31
31
|
end
|
@@ -618,6 +618,33 @@ describe "an Active Record model which includes PgSearch" do
|
|
618
618
|
end
|
619
619
|
end
|
620
620
|
|
621
|
+
describe "highlighting" do
|
622
|
+
before do
|
623
|
+
["Strip Down", "Down", "Down and Out", "Won't Let You Down"].each do |name|
|
624
|
+
ModelWithPgSearch.create! :content => name
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
628
|
+
context "with highlight turned on" do
|
629
|
+
before do
|
630
|
+
ModelWithPgSearch.pg_search_scope :search_content,
|
631
|
+
:against => :content
|
632
|
+
end
|
633
|
+
|
634
|
+
it "adds a #pg_search_highlight method to each returned model record" do
|
635
|
+
result = ModelWithPgSearch.search_content("Strip Down").with_pg_search_highlight.first
|
636
|
+
|
637
|
+
expect(result.pg_search_highlight).to be_a(String)
|
638
|
+
end
|
639
|
+
|
640
|
+
it "returns excerpts of text where search match occurred" do
|
641
|
+
result = ModelWithPgSearch.search_content("Let").with_pg_search_highlight.first
|
642
|
+
|
643
|
+
expect(result.pg_search_highlight).to eq("Won't <b>Let</b> You Down")
|
644
|
+
end
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
621
648
|
describe "ranking" do
|
622
649
|
before do
|
623
650
|
["Strip Down", "Down", "Down and Out", "Won't Let You Down"].each do |name|
|
@@ -3,6 +3,19 @@ require "spec_helper"
|
|
3
3
|
describe PgSearch::Multisearch::Rebuilder do
|
4
4
|
with_table "pg_search_documents", {}, &DOCUMENTS_SCHEMA
|
5
5
|
|
6
|
+
describe 'when intialized with a model that is not multisearchable' do
|
7
|
+
with_model :not_multisearchable
|
8
|
+
|
9
|
+
it 'raises an exception' do
|
10
|
+
expect {
|
11
|
+
PgSearch::Multisearch::Rebuilder.new(NotMultisearchable)
|
12
|
+
}.to raise_exception(
|
13
|
+
PgSearch::Multisearch::ModelNotMultisearchable,
|
14
|
+
"NotMultisearchable is not multisearchable. See PgSearch::ClassMethods#multisearchable"
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
6
19
|
describe "#rebuild" do
|
7
20
|
context "when the model defines .rebuild_pg_search_documents" do
|
8
21
|
context "and multisearchable is not conditional" do
|
@@ -94,6 +94,148 @@ describe PgSearch::Multisearchable do
|
|
94
94
|
end
|
95
95
|
end
|
96
96
|
end
|
97
|
+
|
98
|
+
describe "populating the searchable text" do
|
99
|
+
let(:record) { ModelThatIsMultisearchable.new }
|
100
|
+
subject { record }
|
101
|
+
|
102
|
+
before do
|
103
|
+
ModelThatIsMultisearchable.multisearchable(multisearchable_options)
|
104
|
+
end
|
105
|
+
|
106
|
+
context "when searching against a single column" do
|
107
|
+
let(:multisearchable_options) { {:against => :some_content} }
|
108
|
+
let(:text) { "foo bar" }
|
109
|
+
before do
|
110
|
+
allow(record).to receive(:some_content) { text }
|
111
|
+
record.save
|
112
|
+
end
|
113
|
+
|
114
|
+
describe '#content' do
|
115
|
+
subject { super().pg_search_document.content }
|
116
|
+
it { is_expected.to eq(text) }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "when searching against multiple columns" do
|
121
|
+
let(:multisearchable_options) { {:against => [:attr1, :attr2]} }
|
122
|
+
before do
|
123
|
+
allow(record).to receive(:attr1) { '1' }
|
124
|
+
allow(record).to receive(:attr2) { '2' }
|
125
|
+
record.save
|
126
|
+
end
|
127
|
+
|
128
|
+
describe '#content' do
|
129
|
+
subject { super().pg_search_document.content }
|
130
|
+
it { is_expected.to eq("1 2") }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "populating the searchable attributes" do
|
136
|
+
let(:record) { ModelThatIsMultisearchable.new }
|
137
|
+
subject { record }
|
138
|
+
|
139
|
+
before do
|
140
|
+
ModelThatIsMultisearchable.multisearchable(multisearchable_options)
|
141
|
+
end
|
142
|
+
|
143
|
+
context "when searching against a single column" do
|
144
|
+
let(:multisearchable_options) { {:against => :some_content} }
|
145
|
+
let(:text) { "foo bar" }
|
146
|
+
before do
|
147
|
+
allow(record).to receive(:some_content) { text }
|
148
|
+
record.save
|
149
|
+
end
|
150
|
+
|
151
|
+
describe '#content' do
|
152
|
+
subject { super().pg_search_document.content }
|
153
|
+
it { is_expected.to eq(text) }
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context "when searching against multiple columns" do
|
158
|
+
let(:multisearchable_options) { {:against => [:attr1, :attr2]} }
|
159
|
+
before do
|
160
|
+
allow(record).to receive(:attr1) { '1' }
|
161
|
+
allow(record).to receive(:attr2) { '2' }
|
162
|
+
record.save
|
163
|
+
end
|
164
|
+
|
165
|
+
describe '#content' do
|
166
|
+
subject { super().pg_search_document.content }
|
167
|
+
it { is_expected.to eq("1 2") }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context "with additional_attributes" do
|
172
|
+
let(:multisearchable_options) do
|
173
|
+
{
|
174
|
+
:additional_attributes => lambda do |record|
|
175
|
+
{ foo: record.bar }
|
176
|
+
end
|
177
|
+
}
|
178
|
+
end
|
179
|
+
let(:text) { "foo bar" }
|
180
|
+
|
181
|
+
it "sets the attributes" do
|
182
|
+
allow(record).to receive(:bar).and_return(text)
|
183
|
+
expect(record)
|
184
|
+
.to receive(:create_pg_search_document)
|
185
|
+
.with(content: '', foo: text)
|
186
|
+
record.save
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context "when selectively updating" do
|
191
|
+
let(:multisearchable_options) do
|
192
|
+
{
|
193
|
+
:update_if => lambda do |record|
|
194
|
+
record.bar?
|
195
|
+
end
|
196
|
+
}
|
197
|
+
end
|
198
|
+
let(:text) { "foo bar" }
|
199
|
+
|
200
|
+
it "creates the document" do
|
201
|
+
allow(record).to receive(:bar?).and_return(false)
|
202
|
+
expect(record)
|
203
|
+
.to receive(:create_pg_search_document)
|
204
|
+
.with(content: '')
|
205
|
+
record.save
|
206
|
+
end
|
207
|
+
|
208
|
+
context "the document is created" do
|
209
|
+
before { record.save }
|
210
|
+
|
211
|
+
context "update_if returns false" do
|
212
|
+
before do
|
213
|
+
allow(record).to receive(:bar?).and_return(false)
|
214
|
+
end
|
215
|
+
|
216
|
+
it "does not update the document" do
|
217
|
+
expect_any_instance_of(PgSearch::Document)
|
218
|
+
.to_not receive(:update_attributes)
|
219
|
+
|
220
|
+
record.save
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context "update_if returns true" do
|
225
|
+
before do
|
226
|
+
allow(record).to receive(:bar?).and_return(true)
|
227
|
+
end
|
228
|
+
|
229
|
+
it "updates the document" do
|
230
|
+
expect_any_instance_of(PgSearch::Document)
|
231
|
+
.to receive(:update_attributes)
|
232
|
+
|
233
|
+
record.save
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
97
239
|
end
|
98
240
|
|
99
241
|
describe "a model which is conditionally multisearchable using a Proc" do
|
data/spec/support/database.rb
CHANGED
@@ -30,7 +30,7 @@ rescue *error_classes
|
|
30
30
|
puts " createdb pg_search_test"
|
31
31
|
puts "-" * 80
|
32
32
|
end
|
33
|
-
raise
|
33
|
+
raise $ERROR_INFO
|
34
34
|
end
|
35
35
|
|
36
36
|
if ENV["LOGGER"]
|
@@ -50,7 +50,7 @@ rescue
|
|
50
50
|
else
|
51
51
|
share_path = `pg_config --sharedir`.strip
|
52
52
|
ActiveRecord::Base.connection.execute File.read(File.join(share_path, 'contrib', "#{name}.sql"))
|
53
|
-
puts
|
53
|
+
puts $ERROR_INFO.message
|
54
54
|
end
|
55
55
|
rescue => exception
|
56
56
|
at_exit do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_search
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Grant Hutchins
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2016-05-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -115,14 +115,14 @@ dependencies:
|
|
115
115
|
requirements:
|
116
116
|
- - ">="
|
117
117
|
- !ruby/object:Gem::Version
|
118
|
-
version: 0.
|
118
|
+
version: '0.36'
|
119
119
|
type: :development
|
120
120
|
prerelease: false
|
121
121
|
version_requirements: !ruby/object:Gem::Requirement
|
122
122
|
requirements:
|
123
123
|
- - ">="
|
124
124
|
- !ruby/object:Gem::Version
|
125
|
-
version: 0.
|
125
|
+
version: '0.36'
|
126
126
|
description: PgSearch builds Active Record named scopes that take advantage of PostgreSQL's
|
127
127
|
full text search
|
128
128
|
email:
|
@@ -184,7 +184,6 @@ files:
|
|
184
184
|
- spec/lib/pg_search/configuration/association_spec.rb
|
185
185
|
- spec/lib/pg_search/configuration/column_spec.rb
|
186
186
|
- spec/lib/pg_search/configuration/foreign_column_spec.rb
|
187
|
-
- spec/lib/pg_search/document_spec.rb
|
188
187
|
- spec/lib/pg_search/features/dmetaphone_spec.rb
|
189
188
|
- spec/lib/pg_search/features/trigram_spec.rb
|
190
189
|
- spec/lib/pg_search/features/tsearch_spec.rb
|
@@ -222,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
222
221
|
version: '0'
|
223
222
|
requirements: []
|
224
223
|
rubyforge_project:
|
225
|
-
rubygems_version: 2.
|
224
|
+
rubygems_version: 2.5.1
|
226
225
|
signing_key:
|
227
226
|
specification_version: 4
|
228
227
|
summary: PgSearch builds Active Record named scopes that take advantage of PostgreSQL's
|
@@ -235,7 +234,6 @@ test_files:
|
|
235
234
|
- spec/lib/pg_search/configuration/association_spec.rb
|
236
235
|
- spec/lib/pg_search/configuration/column_spec.rb
|
237
236
|
- spec/lib/pg_search/configuration/foreign_column_spec.rb
|
238
|
-
- spec/lib/pg_search/document_spec.rb
|
239
237
|
- spec/lib/pg_search/features/dmetaphone_spec.rb
|
240
238
|
- spec/lib/pg_search/features/trigram_spec.rb
|
241
239
|
- spec/lib/pg_search/features/tsearch_spec.rb
|
@@ -1,56 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe PgSearch::Document do
|
4
|
-
with_table "pg_search_documents", {}, &DOCUMENTS_SCHEMA
|
5
|
-
|
6
|
-
with_model :Searchable do
|
7
|
-
table
|
8
|
-
model do
|
9
|
-
include PgSearch
|
10
|
-
multisearchable
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
it { is_expected.to be_an(ActiveRecord::Base) }
|
15
|
-
|
16
|
-
describe "callbacks" do
|
17
|
-
describe "before_validation" do
|
18
|
-
subject { document }
|
19
|
-
let(:document) { PgSearch::Document.new(:searchable => searchable) }
|
20
|
-
let(:searchable) { Searchable.new }
|
21
|
-
|
22
|
-
before do
|
23
|
-
# Redefine the options for multisearchable
|
24
|
-
Searchable.multisearchable(multisearchable_options)
|
25
|
-
end
|
26
|
-
|
27
|
-
context "when searching against a single column" do
|
28
|
-
let(:multisearchable_options) { {:against => :some_content} }
|
29
|
-
let(:text) { "foo bar" }
|
30
|
-
before do
|
31
|
-
allow(searchable).to receive(:some_content) { text }
|
32
|
-
document.valid?
|
33
|
-
end
|
34
|
-
|
35
|
-
describe '#content' do
|
36
|
-
subject { super().content }
|
37
|
-
it { is_expected.to eq(text) }
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
context "when searching against multiple columns" do
|
42
|
-
let(:multisearchable_options) { {:against => [:attr1, :attr2]} }
|
43
|
-
before do
|
44
|
-
allow(searchable).to receive(:attr1) { '1' }
|
45
|
-
allow(searchable).to receive(:attr2) { '2' }
|
46
|
-
document.valid?
|
47
|
-
end
|
48
|
-
|
49
|
-
describe '#content' do
|
50
|
-
subject { super().content }
|
51
|
-
it { is_expected.to eq("1 2") }
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|