pg_search 2.3.0 → 2.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +1 -0
  3. data/.github/dependabot.yml +11 -0
  4. data/.jrubyrc +1 -0
  5. data/.rubocop.yml +85 -7
  6. data/.travis.yml +14 -22
  7. data/CHANGELOG.md +41 -16
  8. data/CODE_OF_CONDUCT.md +76 -0
  9. data/Gemfile +1 -1
  10. data/LICENSE +1 -1
  11. data/README.md +60 -18
  12. data/lib/pg_search.rb +4 -6
  13. data/lib/pg_search/document.rb +1 -1
  14. data/lib/pg_search/features/dmetaphone.rb +4 -6
  15. data/lib/pg_search/features/tsearch.rb +13 -12
  16. data/lib/pg_search/migration/templates/add_pg_search_dmetaphone_support_functions.rb.erb +6 -6
  17. data/lib/pg_search/migration/templates/create_pg_search_documents.rb.erb +2 -2
  18. data/lib/pg_search/multisearch.rb +10 -1
  19. data/lib/pg_search/multisearch/rebuilder.rb +7 -3
  20. data/lib/pg_search/scope_options.rb +3 -3
  21. data/lib/pg_search/tasks.rb +2 -1
  22. data/lib/pg_search/version.rb +1 -1
  23. data/pg_search.gemspec +11 -7
  24. data/spec/.rubocop.yml +2 -2
  25. data/spec/integration/.rubocop.yml +11 -0
  26. data/spec/integration/associations_spec.rb +17 -56
  27. data/spec/integration/deprecation_spec.rb +1 -1
  28. data/spec/integration/pg_search_spec.rb +62 -51
  29. data/spec/lib/pg_search/configuration/association_spec.rb +8 -6
  30. data/spec/lib/pg_search/features/dmetaphone_spec.rb +2 -2
  31. data/spec/lib/pg_search/features/trigram_spec.rb +15 -11
  32. data/spec/lib/pg_search/features/tsearch_spec.rb +16 -10
  33. data/spec/lib/pg_search/multisearch/rebuilder_spec.rb +116 -71
  34. data/spec/lib/pg_search/multisearch_spec.rb +48 -29
  35. data/spec/lib/pg_search/multisearchable_spec.rb +150 -97
  36. data/spec/lib/pg_search/normalizer_spec.rb +12 -10
  37. data/spec/lib/pg_search_spec.rb +66 -55
  38. data/spec/spec_helper.rb +13 -4
  39. data/spec/support/database.rb +1 -1
  40. metadata +78 -17
@@ -15,8 +15,10 @@ require "pg_search/scope_options"
15
15
  require "pg_search/version"
16
16
 
17
17
  module PgSearch
18
+ autoload :Document, "pg_search/document"
19
+
18
20
  def self.included(base)
19
- ActiveSupport::Deprecation.warn <<-MESSAGE.strip_heredoc
21
+ ActiveSupport::Deprecation.warn <<~MESSAGE
20
22
  Directly including `PgSearch` into an Active Record model is deprecated and will be removed in pg_search 3.0.
21
23
 
22
24
  Please replace `include PgSearch` with `include PgSearch::Model`.
@@ -67,8 +69,4 @@ module PgSearch
67
69
  end
68
70
  end
69
71
 
70
- ActiveSupport.on_load(:active_record) do
71
- require "pg_search/document"
72
- end
73
-
74
- require "pg_search/railtie" if defined?(Rails)
72
+ require "pg_search/railtie" if defined?(Rails::Railtie)
@@ -12,7 +12,7 @@ module PgSearch
12
12
  # The logger might not have loaded yet.
13
13
  # https://github.com/Casecommons/pg_search/issues/26
14
14
  def self.logger
15
- super || Logger.new(STDERR)
15
+ super || Logger.new($stderr)
16
16
  end
17
17
 
18
18
  pg_search_scope :search, lambda { |*args|
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/module/delegation"
4
+
3
5
  module PgSearch
4
6
  module Features
5
7
  class DMetaphone
@@ -9,13 +11,9 @@ module PgSearch
9
11
  @tsearch = TSearch.new(query, options, columns, model, dmetaphone_normalizer)
10
12
  end
11
13
 
12
- def conditions
13
- tsearch.conditions
14
- end
14
+ delegate :conditions, to: :tsearch
15
15
 
16
- def rank
17
- tsearch.rank
18
- end
16
+ delegate :rank, to: :tsearch
19
17
 
20
18
  private
21
19
 
@@ -59,7 +59,7 @@ module PgSearch
59
59
  end
60
60
  end
61
61
 
62
- def deprecated_headline_options
62
+ def deprecated_headline_options # rubocop:disable Metrics/MethodLength
63
63
  indifferent_options = options.with_indifferent_access
64
64
 
65
65
  %w[
@@ -97,7 +97,7 @@ module PgSearch
97
97
 
98
98
  DISALLOWED_TSQUERY_CHARACTERS = /['?\\:‘’]/.freeze
99
99
 
100
- def tsquery_for_term(unsanitized_term) # rubocop:disable Metrics/AbcSize
100
+ def tsquery_for_term(unsanitized_term)
101
101
  if options[:negation] && unsanitized_term.start_with?("!")
102
102
  unsanitized_term[0] = ''
103
103
  negated = true
@@ -107,25 +107,26 @@ module PgSearch
107
107
 
108
108
  term_sql = Arel.sql(normalize(connection.quote(sanitized_term)))
109
109
 
110
- # After this, the SQL expression evaluates to a string containing the term surrounded by single-quotes.
111
- # If :prefix is true, then the term will have :* appended to the end.
112
- # If :negated is true, then the term will have ! prepended to the front.
110
+ tsquery = tsquery_expression(term_sql, negated: negated, prefix: options[:prefix])
111
+
112
+ Arel::Nodes::NamedFunction.new("to_tsquery", [dictionary, tsquery]).to_sql
113
+ end
114
+
115
+ # After this, the SQL expression evaluates to a string containing the term surrounded by single-quotes.
116
+ # If :prefix is true, then the term will have :* appended to the end.
117
+ # If :negated is true, then the term will have ! prepended to the front.
118
+ def tsquery_expression(term_sql, negated:, prefix:)
113
119
  terms = [
114
120
  (Arel::Nodes.build_quoted('!') if negated),
115
121
  Arel::Nodes.build_quoted("' "),
116
122
  term_sql,
117
123
  Arel::Nodes.build_quoted(" '"),
118
- (Arel::Nodes.build_quoted(":*") if options[:prefix])
124
+ (Arel::Nodes.build_quoted(":*") if prefix)
119
125
  ].compact
120
126
 
121
- tsquery_sql = terms.inject do |memo, term|
127
+ terms.inject do |memo, term|
122
128
  Arel::Nodes::InfixOperation.new("||", memo, Arel::Nodes.build_quoted(term))
123
129
  end
124
-
125
- Arel::Nodes::NamedFunction.new(
126
- "to_tsquery",
127
- [dictionary, tsquery_sql]
128
- ).to_sql
129
130
  end
130
131
 
131
132
  def tsquery
@@ -1,16 +1,16 @@
1
1
  class AddPgSearchDmetaphoneSupportFunctions < ActiveRecord::Migration<%= migration_version %>
2
- def self.up
2
+ def up
3
3
  say_with_time("Adding support functions for pg_search :dmetaphone") do
4
- execute <<-'SQL'
5
- <%= read_sql_file "dmetaphone" %>
4
+ execute <<~'SQL'.squish
5
+ <%= indent(read_sql_file("dmetaphone"), 8) %>
6
6
  SQL
7
7
  end
8
8
  end
9
9
 
10
- def self.down
10
+ def down
11
11
  say_with_time("Dropping support functions for pg_search :dmetaphone") do
12
- execute <<-'SQL'
13
- <%= read_sql_file "uninstall_dmetaphone" %>
12
+ execute <<~'SQL'.squish
13
+ <%= indent(read_sql_file("uninstall_dmetaphone"), 8) %>
14
14
  SQL
15
15
  end
16
16
  end
@@ -1,5 +1,5 @@
1
1
  class CreatePgSearchDocuments < ActiveRecord::Migration<%= migration_version %>
2
- def self.up
2
+ def up
3
3
  say_with_time("Creating table for pg_search multisearch") do
4
4
  create_table :pg_search_documents do |t|
5
5
  t.text :content
@@ -9,7 +9,7 @@ class CreatePgSearchDocuments < ActiveRecord::Migration<%= migration_version %>
9
9
  end
10
10
  end
11
11
 
12
- def self.down
12
+ def down
13
13
  say_with_time("Dropping table for pg_search multisearch") do
14
14
  drop_table :pg_search_documents
15
15
  end
@@ -5,7 +5,15 @@ require "pg_search/multisearch/rebuilder"
5
5
  module PgSearch
6
6
  module Multisearch
7
7
  class << self
8
- def rebuild(model, clean_up = true)
8
+ def rebuild(model, deprecated_clean_up = nil, clean_up: true)
9
+ unless deprecated_clean_up.nil?
10
+ ActiveSupport::Deprecation.warn(
11
+ "pg_search 3.0 will no longer accept a boolean second argument to PgSearchMultisearch.rebuild, " \
12
+ "use keyword argument `clean_up:` instead."
13
+ )
14
+ clean_up = deprecated_clean_up
15
+ end
16
+
9
17
  model.transaction do
10
18
  PgSearch::Document.where(searchable_type: model.base_class.name).delete_all if clean_up
11
19
  Rebuilder.new(model).rebuild
@@ -15,6 +23,7 @@ module PgSearch
15
23
 
16
24
  class ModelNotMultisearchable < StandardError
17
25
  def initialize(model_class)
26
+ super
18
27
  @model_class = model_class
19
28
  end
20
29
 
@@ -13,7 +13,7 @@ module PgSearch
13
13
  def rebuild
14
14
  if model.respond_to?(:rebuild_pg_search_documents)
15
15
  model.rebuild_pg_search_documents
16
- elsif conditional? || dynamic?
16
+ elsif conditional? || dynamic? || additional_attributes?
17
17
  model.find_each(&:update_pg_search_document)
18
18
  else
19
19
  model.connection.execute(rebuild_sql)
@@ -30,7 +30,11 @@ module PgSearch
30
30
 
31
31
  def dynamic?
32
32
  column_names = model.columns.map(&:name)
33
- columns.any? { |column| !column_names.include?(column.to_s) }
33
+ columns.any? { |column| column_names.exclude?(column.to_s) }
34
+ end
35
+
36
+ def additional_attributes?
37
+ model.pg_search_multisearchable_options.key?(:additional_attributes)
34
38
  end
35
39
 
36
40
  def connection
@@ -42,7 +46,7 @@ module PgSearch
42
46
  end
43
47
 
44
48
  def rebuild_sql_template
45
- <<-SQL.strip_heredoc
49
+ <<~SQL.squish
46
50
  INSERT INTO :documents_table (searchable_type, searchable_id, content, created_at, updated_at)
47
51
  SELECT :base_model_name AS searchable_type,
48
52
  :model_table.#{primary_key} AS searchable_id,
@@ -14,7 +14,7 @@ module PgSearch
14
14
 
15
15
  def apply(scope)
16
16
  scope = include_table_aliasing_for_rank(scope)
17
- rank_table_alias = scope.pg_search_rank_table_alias(:include_counter)
17
+ rank_table_alias = scope.pg_search_rank_table_alias(include_counter: true)
18
18
 
19
19
  scope
20
20
  .joins(rank_join(rank_table_alias))
@@ -58,7 +58,7 @@ module PgSearch
58
58
  end
59
59
 
60
60
  module PgSearchRankTableAliasing
61
- def pg_search_rank_table_alias(include_counter = false)
61
+ def pg_search_rank_table_alias(include_counter: false)
62
62
  components = [arel_table.name]
63
63
  if include_counter
64
64
  count = increment_counter
@@ -152,7 +152,7 @@ module PgSearch
152
152
  return scope if scope.included_modules.include?(PgSearchRankTableAliasing)
153
153
 
154
154
  scope.all.spawn.tap do |new_scope|
155
- new_scope.class_eval { include PgSearchRankTableAliasing }
155
+ new_scope.instance_eval { extend PgSearchRankTableAliasing }
156
156
  end
157
157
  end
158
158
  end
@@ -7,11 +7,12 @@ namespace :pg_search do
7
7
  namespace :multisearch do
8
8
  desc "Rebuild PgSearch multisearch records for a given model"
9
9
  task :rebuild, %i[model schema] => :environment do |_task, args|
10
- raise ArgumentError, <<-MESSAGE.strip_heredoc unless args.model
10
+ raise ArgumentError, <<~MESSAGE unless args.model
11
11
 
12
12
  You must pass a model as an argument.
13
13
  Example: rake pg_search:multisearch:rebuild[BlogPost]
14
14
  MESSAGE
15
+
15
16
  model_class = args.model.classify.constantize
16
17
  connection = PgSearch::Document.connection
17
18
  original_schema_search_path = connection.schema_search_path
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgSearch
4
- VERSION = '2.3.0'
4
+ VERSION = '2.3.5'
5
5
  end
@@ -3,7 +3,7 @@
3
3
  $LOAD_PATH.push File.expand_path('lib', __dir__)
4
4
  require 'pg_search/version'
5
5
 
6
- Gem::Specification.new do |s|
6
+ Gem::Specification.new do |s| # rubocop:disable Metrics/BlockLength
7
7
  s.name = 'pg_search'
8
8
  s.version = PgSearch::VERSION
9
9
  s.platform = Gem::Platform::RUBY
@@ -18,16 +18,20 @@ Gem::Specification.new do |s|
18
18
  s.test_files = `git ls-files -- spec/*`.split("\n")
19
19
  s.require_paths = ['lib']
20
20
 
21
- s.add_dependency 'activerecord', '>= 4.2'
22
- s.add_dependency 'activesupport', '>= 4.2'
21
+ s.add_dependency 'activerecord', '>= 5.2'
22
+ s.add_dependency 'activesupport', '>= 5.2'
23
23
 
24
24
  s.add_development_dependency 'pry'
25
25
  s.add_development_dependency 'rake'
26
- s.add_development_dependency 'rspec', '>= 3.3'
27
- s.add_development_dependency 'rubocop', '>= 0.68.1'
26
+ s.add_development_dependency 'rspec'
27
+ s.add_development_dependency 'rubocop'
28
28
  s.add_development_dependency 'rubocop-performance'
29
+ s.add_development_dependency 'rubocop-rails'
30
+ s.add_development_dependency 'rubocop-rake'
31
+ s.add_development_dependency 'rubocop-rspec'
29
32
  s.add_development_dependency 'simplecov'
30
- s.add_development_dependency 'with_model', '>= 1.2'
33
+ s.add_development_dependency 'warning'
34
+ s.add_development_dependency 'with_model'
31
35
 
32
- s.required_ruby_version = '>= 2.4'
36
+ s.required_ruby_version = '>= 2.5'
33
37
  end
@@ -1,10 +1,10 @@
1
1
  inherit_from:
2
2
  - ../.rubocop.yml
3
3
 
4
- Metrics/LineLength:
4
+ Layout/LineLength:
5
5
  Enabled: false
6
6
 
7
- Lint/HandleExceptions:
7
+ Lint/SuppressedException:
8
8
  Enabled: false
9
9
 
10
10
  Lint/UselessAssignment:
@@ -0,0 +1,11 @@
1
+ inherit_from:
2
+ - ../.rubocop.yml
3
+
4
+ RSpec/DescribeClass:
5
+ Enabled: false
6
+
7
+ RSpec/MultipleExpectations:
8
+ Enabled: false
9
+
10
+ RSpec/ExampleLength:
11
+ Enabled: false
@@ -2,8 +2,9 @@
2
2
 
3
3
  require "spec_helper"
4
4
 
5
- describe PgSearch do
6
- context "joining to another table" do
5
+ # rubocop:disable RSpec/NestedGroups
6
+ describe "a pg_search_scope" do
7
+ context "when joining to another table" do
7
8
  context "without an :against" do
8
9
  with_model :AssociatedModel do
9
10
  table do |t|
@@ -41,7 +42,7 @@ describe PgSearch do
41
42
  end
42
43
  end
43
44
 
44
- context "through a belongs_to association" do
45
+ context "via a belongs_to association" do
45
46
  with_model :AssociatedModel do
46
47
  table do |t|
47
48
  t.string 'title'
@@ -77,7 +78,7 @@ describe PgSearch do
77
78
  end
78
79
  end
79
80
 
80
- context "through a has_many association" do
81
+ context "via a has_many association" do
81
82
  with_model :AssociatedModelWithHasMany do
82
83
  table do |t|
83
84
  t.string 'title'
@@ -141,8 +142,8 @@ describe PgSearch do
141
142
  end
142
143
  end
143
144
 
144
- context "across multiple associations" do
145
- context "on different tables" do
145
+ context "when across multiple associations" do
146
+ context "when on different tables" do
146
147
  with_model :FirstAssociatedModel do
147
148
  table do |t|
148
149
  t.string 'title'
@@ -207,7 +208,7 @@ describe PgSearch do
207
208
  end
208
209
  end
209
210
 
210
- context "on the same table" do
211
+ context "when on the same table" do
211
212
  with_model :DoublyAssociatedModel do
212
213
  table do |t|
213
214
  t.string 'title'
@@ -268,7 +269,7 @@ describe PgSearch do
268
269
  end
269
270
  end
270
271
 
271
- context "against multiple attributes on one association" do
272
+ context "when against multiple attributes on one association" do
272
273
  with_model :AssociatedModel do
273
274
  table do |t|
274
275
  t.string 'title'
@@ -289,7 +290,7 @@ describe PgSearch do
289
290
  end
290
291
  end
291
292
 
292
- it "should only do one join" do
293
+ it "joins only once" do
293
294
  included = [
294
295
  ModelWithAssociation.create!(
295
296
  another_model: AssociatedModel.create!(
@@ -321,7 +322,7 @@ describe PgSearch do
321
322
  end
322
323
  end
323
324
 
324
- context "against non-text columns" do
325
+ context "when against non-text columns" do
325
326
  with_model :AssociatedModel do
326
327
  table do |t|
327
328
  t.integer 'number'
@@ -342,7 +343,7 @@ describe PgSearch do
342
343
  end
343
344
  end
344
345
 
345
- it "should cast the columns to text" do
346
+ it "casts the columns to text" do
346
347
  associated = AssociatedModel.create!(number: 123)
347
348
  included = [
348
349
  Model.create!(number: 123, another_model: associated),
@@ -395,7 +396,7 @@ describe PgSearch do
395
396
  end
396
397
  end
397
398
 
398
- context "merging a pg_search_scope into another model's scope" do
399
+ context "when merging a pg_search_scope into another model's scope" do
399
400
  with_model :ModelWithAssociation do
400
401
  model do
401
402
  has_many :associated_models
@@ -416,7 +417,7 @@ describe PgSearch do
416
417
  end
417
418
  end
418
419
 
419
- it "should find records of the other model" do
420
+ it "finds records of the other model" do
420
421
  included_associated_1 = AssociatedModel.create(content: "foo bar")
421
422
  included_associated_2 = AssociatedModel.create(content: "foo baz")
422
423
  excluded_associated_1 = AssociatedModel.create(content: "baz quux")
@@ -441,7 +442,7 @@ describe PgSearch do
441
442
  end
442
443
  end
443
444
 
444
- context "chained onto a has_many association" do
445
+ context "when chained onto a has_many association" do
445
446
  with_model :Company do
446
447
  model do
447
448
  has_many :positions
@@ -461,48 +462,7 @@ describe PgSearch do
461
462
  end
462
463
 
463
464
  # https://github.com/Casecommons/pg_search/issues/106
464
- it "should handle numbers in a trigram query properly" do
465
- company = Company.create!
466
- another_company = Company.create!
467
-
468
- included = [
469
- Position.create!(company_id: company.id, title: "teller 1")
470
- ]
471
-
472
- excluded = [
473
- Position.create!(company_id: nil, title: "teller 1"),
474
- Position.create!(company_id: another_company.id, title: "teller 1"),
475
- Position.create!(company_id: company.id, title: "penn 1")
476
- ]
477
-
478
- results = company.positions.search('teller 1')
479
-
480
- expect(results).to include(*included)
481
- expect(results).not_to include(*excluded)
482
- end
483
- end
484
-
485
- context "chained onto a has_many association" do
486
- with_model :Company do
487
- model do
488
- has_many :positions
489
- end
490
- end
491
-
492
- with_model :Position do
493
- table do |t|
494
- t.string :title
495
- t.belongs_to :company
496
- end
497
-
498
- model do
499
- include PgSearch::Model
500
- pg_search_scope :search, against: :title, using: %i[tsearch trigram]
501
- end
502
- end
503
-
504
- # https://github.com/Casecommons/pg_search/issues/106
505
- it "should handle numbers in a trigram query properly" do
465
+ it "handles numbers in a trigram query properly" do
506
466
  company = Company.create!
507
467
  another_company = Company.create!
508
468
 
@@ -524,3 +484,4 @@ describe PgSearch do
524
484
  end
525
485
  end
526
486
  end
487
+ # rubocop:enable RSpec/NestedGroups