pg_search 0.7.8 → 0.7.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8d79889dbc874d312da8be0d1770875df5048975
4
- data.tar.gz: d29022a5e3c1bcfd6668637e7f1c436d65140b63
3
+ metadata.gz: e2af7110ee0301accace6b1a5143605fdddaf59b
4
+ data.tar.gz: ae0a9e2e5da80e29afd0449057685d64f20cb08c
5
5
  SHA512:
6
- metadata.gz: 64adadba491dc22693215cb70ff4ee1d006336d4c1619f554755a50bd8a53f81601c7accd29d84fc1d787a291920f71f11bd450af6ded9ec341269325af3c248
7
- data.tar.gz: 51a38eecd6feef873c613d1bf57cf2de36beeeca59e899f1d2ddb1919fec5c8de24c337445f012af118289f57d1a28eca3244fda45a7979e51599edcb89e7527
6
+ metadata.gz: 6d5b4e6084872eac490fa2e7e923a3c9e53bd5acf77596a6a2a42547ecf90cee8f75a9c1d9a5819bcf4bc3546fc2dcc8f1ba46848dd5561c62172235d8ffff6b
7
+ data.tar.gz: baf8e2bcf8fd110c8db6f1bd68acdb62af2510ec5eab40bfd607494f83c202282238db599b95c30e44b64d7a13338b894b2dd074d7104779512fd0efac7b5ad0
data/.rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ Style/StringLiterals:
4
+ Enabled: false
5
+
6
+ # # Offense count: 2
7
+ # Lint/HandleExceptions:
8
+ # Enabled: false
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,273 @@
1
+ # This configuration was generated by `rubocop --auto-gen-config`
2
+ # on 2014-12-12 15:10:11 -0600 using RuboCop version 0.28.0.
3
+ # The point is for the user to remove these configuration records
4
+ # one by one as the offenses are removed from the code base.
5
+ # Note that changes in the inspected code, or installation of new
6
+ # versions of RuboCop, may require this file to be generated again.
7
+
8
+ # Offense count: 11
9
+ # Cop supports --auto-correct.
10
+ Lint/UnusedBlockArgument:
11
+ Enabled: false
12
+
13
+ # Offense count: 12
14
+ Lint/UselessAssignment:
15
+ Enabled: false
16
+
17
+ # Offense count: 6
18
+ Metrics/AbcSize:
19
+ Max: 27
20
+
21
+ # Offense count: 347
22
+ # Configuration parameters: AllowURI, URISchemes.
23
+ Metrics/LineLength:
24
+ Max: 304
25
+
26
+ # Offense count: 9
27
+ # Configuration parameters: CountComments.
28
+ Metrics/MethodLength:
29
+ Max: 21
30
+
31
+ # Offense count: 1
32
+ # Cop supports --auto-correct.
33
+ Style/Alias:
34
+ Enabled: false
35
+
36
+ # Offense count: 3
37
+ # Cop supports --auto-correct.
38
+ # Configuration parameters: EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle, SupportedLastArgumentHashStyles.
39
+ Style/AlignHash:
40
+ Enabled: false
41
+
42
+ # Offense count: 42
43
+ # Cop supports --auto-correct.
44
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
45
+ Style/AlignParameters:
46
+ Enabled: false
47
+
48
+ # Offense count: 14
49
+ # Cop supports --auto-correct.
50
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
51
+ Style/BarePercentLiterals:
52
+ Enabled: false
53
+
54
+ # Offense count: 1
55
+ # Cop supports --auto-correct.
56
+ Style/BlockEndNewline:
57
+ Enabled: false
58
+
59
+ # Offense count: 14
60
+ # Cop supports --auto-correct.
61
+ Style/Blocks:
62
+ Enabled: false
63
+
64
+ # Offense count: 1
65
+ # Cop supports --auto-correct.
66
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
67
+ Style/BracesAroundHashParameters:
68
+ Enabled: false
69
+
70
+ # Offense count: 27
71
+ Style/Documentation:
72
+ Enabled: false
73
+
74
+ # Offense count: 5
75
+ # Cop supports --auto-correct.
76
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
77
+ Style/DotPosition:
78
+ Enabled: false
79
+
80
+ # Offense count: 1
81
+ # Cop supports --auto-correct.
82
+ Style/ElseAlignment:
83
+ Enabled: false
84
+
85
+ # Offense count: 1
86
+ # Cop supports --auto-correct.
87
+ Style/EmptyLines:
88
+ Enabled: false
89
+
90
+ # Offense count: 6
91
+ # Cop supports --auto-correct.
92
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
93
+ Style/EmptyLinesAroundBlockBody:
94
+ Enabled: false
95
+
96
+ # Offense count: 2
97
+ # Cop supports --auto-correct.
98
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
99
+ Style/EmptyLinesAroundClassBody:
100
+ Enabled: false
101
+
102
+ # Offense count: 1
103
+ # Cop supports --auto-correct.
104
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
105
+ Style/EmptyLinesAroundModuleBody:
106
+ Enabled: false
107
+
108
+ # Offense count: 2
109
+ # Configuration parameters: MinBodyLength.
110
+ Style/GuardClause:
111
+ Enabled: false
112
+
113
+ # Offense count: 521
114
+ # Cop supports --auto-correct.
115
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
116
+ Style/HashSyntax:
117
+ Enabled: false
118
+
119
+ # Offense count: 1
120
+ # Configuration parameters: MaxLineLength.
121
+ Style/IfUnlessModifier:
122
+ Enabled: false
123
+
124
+ # Offense count: 23
125
+ # Cop supports --auto-correct.
126
+ Style/IndentArray:
127
+ Enabled: false
128
+
129
+ # Offense count: 1
130
+ # Cop supports --auto-correct.
131
+ Style/IndentationConsistency:
132
+ Enabled: false
133
+
134
+ # Offense count: 3
135
+ # Cop supports --auto-correct.
136
+ # Configuration parameters: Width.
137
+ Style/IndentationWidth:
138
+ Enabled: false
139
+
140
+ # Offense count: 10
141
+ Style/Lambda:
142
+ Enabled: false
143
+
144
+ # Offense count: 1
145
+ # Cop supports --auto-correct.
146
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
147
+ Style/MethodDefParentheses:
148
+ Enabled: false
149
+
150
+ # Offense count: 2
151
+ Style/MultilineBlockChain:
152
+ Enabled: false
153
+
154
+ # Offense count: 1
155
+ # Cop supports --auto-correct.
156
+ Style/MultilineBlockLayout:
157
+ Enabled: false
158
+
159
+ # Offense count: 16
160
+ # Cop supports --auto-correct.
161
+ Style/NumericLiterals:
162
+ MinDigits: 6
163
+
164
+ # Offense count: 12
165
+ # Cop supports --auto-correct.
166
+ # Configuration parameters: PreferredDelimiters.
167
+ Style/PercentLiteralDelimiters:
168
+ Enabled: false
169
+
170
+ # Offense count: 2
171
+ # Cop supports --auto-correct.
172
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
173
+ Style/PercentQLiterals:
174
+ Enabled: false
175
+
176
+ # Offense count: 1
177
+ # Cop supports --auto-correct.
178
+ Style/PerlBackrefs:
179
+ Enabled: false
180
+
181
+ # Offense count: 1
182
+ # Cop supports --auto-correct.
183
+ Style/Proc:
184
+ Enabled: false
185
+
186
+ # Offense count: 4
187
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
188
+ Style/RaiseArgs:
189
+ Enabled: false
190
+
191
+ # Offense count: 1
192
+ # Cop supports --auto-correct.
193
+ # Configuration parameters: AllowMultipleReturnValues.
194
+ Style/RedundantReturn:
195
+ Enabled: false
196
+
197
+ # Offense count: 2
198
+ Style/RegexpLiteral:
199
+ MaxSlashes: 0
200
+
201
+ # Offense count: 10
202
+ # Cop supports --auto-correct.
203
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
204
+ Style/SignalException:
205
+ Enabled: false
206
+
207
+ # Offense count: 2
208
+ # Cop supports --auto-correct.
209
+ Style/SpaceAfterComma:
210
+ Enabled: false
211
+
212
+ # Offense count: 1
213
+ # Cop supports --auto-correct.
214
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
215
+ Style/SpaceAroundEqualsInParameterDefault:
216
+ Enabled: false
217
+
218
+ # Offense count: 1
219
+ # Cop supports --auto-correct.
220
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
221
+ Style/SpaceBeforeBlockBraces:
222
+ Enabled: false
223
+
224
+ # Offense count: 2
225
+ # Cop supports --auto-correct.
226
+ # Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
227
+ Style/SpaceInsideBlockBraces:
228
+ Enabled: false
229
+
230
+ # Offense count: 76
231
+ # Cop supports --auto-correct.
232
+ # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SupportedStyles.
233
+ Style/SpaceInsideHashLiteralBraces:
234
+ Enabled: false
235
+
236
+ # Offense count: 2
237
+ # Cop supports --auto-correct.
238
+ Style/SpaceInsideParens:
239
+ Enabled: false
240
+
241
+ # Offense count: 3
242
+ # Cop supports --auto-correct.
243
+ Style/SpecialGlobalVars:
244
+ Enabled: false
245
+
246
+ # Offense count: 2
247
+ # Cop supports --auto-correct.
248
+ # Configuration parameters: IgnoredMethods.
249
+ Style/SymbolProc:
250
+ Enabled: false
251
+
252
+ # Offense count: 4
253
+ # Cop supports --auto-correct.
254
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
255
+ Style/TrailingBlankLines:
256
+ Enabled: false
257
+
258
+ # Offense count: 9
259
+ # Cop supports --auto-correct.
260
+ # Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
261
+ Style/TrailingComma:
262
+ Enabled: false
263
+
264
+ # Offense count: 6
265
+ # Cop supports --auto-correct.
266
+ Style/UnneededPercentQ:
267
+ Enabled: false
268
+
269
+ # Offense count: 3
270
+ # Cop supports --auto-correct.
271
+ # Configuration parameters: WordRegex.
272
+ Style/WordArray:
273
+ MinSize: 2
data/.travis.yml CHANGED
@@ -1,16 +1,19 @@
1
1
  language: ruby
2
+ sudo: false
2
3
 
3
4
  rvm:
4
- - 1.9.3
5
- - 2.0.0
6
- - 2.1.2
5
+ - "1.9"
6
+ - "2.0"
7
+ - "2.1"
8
+ - "2.2"
7
9
  - jruby-19mode
8
10
 
9
11
  env:
10
12
  - ACTIVE_RECORD_BRANCH="master"
13
+ - ACTIVE_RECORD_BRANCH="4-2-stable"
11
14
  - ACTIVE_RECORD_BRANCH="4-1-stable"
12
15
  - ACTIVE_RECORD_BRANCH="4-0-stable"
13
- - ACTIVE_RECORD_VERSION="~> 4.2.0.beta1"
16
+ - ACTIVE_RECORD_VERSION="~> 4.2.0"
14
17
  - ACTIVE_RECORD_VERSION="~> 4.1.0"
15
18
  - ACTIVE_RECORD_VERSION="~> 4.0.0"
16
19
  - ACTIVE_RECORD_VERSION="~> 3.2.0"
@@ -20,11 +23,20 @@ matrix:
20
23
  allow_failures:
21
24
  - rvm: jruby-19mode
22
25
  - env: ACTIVE_RECORD_BRANCH="master"
26
+ - env: ACTIVE_RECORD_BRANCH="4-2-stable"
23
27
  - env: ACTIVE_RECORD_BRANCH="4-1-stable"
24
28
  - env: ACTIVE_RECORD_BRANCH="4-0-stable"
25
- - env: ACTIVE_RECORD_VERSION="~> 4.2.0.beta1"
29
+ exclude:
30
+ - rvm: "1.9"
31
+ env: ACTIVE_RECORD_BRANCH="master"
32
+ - rvm: "2.0"
33
+ env: ACTIVE_RECORD_BRANCH="master"
34
+ - rvm: "2.1"
35
+ env: ACTIVE_RECORD_BRANCH="master"
36
+ - rvm: jruby-19mode
37
+ env: ACTIVE_RECORD_BRANCH="master"
26
38
 
27
39
  before_script:
28
40
  - "psql -c 'create database pg_search_test;' -U postgres >/dev/null"
29
41
 
30
- script: "bundle exec rspec spec"
42
+ script: "bin/rake"
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # pg_search changelog
2
2
 
3
+ ## 0.7.9
4
+
5
+ * Improve support for single table inheritance (STI) models (Ewan McDougall)
6
+
3
7
  ## 0.7.8
4
8
 
5
9
  * Stop inadvertently including binstubs for guard and rspec.
data/Gemfile CHANGED
@@ -5,11 +5,13 @@ gemspec
5
5
  gem 'pg', :platform => :ruby
6
6
  gem "activerecord-jdbcpostgresql-adapter", ">= 1.3.1", :platform => :jruby
7
7
 
8
- gem "activerecord", ENV["ACTIVE_RECORD_VERSION"] if ENV["ACTIVE_RECORD_VERSION"]
9
- gem "activerecord", :github => "rails", :branch => ENV["ACTIVE_RECORD_BRANCH"] if ENV["ACTIVE_RECORD_BRANCH"]
8
+ if ENV['ACTIVE_RECORD_BRANCH']
9
+ gem 'activerecord', :git => 'https://github.com/rails/rails.git', :branch => ENV['ACTIVE_RECORD_BRANCH']
10
+ gem 'arel', :git => 'https://github.com/rails/arel.git' if ENV['ACTIVE_RECORD_BRANCH'] == 'master'
11
+ end
10
12
 
11
- if ENV["TRAVIS"]
12
- gem 'coveralls', :require => false, :platform => :mri_20
13
+ if ENV['ACTIVE_RECORD_VERSION']
14
+ gem 'activerecord', ENV['ACTIVE_RECORD_VERSION']
13
15
  end
14
16
 
15
17
  group :development do
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # [pg_search](http://github.com/Casecommons/pg_search/)
2
2
 
3
3
  [![Build Status](https://secure.travis-ci.org/Casecommons/pg_search.svg?branch=master)](https://travis-ci.org/Casecommons/pg_search)
4
- [![Code Climate](https://img.shields.io/codeclimate/github/Casecommons/pg_search.svg)](https://codeclimate.com/github/Casecommons/pg_search)
5
- [![Coverage Status](https://img.shields.io/coveralls/Casecommons/pg_search/master.svg)](https://coveralls.io/r/Casecommons/pg_search)
6
- [![Gem Version](https://badge.fury.io/rb/pg_search.svg)](https://rubygems.org/gems/pg_search)
7
- [![Dependency Status](https://gemnasium.com/Casecommons/pg_search.svg)](https://gemnasium.com/Casecommons/pg_search)
4
+ [![Code Climate](https://img.shields.io/codeclimate/github/Casecommons/pg_search.svg?style=flat)](https://codeclimate.com/github/Casecommons/pg_search)
5
+ [![Gem Version](https://img.shields.io/gem/v/pg_search.svg?style=flat)](https://rubygems.org/gems/pg_search)
6
+ [![Dependency Status](https://img.shields.io/gemnasium/Casecommons/pg_search.svg?style=flat)](https://gemnasium.com/Casecommons/pg_search)
7
+ [![Inline docs](http://inch-ci.org/github/Casecommons/pg_search.svg?branch=master&style=flat)](http://inch-ci.org/github/Casecommons/pg_search)
8
8
 
9
9
  ## DESCRIPTION
10
10
 
@@ -562,6 +562,36 @@ robin = Superhero.create :name => 'Robin'
562
562
 
563
563
  Superhero.whose_name_starts_with("Bat") # => [batman, batgirl]
564
564
  ```
565
+ ##### :negation
566
+
567
+ PostgreSQL's full text search matches all search terms by default. If you want
568
+ to exclude certain words, you can set :negation to true. Then any term that begins with
569
+ an exclamation point `!` will be excluded from the results. Since this
570
+ is a :tsearch-specific option, you should pass it to :tsearch directly, as
571
+ shown in the following example.
572
+
573
+ Note that combining this with other search features can have unexpected results. For
574
+ example, :trigram searches don't have a concept of excluded terms, and thus if you
575
+ use both :tsearch and :trigram in tandem, you may still find results that contain the
576
+ term that you were trying to exclude.
577
+
578
+ ```ruby
579
+ class Animal < ActiveRecord::Base
580
+ include PgSearch
581
+ pg_search_scope :with_name_matching,
582
+ :against => :name,
583
+ :using => {
584
+ :tsearch => {:negation => true}
585
+ }
586
+ end
587
+
588
+ one_fish = Animal.create(:name => "one fish")
589
+ two_fish = Animal.create(:name => "two fish")
590
+ red_fish = Animal.create(:name => "red fish")
591
+ blue_fish = Animal.create(:name => "blue fish")
592
+
593
+ Animal.with_name_matching("fish !red !blue") # => [one_fish, two_fish]
594
+ ```
565
595
 
566
596
  ##### :dictionary
567
597
 
@@ -879,19 +909,20 @@ To use this functionality you'll need to do a few things:
879
909
  function uses.
880
910
  * Add the option to pg_search_scope, e.g:
881
911
 
882
- pg_search_scope :fast_content_search,
883
- :against => :content,
884
- :using => {
885
- dmetaphone: {
886
- tsvector_column: 'tsvector_content_dmetaphone'
887
- },
888
- tsearch: {
889
- dictionary: 'english',
890
- tsvector_column: 'tsvector_content_tsearch'
891
- }
892
- trigram: {} # trigram does not use tsvectors
893
- }
894
-
912
+ ```ruby
913
+ pg_search_scope :fast_content_search,
914
+ :against => :content,
915
+ :using => {
916
+ dmetaphone: {
917
+ tsvector_column: 'tsvector_content_dmetaphone'
918
+ },
919
+ tsearch: {
920
+ dictionary: 'english',
921
+ tsvector_column: 'tsvector_content_tsearch'
922
+ }
923
+ trigram: {} # trigram does not use tsvectors
924
+ }
925
+ ```
895
926
  * You cannot dump a `tsvector` column to `schema.rb`. Instead, you need to switch to using the native PostgreSQL SQL format schema dump.
896
927
  In your `config/application.rb` you should set
897
928
 
@@ -903,6 +934,36 @@ To use this functionality you'll need to do a few things:
903
934
  Please note that the :against column is only used when the tsvector_column is
904
935
  not present for the search type.
905
936
 
937
+ #### Combining multiple tsvectors
938
+
939
+ It's possible to search against more than one tsvector at a time. This could be useful if you want to maintain multiple search scopes but do not want to maintain separate tsvectors for each scope. For example:
940
+
941
+ ```ruby
942
+ pg_search_scope :search_title,
943
+ :against => :title,
944
+ :using => {
945
+ :tsearch => {
946
+ :tsvector_column => "title_tsvector"
947
+ }
948
+ }
949
+
950
+ pg_search_scope :search_body,
951
+ :against => :body,
952
+ :using => {
953
+ :tsearch => {
954
+ :tsvector_column => "body_tsvector"
955
+ }
956
+ }
957
+
958
+ pg_search_scope :search_title_and_body,
959
+ :against => [:title, :body],
960
+ :using => {
961
+ :tsearch => {
962
+ :tsvector_column => ["title_tsvector", "body_tsvector"]
963
+ }
964
+ }
965
+ ```
966
+
906
967
  ### Configuring ranking and ordering
907
968
 
908
969
  #### :ranked_by (Choosing a ranking algorithm)
data/Rakefile CHANGED
@@ -1,13 +1,10 @@
1
1
  require 'bundler'
2
2
  Bundler::GemHelper.install_tasks
3
3
 
4
- task :default => :spec
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
5
6
 
6
- def bundle_exec(command)
7
- sh %Q{bundle update && bundle exec #{command}}
8
- end
7
+ require "rubocop/rake_task"
8
+ RuboCop::RakeTask.new
9
9
 
10
- desc "Run all specs"
11
- task "spec" do
12
- bundle_exec("rspec spec")
13
- end
10
+ task :default => %w[ spec rubocop ]
data/bin/rake ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rake', 'rake')
@@ -18,10 +18,10 @@ module PgSearch
18
18
 
19
19
  pg_search_scope :search, lambda { |*args|
20
20
  options = if PgSearch.multisearch_options.respond_to?(:call)
21
- PgSearch.multisearch_options.call(*args)
22
- else
23
- {:query => args.first}.merge(PgSearch.multisearch_options)
24
- end
21
+ PgSearch.multisearch_options.call(*args)
22
+ else
23
+ {:query => args.first}.merge(PgSearch.multisearch_options)
24
+ end
25
25
 
26
26
  {:against => :content}.merge(options)
27
27
  }
@@ -29,13 +29,20 @@ module PgSearch
29
29
  DISALLOWED_TSQUERY_CHARACTERS = /['?\\:]/
30
30
 
31
31
  def tsquery_for_term(unsanitized_term)
32
+ if options[:negation] && unsanitized_term.start_with?("!")
33
+ unsanitized_term[0] = ''
34
+ negated = true
35
+ end
36
+
32
37
  sanitized_term = unsanitized_term.gsub(DISALLOWED_TSQUERY_CHARACTERS, " ")
33
38
 
34
39
  term_sql = Arel.sql(normalize(connection.quote(sanitized_term)))
35
40
 
36
41
  # After this, the SQL expression evaluates to a string containing the term surrounded by single-quotes.
37
- # If :prefix is true, then the term will also have :* appended to the end.
42
+ # If :prefix is true, then the term will have :* appended to the end.
43
+ # If :negated is true, then the term will have ! prepended to the front.
38
44
  terms = [
45
+ (Compatibility.build_quoted('!') if negated),
39
46
  Compatibility.build_quoted("' "),
40
47
  term_sql,
41
48
  Compatibility.build_quoted(" '"),
@@ -65,8 +72,13 @@ module PgSearch
65
72
  end
66
73
 
67
74
  if options[:tsvector_column]
68
- column_name = connection.quote_column_name(options[:tsvector_column])
69
- tsdocument_terms << "#{quoted_table_name}.#{column_name}"
75
+ tsvector_columns = Array.wrap(options[:tsvector_column])
76
+
77
+ tsdocument_terms << tsvector_columns.map do |tsvector_column|
78
+ column_name = connection.quote_column_name(tsvector_column)
79
+
80
+ "#{quoted_table_name}.#{column_name}"
81
+ end
70
82
  end
71
83
 
72
84
  tsdocument_terms.join(' || ')
@@ -6,7 +6,7 @@ module PgSearch
6
6
  class << self
7
7
  def rebuild(model, clean_up=true)
8
8
  model.transaction do
9
- PgSearch::Document.where(:searchable_type => model.name).delete_all if clean_up
9
+ PgSearch::Document.where(:searchable_type => model.base_class.name).delete_all if clean_up
10
10
  Rebuilder.new(model).rebuild
11
11
  end
12
12
  end
@@ -35,14 +35,14 @@ module PgSearch
35
35
  def rebuild_sql_template
36
36
  <<-SQL.strip_heredoc
37
37
  INSERT INTO :documents_table (searchable_type, searchable_id, content, created_at, updated_at)
38
- SELECT :model_name AS searchable_type,
38
+ SELECT :base_model_name AS searchable_type,
39
39
  :model_table.#{primary_key} AS searchable_id,
40
40
  (
41
41
  :content_expressions
42
42
  ) AS content,
43
43
  :current_time AS created_at,
44
44
  :current_time AS updated_at
45
- FROM :model_table
45
+ FROM :model_table :sti_clause
46
46
  SQL
47
47
  end
48
48
 
@@ -52,8 +52,20 @@ module PgSearch
52
52
  end
53
53
  end
54
54
 
55
+ def sti_clause
56
+ clause = ""
57
+ if model.column_names.include? 'type'
58
+ clause = "WHERE"
59
+ if model.base_class == model
60
+ clause = "#{clause} type IS NULL OR"
61
+ end
62
+ clause = "#{clause} type = #{model_name}"
63
+ end
64
+ clause
65
+ end
66
+
55
67
  def replacements
56
- %w[content_expressions model_name model_table documents_table current_time]
68
+ %w[content_expressions base_model_name model_name model_table documents_table current_time sti_clause]
57
69
  end
58
70
 
59
71
  def content_expressions
@@ -70,6 +82,10 @@ module PgSearch
70
82
  connection.quote(model.name)
71
83
  end
72
84
 
85
+ def base_model_name
86
+ connection.quote(model.base_class.name)
87
+ end
88
+
73
89
  def model_table
74
90
  model.quoted_table_name
75
91
  end
@@ -23,10 +23,7 @@ module PgSearch
23
23
  unless_conditions.all? { |condition| !condition.to_proc.call(self) }
24
24
 
25
25
  if should_have_document
26
- unless pg_search_document.present?
27
- build_pg_search_document.searchable_type = self.class.name
28
- end
29
- pg_search_document.save
26
+ pg_search_document ? pg_search_document.save : create_pg_search_document
30
27
  else
31
28
  pg_search_document.destroy if pg_search_document
32
29
  end
@@ -1,3 +1,3 @@
1
1
  module PgSearch
2
- VERSION = "0.7.8".freeze
2
+ VERSION = "0.7.9".freeze
3
3
  end
data/pg_search.gemspec CHANGED
@@ -17,14 +17,15 @@ Gem::Specification.new do |s|
17
17
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
18
  s.require_paths = ["lib"]
19
19
 
20
- s.add_dependency 'activerecord', '>=3.1'
21
- s.add_dependency 'activesupport', '>=3.1'
20
+ s.add_dependency 'activerecord', '>= 3.1'
21
+ s.add_dependency 'activesupport', '>= 3.1'
22
22
  s.add_dependency 'arel'
23
23
 
24
24
  s.add_development_dependency 'rake'
25
25
  s.add_development_dependency 'pry'
26
26
  s.add_development_dependency 'rspec', '~> 3.0'
27
- s.add_development_dependency 'with_model'
27
+ s.add_development_dependency 'with_model', '>= 1.2'
28
+ s.add_development_dependency 'rubocop'
28
29
 
29
30
  s.required_ruby_version = ">= 1.9.2"
30
31
  end
@@ -536,6 +536,58 @@ describe "an Active Record model which includes PgSearch" do
536
536
  expect(results.map(&:title)).to eq([])
537
537
  end
538
538
  end
539
+
540
+ context "with :negation" do
541
+ before do
542
+ ModelWithPgSearch.pg_search_scope :search_with_negation,
543
+ :against => :title,
544
+ :using => {
545
+ :tsearch => {:negation => true}
546
+ }
547
+ end
548
+
549
+ it "doesn't return results that contain terms prepended with '!'" do
550
+ included = [
551
+ ModelWithPgSearch.create!(:title => "one fish"),
552
+ ModelWithPgSearch.create!(:title => "two fish")
553
+ ]
554
+
555
+ excluded = [
556
+ ModelWithPgSearch.create!(:title => "red fish"),
557
+ ModelWithPgSearch.create!(:title => "blue fish")
558
+ ]
559
+
560
+ results = ModelWithPgSearch.search_with_negation("fish !red !blue")
561
+
562
+ expect(results).to include(*included)
563
+ expect(results).not_to include(*excluded)
564
+ end
565
+ end
566
+
567
+ context "without :negation" do
568
+ before do
569
+ ModelWithPgSearch.pg_search_scope :search_without_negation,
570
+ :against => :title,
571
+ :using => {
572
+ :tsearch => {}
573
+ }
574
+ end
575
+
576
+ it "return results that contain terms prepended with '!'" do
577
+ included = [
578
+ ModelWithPgSearch.create!(:title => "!bang")
579
+ ]
580
+
581
+ excluded = [
582
+ ModelWithPgSearch.create!(:title => "?question")
583
+ ]
584
+
585
+ results = ModelWithPgSearch.search_without_negation("!bang")
586
+
587
+ expect(results).to include(*included)
588
+ expect(results).not_to include(*excluded)
589
+ end
590
+ end
539
591
  end
540
592
 
541
593
  context "using dmetaphone" do
@@ -735,6 +787,30 @@ describe "an Active Record model which includes PgSearch" do
735
787
  end
736
788
  end
737
789
 
790
+ context 'using multiple tsvector columns' do
791
+ with_model :ModelWithTsvector do
792
+ model do
793
+ include PgSearch
794
+
795
+ pg_search_scope :search_by_multiple_tsvector_columns,
796
+ :against => ['content', 'message'],
797
+ :using => {
798
+ :tsearch => {
799
+ :tsvector_column => ['content_tsvector', 'message_tsvector'],
800
+ :dictionary => 'english'
801
+ }
802
+ }
803
+ end
804
+ end
805
+
806
+ it 'concats tsvector columns' do
807
+ expected = "#{ModelWithTsvector.quoted_table_name}.\"content_tsvector\" || "\
808
+ "#{ModelWithTsvector.quoted_table_name}.\"message_tsvector\""
809
+
810
+ expect(ModelWithTsvector.search_by_multiple_tsvector_columns("something").to_sql).to include(expected)
811
+ end
812
+ end
813
+
738
814
  context "using a tsvector column with" do
739
815
  with_model :ModelWithTsvector do
740
816
  table do |t|
@@ -937,29 +1013,29 @@ describe "an Active Record model which includes PgSearch" do
937
1013
  before do
938
1014
  SuperclassModel.pg_search_scope :search_content, :against => :content
939
1015
 
940
- class SubclassModel < SuperclassModel
1016
+ class SearchableSubclassModel < SuperclassModel
941
1017
  end
942
1018
 
943
- class AnotherSubclassModel < SuperclassModel
1019
+ class AnotherSearchableSubclassModel < SuperclassModel
944
1020
  end
945
1021
  end
946
1022
 
947
1023
  it "returns only results for that subclass" do
948
1024
  included = [
949
- SubclassModel.create!(:content => "foo bar")
1025
+ SearchableSubclassModel.create!(:content => "foo bar")
950
1026
  ]
951
1027
  excluded = [
952
- SubclassModel.create!(:content => "baz"),
1028
+ SearchableSubclassModel.create!(:content => "baz"),
953
1029
  SuperclassModel.create!(:content => "foo bar"),
954
1030
  SuperclassModel.create!(:content => "baz"),
955
- AnotherSubclassModel.create!(:content => "foo bar"),
956
- AnotherSubclassModel.create!(:content => "baz")
1031
+ AnotherSearchableSubclassModel.create!(:content => "foo bar"),
1032
+ AnotherSearchableSubclassModel.create!(:content => "baz")
957
1033
  ]
958
1034
 
959
1035
  expect(SuperclassModel.count).to eq(6)
960
- expect(SubclassModel.count).to eq(2)
1036
+ expect(SearchableSubclassModel.count).to eq(2)
961
1037
 
962
- results = SubclassModel.search_content("foo bar")
1038
+ results = SearchableSubclassModel.search_content("foo bar")
963
1039
 
964
1040
  expect(results).to include(*included)
965
1041
  expect(results).not_to include(*excluded)
@@ -1074,7 +1150,7 @@ describe "an Active Record model which includes PgSearch" do
1074
1150
  end
1075
1151
 
1076
1152
  context "on an STI subclass" do
1077
- with_model :ASuperclassModel do
1153
+ with_model :SuperclassModel do
1078
1154
  table do |t|
1079
1155
  t.text 'content'
1080
1156
  t.string 'type'
@@ -1082,14 +1158,13 @@ describe "an Active Record model which includes PgSearch" do
1082
1158
  end
1083
1159
 
1084
1160
  before do
1085
-
1086
- class SearchableSubclassModel < ASuperclassModel
1161
+ searchable_subclass_model = Class.new(SuperclassModel) do
1087
1162
  include PgSearch
1088
1163
  multisearchable :against => :content
1089
1164
  end
1090
-
1091
- class NonSearchableSubclassModel < ASuperclassModel
1092
- end
1165
+ stub_const("SearchableSubclassModel", searchable_subclass_model)
1166
+ stub_const("AnotherSearchableSubclassModel", searchable_subclass_model)
1167
+ stub_const("NonSearchableSubclassModel", Class.new(SuperclassModel))
1093
1168
  end
1094
1169
 
1095
1170
  it "returns only results for that subclass" do
@@ -1098,21 +1173,68 @@ describe "an Active Record model which includes PgSearch" do
1098
1173
  ]
1099
1174
  excluded = [
1100
1175
  SearchableSubclassModel.create!(:content => "baz"),
1101
- ASuperclassModel.create!(:content => "foo bar"),
1102
- ASuperclassModel.create!(:content => "baz"),
1176
+ SuperclassModel.create!(:content => "foo bar"),
1177
+ SuperclassModel.create!(:content => "baz"),
1103
1178
  NonSearchableSubclassModel.create!(:content => "foo bar"),
1104
1179
  NonSearchableSubclassModel.create!(:content => "baz")
1105
1180
  ]
1106
1181
 
1107
- expect(ASuperclassModel.count).to be 6
1182
+ expect(SuperclassModel.count).to be 6
1108
1183
  expect(SearchableSubclassModel.count).to be 2
1109
1184
 
1185
+ expect(PgSearch::Document.count).to be 2
1186
+
1110
1187
  results = PgSearch.multisearch("foo bar")
1111
1188
 
1112
- expect(results.map(&:searchable_id)).to include(*included.map(&:id))
1113
- expect(results.map(&:searchable_id)).not_to include(*excluded.map(&:id))
1114
- expect(results.map(&:searchable_type)).to include(*%w[SearchableSubclassModel])
1115
- expect(results.map(&:searchable_type)).not_to include(*%w[ASuperclassModel NonSearchableSubclassModel])
1189
+ expect(results.length).to be 1
1190
+ expect(results.first.searchable.class).to be SearchableSubclassModel
1191
+ expect(results.first.searchable).to eq included.first
1192
+ end
1193
+
1194
+ it "updates an existing STI model does not create a new pg_search document" do
1195
+ model = SearchableSubclassModel.create!(:content => "foo bar")
1196
+ expect(SearchableSubclassModel.count).to eq(1)
1197
+ # We fetch the model from the database again otherwise
1198
+ # the pg_search_document from the cache is used.
1199
+ model = SearchableSubclassModel.find(model.id)
1200
+ model.content = "foo"
1201
+ model.save!
1202
+ results = PgSearch.multisearch("foo")
1203
+ expect(results.size).to eq(SearchableSubclassModel.count)
1204
+ end
1205
+
1206
+ it "reindexing works" do
1207
+ NonSearchableSubclassModel.create!(:content => "foo bar")
1208
+ NonSearchableSubclassModel.create!(:content => "baz")
1209
+ expected = SearchableSubclassModel.create!(:content => "baz")
1210
+ SuperclassModel.create!(:content => "foo bar")
1211
+ SuperclassModel.create!(:content => "baz")
1212
+ SuperclassModel.create!(:content => "baz2")
1213
+
1214
+ expect(SuperclassModel.count).to be 6
1215
+ expect(NonSearchableSubclassModel.count).to be 2
1216
+ expect(SearchableSubclassModel.count).to be 1
1217
+
1218
+ expect(PgSearch::Document.count).to be 1
1219
+
1220
+ PgSearch::Multisearch.rebuild(SearchableSubclassModel)
1221
+
1222
+ expect(PgSearch::Document.count).to be 1
1223
+ expect(PgSearch::Document.first.searchable.class).to be SearchableSubclassModel
1224
+ expect(PgSearch::Document.first.searchable).to eq expected
1225
+ end
1226
+
1227
+ it "reindexing searchable STI doesn't clobber other related STI models" do
1228
+ searchable_s = SearchableSubclassModel.create!(:content => "baz")
1229
+ searchable_a = AnotherSearchableSubclassModel.create!(:content => "baz")
1230
+
1231
+ expect(PgSearch::Document.count).to be 2
1232
+ PgSearch::Multisearch.rebuild(SearchableSubclassModel)
1233
+ expect(PgSearch::Document.count).to be 2
1234
+
1235
+ classes = PgSearch::Document.all.collect {|d| d.searchable.class }
1236
+ expect(classes).to include SearchableSubclassModel
1237
+ expect(classes).to include AnotherSearchableSubclassModel
1116
1238
  end
1117
1239
  end
1118
1240
  end
@@ -1137,7 +1259,7 @@ describe "an Active Record model which includes PgSearch" do
1137
1259
  @multisearch_enabled_inside = PgSearch.multisearch_enabled?
1138
1260
  raise
1139
1261
  end
1140
- rescue
1262
+ rescue # rubocop:disable Lint/HandleExceptions
1141
1263
  end
1142
1264
 
1143
1265
  @multisearch_enabled_after = PgSearch.multisearch_enabled?
@@ -49,5 +49,77 @@ describe PgSearch::Features::TSearch do
49
49
  %Q{((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))) @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))}
50
50
  )
51
51
  end
52
+
53
+ context "when options[:negation] is true" do
54
+ it "returns a negated expression when a query is prepended with !" do
55
+ query = "!query"
56
+ columns = [
57
+ PgSearch::Configuration::Column.new(:name, nil, Model),
58
+ PgSearch::Configuration::Column.new(:content, nil, Model),
59
+ ]
60
+ options = {:negation => true}
61
+ config = double(:config, :ignore => [])
62
+ normalizer = PgSearch::Normalizer.new(config)
63
+
64
+ feature = described_class.new(query, options, columns, Model, normalizer)
65
+ expect(feature.conditions.to_sql).to eq(
66
+ %Q{((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))) @@ (to_tsquery('simple', '!' || ''' ' || 'query' || ' ''')))}
67
+ )
68
+ end
69
+ end
70
+
71
+ context "when options[:negation] is false" do
72
+ it "does not return a negated expression when a query is prepended with !" do
73
+ query = "!query"
74
+ columns = [
75
+ PgSearch::Configuration::Column.new(:name, nil, Model),
76
+ PgSearch::Configuration::Column.new(:content, nil, Model),
77
+ ]
78
+ options = {:negation => false}
79
+ config = double(:config, :ignore => [])
80
+ normalizer = PgSearch::Normalizer.new(config)
81
+
82
+ feature = described_class.new(query, options, columns, Model, normalizer)
83
+ expect(feature.conditions.to_sql).to eq(
84
+ %Q{((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))) @@ (to_tsquery('simple', ''' ' || '!query' || ' ''')))}
85
+ )
86
+ end
87
+ end
88
+
89
+ context "when options[:tsvector_column] is a string" do
90
+ it 'uses the tsvector column' do
91
+ query = "query"
92
+ columns = [
93
+ PgSearch::Configuration::Column.new(:name, nil, Model),
94
+ PgSearch::Configuration::Column.new(:content, nil, Model),
95
+ ]
96
+ options = { tsvector_column: "my_tsvector" }
97
+ config = double(:config, :ignore => [])
98
+ normalizer = PgSearch::Normalizer.new(config)
99
+
100
+ feature = described_class.new(query, options, columns, Model, normalizer)
101
+ expect(feature.conditions.to_sql).to eq(
102
+ %Q{((#{Model.quoted_table_name}.\"my_tsvector\") @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))}
103
+ )
104
+ end
105
+ end
106
+
107
+ context "when options[:tsvector_column] is an array of strings" do
108
+ it 'uses the tsvector column' do
109
+ query = "query"
110
+ columns = [
111
+ PgSearch::Configuration::Column.new(:name, nil, Model),
112
+ PgSearch::Configuration::Column.new(:content, nil, Model),
113
+ ]
114
+ options = { tsvector_column: ["tsvector1", "tsvector2"] }
115
+ config = double(:config, :ignore => [])
116
+ normalizer = PgSearch::Normalizer.new(config)
117
+
118
+ feature = described_class.new(query, options, columns, Model, normalizer)
119
+ expect(feature.conditions.to_sql).to eq(
120
+ %Q{((#{Model.quoted_table_name}.\"tsvector1\" || #{Model.quoted_table_name}.\"tsvector2\") @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))}
121
+ )
122
+ end
123
+ end
52
124
  end
53
125
  end
@@ -121,7 +121,7 @@ describe PgSearch::Multisearch::Rebuilder do
121
121
  ActiveSupport::Notifications.unsubscribe(notifier)
122
122
 
123
123
  expect(executed_sql.length).to eq(1)
124
- expect(executed_sql.first).to eq(expected_sql)
124
+ expect(executed_sql.first.strip).to eq(expected_sql.strip)
125
125
  end
126
126
 
127
127
  context "for a model with a non-standard primary key" do
@@ -177,7 +177,7 @@ describe PgSearch::Multisearch::Rebuilder do
177
177
  ActiveSupport::Notifications.unsubscribe(notifier)
178
178
 
179
179
  expect(executed_sql.length).to eq(1)
180
- expect(executed_sql.first).to eq(expected_sql)
180
+ expect(executed_sql.first.strip).to eq(expected_sql.strip)
181
181
  end
182
182
  end
183
183
  end
@@ -129,11 +129,11 @@ describe PgSearch::Multisearch do
129
129
  SQL
130
130
 
131
131
  statements = []
132
- allow(connection).to receive(:execute) { |sql| statements << sql }
132
+ allow(connection).to receive(:execute) { |sql| statements << sql.strip }
133
133
 
134
134
  PgSearch::Multisearch.rebuild(model)
135
135
 
136
- expect(statements).to include(expected_sql)
136
+ expect(statements).to include(expected_sql.strip)
137
137
  end
138
138
  end
139
139
 
@@ -156,11 +156,11 @@ describe PgSearch::Multisearch do
156
156
  SQL
157
157
 
158
158
  statements = []
159
- allow(connection).to receive(:execute) { |sql| statements << sql }
159
+ allow(connection).to receive(:execute) { |sql| statements << sql.strip }
160
160
 
161
161
  PgSearch::Multisearch.rebuild(model)
162
162
 
163
- expect(statements).to include(expected_sql)
163
+ expect(statements).to include(expected_sql.strip)
164
164
  end
165
165
  end
166
166
  end
data/spec/spec_helper.rb CHANGED
@@ -11,7 +11,6 @@ RSpec.configure do |config|
11
11
  end
12
12
  end
13
13
 
14
- require 'support/coveralls'
15
14
  require 'support/database'
16
15
  require 'support/with_model'
17
16
 
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: 0.7.8
4
+ version: 0.7.9
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: 2014-09-06 00:00:00.000000000 Z
12
+ date: 2015-02-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -97,6 +97,20 @@ dependencies:
97
97
  version: '3.0'
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: with_model
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '1.2'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '1.2'
112
+ - !ruby/object:Gem::Dependency
113
+ name: rubocop
100
114
  requirement: !ruby/object:Gem::Requirement
101
115
  requirements:
102
116
  - - ">="
@@ -121,6 +135,8 @@ files:
121
135
  - ".autotest"
122
136
  - ".gitignore"
123
137
  - ".rspec"
138
+ - ".rubocop.yml"
139
+ - ".rubocop_todo.yml"
124
140
  - ".travis.yml"
125
141
  - CHANGELOG.md
126
142
  - CONTRIBUTING.md
@@ -130,6 +146,7 @@ files:
130
146
  - README.md
131
147
  - Rakefile
132
148
  - bin/guard
149
+ - bin/rake
133
150
  - bin/rspec
134
151
  - lib/pg_search.rb
135
152
  - lib/pg_search/compatibility.rb
@@ -175,7 +192,6 @@ files:
175
192
  - spec/lib/pg_search/multisearchable_spec.rb
176
193
  - spec/lib/pg_search/normalizer_spec.rb
177
194
  - spec/spec_helper.rb
178
- - spec/support/coveralls.rb
179
195
  - spec/support/database.rb
180
196
  - spec/support/with_model.rb
181
197
  - sql/array_agg.sql
@@ -204,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
204
220
  version: '0'
205
221
  requirements: []
206
222
  rubyforge_project:
207
- rubygems_version: 2.2.2
223
+ rubygems_version: 2.4.5
208
224
  signing_key:
209
225
  specification_version: 4
210
226
  summary: PgSearch builds Active Record named scopes that take advantage of PostgreSQL's
@@ -225,6 +241,5 @@ test_files:
225
241
  - spec/lib/pg_search/multisearchable_spec.rb
226
242
  - spec/lib/pg_search/normalizer_spec.rb
227
243
  - spec/spec_helper.rb
228
- - spec/support/coveralls.rb
229
244
  - spec/support/database.rb
230
245
  - spec/support/with_model.rb
@@ -1,7 +0,0 @@
1
- if ENV["TRAVIS"]
2
- begin
3
- require 'coveralls'
4
- Coveralls.wear!
5
- rescue LoadError
6
- end
7
- end