pg_search 2.1.2 → 2.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +1 -0
  3. data/.editorconfig +10 -0
  4. data/.github/dependabot.yml +11 -0
  5. data/.github/workflows/ci.yml +75 -0
  6. data/.jrubyrc +1 -0
  7. data/.rubocop.yml +95 -10
  8. data/.travis.yml +26 -48
  9. data/CHANGELOG.md +179 -112
  10. data/CODE_OF_CONDUCT.md +76 -0
  11. data/CONTRIBUTING.md +5 -3
  12. data/Gemfile +6 -4
  13. data/LICENSE +1 -1
  14. data/README.md +307 -198
  15. data/Rakefile +7 -3
  16. data/lib/pg_search/configuration/association.rb +2 -0
  17. data/lib/pg_search/configuration/column.rb +2 -0
  18. data/lib/pg_search/configuration/foreign_column.rb +2 -0
  19. data/lib/pg_search/configuration.rb +20 -6
  20. data/lib/pg_search/document.rb +7 -5
  21. data/lib/pg_search/features/dmetaphone.rb +7 -7
  22. data/lib/pg_search/features/feature.rb +4 -2
  23. data/lib/pg_search/features/trigram.rb +31 -5
  24. data/lib/pg_search/features/tsearch.rb +18 -14
  25. data/lib/pg_search/features.rb +2 -0
  26. data/lib/pg_search/migration/dmetaphone_generator.rb +3 -1
  27. data/lib/pg_search/migration/generator.rb +4 -2
  28. data/lib/pg_search/migration/multisearch_generator.rb +2 -1
  29. data/lib/pg_search/migration/templates/add_pg_search_dmetaphone_support_functions.rb.erb +6 -6
  30. data/lib/pg_search/migration/templates/create_pg_search_documents.rb.erb +3 -3
  31. data/lib/pg_search/model.rb +57 -0
  32. data/lib/pg_search/multisearch/rebuilder.rb +14 -6
  33. data/lib/pg_search/multisearch.rb +23 -6
  34. data/lib/pg_search/multisearchable.rb +10 -6
  35. data/lib/pg_search/normalizer.rb +2 -0
  36. data/lib/pg_search/railtie.rb +2 -0
  37. data/lib/pg_search/scope_options.rb +26 -49
  38. data/lib/pg_search/tasks.rb +5 -1
  39. data/lib/pg_search/version.rb +3 -1
  40. data/lib/pg_search.rb +17 -55
  41. data/pg_search.gemspec +19 -11
  42. data/spec/.rubocop.yml +2 -2
  43. data/spec/integration/.rubocop.yml +11 -0
  44. data/spec/integration/associations_spec.rb +125 -162
  45. data/spec/integration/deprecation_spec.rb +33 -0
  46. data/spec/integration/pagination_spec.rb +10 -8
  47. data/spec/integration/pg_search_spec.rb +359 -306
  48. data/spec/integration/single_table_inheritance_spec.rb +18 -17
  49. data/spec/lib/pg_search/configuration/association_spec.rb +17 -13
  50. data/spec/lib/pg_search/configuration/column_spec.rb +2 -0
  51. data/spec/lib/pg_search/configuration/foreign_column_spec.rb +6 -4
  52. data/spec/lib/pg_search/features/dmetaphone_spec.rb +6 -4
  53. data/spec/lib/pg_search/features/trigram_spec.rb +51 -20
  54. data/spec/lib/pg_search/features/tsearch_spec.rb +29 -21
  55. data/spec/lib/pg_search/multisearch/rebuilder_spec.rb +151 -85
  56. data/spec/lib/pg_search/multisearch_spec.rb +67 -37
  57. data/spec/lib/pg_search/multisearchable_spec.rb +217 -123
  58. data/spec/lib/pg_search/normalizer_spec.rb +14 -10
  59. data/spec/lib/pg_search_spec.rb +102 -89
  60. data/spec/spec_helper.rb +25 -6
  61. data/spec/support/database.rb +19 -21
  62. data/spec/support/with_model.rb +2 -0
  63. metadata +106 -29
  64. data/.autotest +0 -5
  65. data/.rubocop_todo.yml +0 -163
  66. data/Guardfile +0 -6
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler'
2
4
  Bundler::GemHelper.install_tasks
3
5
 
@@ -9,8 +11,10 @@ RuboCop::RakeTask.new do |t|
9
11
  t.options = %w[--display-cop-names]
10
12
  end
11
13
 
12
- task :codeclimate do
13
- sh 'bin/codeclimate-test-reporter' if ENV['CODECLIMATE_REPO_TOKEN']
14
+ desc "Check test coverage"
15
+ task :undercover do
16
+ system("git fetch --unshallow") if ENV["CI"]
17
+ exit(1) unless system("bin/undercover --compare origin/master")
14
18
  end
15
19
 
16
- task :default => %w[spec codeclimate rubocop]
20
+ task default: %w[spec rubocop undercover]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "digest"
2
4
 
3
5
  module PgSearch
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'digest'
2
4
 
3
5
  module PgSearch
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'digest'
2
4
 
3
5
  module PgSearch
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "pg_search/configuration/association"
2
4
  require "pg_search/configuration/column"
3
5
  require "pg_search/configuration/foreign_column"
@@ -17,7 +19,7 @@ module PgSearch
17
19
  def alias(*strings)
18
20
  name = Array(strings).compact.join("_")
19
21
  # By default, PostgreSQL limits names to 32 characters, so we hash and limit to 32 characters.
20
- "pg_search_#{Digest::SHA2.hexdigest(name)}"[0,32]
22
+ "pg_search_#{Digest::SHA2.hexdigest(name)}".first(32)
21
23
  end
22
24
  end
23
25
 
@@ -27,6 +29,7 @@ module PgSearch
27
29
 
28
30
  def regular_columns
29
31
  return [] unless options[:against]
32
+
30
33
  Array(options[:against]).map do |column_name, weight|
31
34
  Column.new(column_name, weight, model)
32
35
  end
@@ -34,6 +37,7 @@ module PgSearch
34
37
 
35
38
  def associations
36
39
  return [] unless options[:associated_against]
40
+
37
41
  options[:associated_against].map do |association, column_names|
38
42
  Association.new(model, association, column_names)
39
43
  end.flatten
@@ -76,7 +80,7 @@ module PgSearch
76
80
  attr_reader :options
77
81
 
78
82
  def default_options
79
- {:using => :tsearch}
83
+ { using: :tsearch }
80
84
  end
81
85
 
82
86
  VALID_KEYS = %w[
@@ -84,12 +88,15 @@ module PgSearch
84
88
  ].map(&:to_sym)
85
89
 
86
90
  VALID_VALUES = {
87
- :ignoring => [:accents]
88
- }
91
+ ignoring: [:accents]
92
+ }.freeze
89
93
 
90
94
  def assert_valid_options(options)
91
- unless options[:against] || options[:associated_against]
92
- raise ArgumentError, "the search scope #{@name} must have :against or :associated_against in its options"
95
+ unless options[:against] || options[:associated_against] || using_tsvector_column?(options[:using])
96
+ raise(
97
+ ArgumentError,
98
+ "the search scope #{@name} must have :against, :associated_against, or :tsvector_column in its options"
99
+ )
93
100
  end
94
101
 
95
102
  options.assert_valid_keys(VALID_KEYS)
@@ -100,5 +107,12 @@ module PgSearch
100
107
  end
101
108
  end
102
109
  end
110
+
111
+ def using_tsvector_column?(options)
112
+ return unless options.is_a?(Hash)
113
+
114
+ options.dig(:dmetaphone, :tsvector_column).present? ||
115
+ options.dig(:tsearch, :tsvector_column).present?
116
+ end
103
117
  end
104
118
  end
@@ -1,26 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
 
3
5
  module PgSearch
4
6
  class Document < ActiveRecord::Base
5
- include PgSearch
7
+ include PgSearch::Model
6
8
 
7
9
  self.table_name = 'pg_search_documents'
8
- belongs_to :searchable, :polymorphic => true
10
+ belongs_to :searchable, polymorphic: true
9
11
 
10
12
  # The logger might not have loaded yet.
11
13
  # https://github.com/Casecommons/pg_search/issues/26
12
14
  def self.logger
13
- super || Logger.new(STDERR)
15
+ super || Logger.new($stderr)
14
16
  end
15
17
 
16
18
  pg_search_scope :search, lambda { |*args|
17
19
  options = if PgSearch.multisearch_options.respond_to?(:call)
18
20
  PgSearch.multisearch_options.call(*args)
19
21
  else
20
- {:query => args.first}.merge(PgSearch.multisearch_options)
22
+ { query: args.first }.merge(PgSearch.multisearch_options)
21
23
  end
22
24
 
23
- {:against => :content}.merge(options)
25
+ { against: :content }.merge(options)
24
26
  }
25
27
  end
26
28
  end
@@ -1,19 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/delegation"
4
+
1
5
  module PgSearch
2
6
  module Features
3
7
  class DMetaphone
4
8
  def initialize(query, options, columns, model, normalizer)
5
9
  dmetaphone_normalizer = Normalizer.new(normalizer)
6
- options = (options || {}).merge(:dictionary => 'simple')
10
+ options = (options || {}).merge(dictionary: 'simple')
7
11
  @tsearch = TSearch.new(query, options, columns, model, dmetaphone_normalizer)
8
12
  end
9
13
 
10
- def conditions
11
- tsearch.conditions
12
- end
14
+ delegate :conditions, to: :tsearch
13
15
 
14
- def rank
15
- tsearch.rank
16
- end
16
+ delegate :rank, to: :tsearch
17
17
 
18
18
  private
19
19
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/module/delegation"
2
4
  require "active_support/core_ext/hash/keys"
3
5
 
@@ -8,7 +10,7 @@ module PgSearch
8
10
  %i[only sort_only]
9
11
  end
10
12
 
11
- delegate :connection, :quoted_table_name, :to => :'@model'
13
+ delegate :connection, :quoted_table_name, to: :@model
12
14
 
13
15
  def initialize(query, options, all_columns, model, normalizer)
14
16
  @query = query
@@ -23,7 +25,7 @@ module PgSearch
23
25
  attr_reader :query, :options, :all_columns, :model, :normalizer
24
26
 
25
27
  def document
26
- columns.map { |column| column.to_sql }.join(" || ' ' || ")
28
+ columns.map(&:to_sql).join(" || ' ' || ")
27
29
  end
28
30
 
29
31
  def columns
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PgSearch
2
4
  module Features
3
5
  class Trigram < Feature
4
6
  def self.valid_options
5
- super + [:threshold]
7
+ super + %i[threshold word_similarity]
6
8
  end
7
9
 
8
10
  def conditions
@@ -12,7 +14,11 @@ module PgSearch
12
14
  )
13
15
  else
14
16
  Arel::Nodes::Grouping.new(
15
- Arel::Nodes::InfixOperation.new("%", normalized_document, normalized_query)
17
+ Arel::Nodes::InfixOperation.new(
18
+ infix_operator,
19
+ normalized_query,
20
+ normalized_document
21
+ )
16
22
  )
17
23
  end
18
24
  end
@@ -23,12 +29,32 @@ module PgSearch
23
29
 
24
30
  private
25
31
 
32
+ def word_similarity?
33
+ options[:word_similarity]
34
+ end
35
+
36
+ def similarity_function
37
+ if word_similarity?
38
+ 'word_similarity'
39
+ else
40
+ 'similarity'
41
+ end
42
+ end
43
+
44
+ def infix_operator
45
+ if word_similarity?
46
+ '<%'
47
+ else
48
+ '%'
49
+ end
50
+ end
51
+
26
52
  def similarity
27
53
  Arel::Nodes::NamedFunction.new(
28
- "similarity",
54
+ similarity_function,
29
55
  [
30
- normalized_document,
31
- normalized_query
56
+ normalized_query,
57
+ normalized_document
32
58
  ]
33
59
  )
34
60
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/module/delegation"
2
4
  require 'active_support/deprecation'
3
5
 
@@ -57,7 +59,7 @@ module PgSearch
57
59
  end
58
60
  end
59
61
 
60
- def deprecated_headline_options
62
+ def deprecated_headline_options # rubocop:disable Metrics/MethodLength
61
63
  indifferent_options = options.with_indifferent_access
62
64
 
63
65
  %w[
@@ -93,9 +95,9 @@ module PgSearch
93
95
  end
94
96
  end
95
97
 
96
- DISALLOWED_TSQUERY_CHARACTERS = /['?\\:]/
98
+ DISALLOWED_TSQUERY_CHARACTERS = /['?\\:‘’]/.freeze
97
99
 
98
- def tsquery_for_term(unsanitized_term) # rubocop:disable Metrics/AbcSize
100
+ def tsquery_for_term(unsanitized_term)
99
101
  if options[:negation] && unsanitized_term.start_with?("!")
100
102
  unsanitized_term[0] = ''
101
103
  negated = true
@@ -105,30 +107,32 @@ module PgSearch
105
107
 
106
108
  term_sql = Arel.sql(normalize(connection.quote(sanitized_term)))
107
109
 
108
- # After this, the SQL expression evaluates to a string containing the term surrounded by single-quotes.
109
- # If :prefix is true, then the term will have :* appended to the end.
110
- # 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:)
111
119
  terms = [
112
120
  (Arel::Nodes.build_quoted('!') if negated),
113
121
  Arel::Nodes.build_quoted("' "),
114
122
  term_sql,
115
123
  Arel::Nodes.build_quoted(" '"),
116
- (Arel::Nodes.build_quoted(":*") if options[:prefix])
124
+ (Arel::Nodes.build_quoted(":*") if prefix)
117
125
  ].compact
118
126
 
119
- tsquery_sql = terms.inject do |memo, term|
127
+ terms.inject do |memo, term|
120
128
  Arel::Nodes::InfixOperation.new("||", memo, Arel::Nodes.build_quoted(term))
121
129
  end
122
-
123
- Arel::Nodes::NamedFunction.new(
124
- "to_tsquery",
125
- [dictionary, tsquery_sql]
126
- ).to_sql
127
130
  end
128
131
 
129
132
  def tsquery
130
133
  return "''" if query.blank?
131
- query_terms = query.split(" ").compact
134
+
135
+ query_terms = query.split.compact
132
136
  tsquery_terms = query_terms.map { |term| tsquery_for_term(term) }
133
137
  tsquery_terms.join(options[:any_word] ? ' || ' : ' && ')
134
138
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "pg_search/features/feature"
2
4
 
3
5
  require "pg_search/features/dmetaphone"
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pg_search/migration/generator'
2
4
 
3
5
  module PgSearch
4
6
  module Migration
5
7
  class DmetaphoneGenerator < Generator
6
8
  def migration_name
7
- 'add_pg_search_dmetaphone_support_functions'.freeze
9
+ 'add_pg_search_dmetaphone_support_functions'
8
10
  end
9
11
  end
10
12
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_record'
2
4
  require 'rails/generators/base'
3
5
 
@@ -8,7 +10,7 @@ module PgSearch
8
10
 
9
11
  def self.inherited(subclass)
10
12
  super
11
- subclass.source_root File.expand_path('../templates', __FILE__)
13
+ subclass.source_root File.expand_path('templates', __dir__)
12
14
  end
13
15
 
14
16
  def create_migration
@@ -20,7 +22,7 @@ module PgSearch
20
22
  private
21
23
 
22
24
  def read_sql_file(filename)
23
- sql_directory = File.expand_path('../../../../sql', __FILE__)
25
+ sql_directory = File.expand_path('../../../sql', __dir__)
24
26
  source_path = File.join(sql_directory, "#{filename}.sql")
25
27
  File.read(source_path).strip
26
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pg_search/migration/generator'
2
4
 
3
5
  module PgSearch
@@ -9,4 +11,3 @@ module PgSearch
9
11
  end
10
12
  end
11
13
  end
12
-
@@ -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,15 +1,15 @@
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
6
- t.belongs_to :searchable, :polymorphic => true, :index => true
6
+ t.belongs_to :searchable, polymorphic: true, index: true
7
7
  t.timestamps null: false
8
8
  end
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
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgSearch
4
+ module Model
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def pg_search_scope(name, options)
9
+ options_proc = if options.respond_to?(:call)
10
+ options
11
+ elsif options.respond_to?(:merge)
12
+ ->(query) { { query: query }.merge(options) }
13
+ else
14
+ raise ArgumentError, 'pg_search_scope expects a Hash or Proc'
15
+ end
16
+
17
+ define_singleton_method(name) do |*args|
18
+ config = Configuration.new(options_proc.call(*args), self)
19
+ scope_options = ScopeOptions.new(config)
20
+ scope_options.apply(self)
21
+ end
22
+ end
23
+
24
+ def multisearchable(options = {})
25
+ include PgSearch::Multisearchable
26
+ class_attribute :pg_search_multisearchable_options
27
+ self.pg_search_multisearchable_options = options
28
+ end
29
+ end
30
+
31
+ def method_missing(symbol, *args)
32
+ case symbol
33
+ when :pg_search_rank
34
+ raise PgSearchRankNotSelected unless respond_to?(:pg_search_rank)
35
+
36
+ read_attribute(:pg_search_rank).to_f
37
+ when :pg_search_highlight
38
+ raise PgSearchHighlightNotSelected unless respond_to?(:pg_search_highlight)
39
+
40
+ read_attribute(:pg_search_highlight)
41
+ else
42
+ super
43
+ end
44
+ end
45
+
46
+ def respond_to_missing?(symbol, *args)
47
+ case symbol
48
+ when :pg_search_rank
49
+ attributes.key?(:pg_search_rank)
50
+ when :pg_search_highlight
51
+ attributes.key?(:pg_search_highlight)
52
+ else
53
+ super
54
+ end
55
+ end
56
+ end
57
+ end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PgSearch
2
4
  module Multisearch
3
5
  class Rebuilder
4
6
  def initialize(model, time_source = Time.method(:now))
5
- raise ModelNotMultisearchable.new(model) unless model.respond_to?(:pg_search_multisearchable_options)
7
+ raise ModelNotMultisearchable, model unless model.respond_to?(:pg_search_multisearchable_options)
6
8
 
7
9
  @model = model
8
10
  @time_source = time_source
@@ -11,8 +13,8 @@ module PgSearch
11
13
  def rebuild
12
14
  if model.respond_to?(:rebuild_pg_search_documents)
13
15
  model.rebuild_pg_search_documents
14
- elsif conditional? || dynamic?
15
- model.find_each { |record| record.update_pg_search_document }
16
+ elsif conditional? || dynamic? || additional_attributes?
17
+ model.find_each(&:update_pg_search_document)
16
18
  else
17
19
  model.connection.execute(rebuild_sql)
18
20
  end
@@ -28,7 +30,11 @@ module PgSearch
28
30
 
29
31
  def dynamic?
30
32
  column_names = model.columns.map(&:name)
31
- 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)
32
38
  end
33
39
 
34
40
  def connection
@@ -40,7 +46,7 @@ module PgSearch
40
46
  end
41
47
 
42
48
  def rebuild_sql_template
43
- <<-SQL.strip_heredoc
49
+ <<~SQL.squish
44
50
  INSERT INTO :documents_table (searchable_type, searchable_id, content, created_at, updated_at)
45
51
  SELECT :base_model_name AS searchable_type,
46
52
  :model_table.#{primary_key} AS searchable_id,
@@ -74,7 +80,9 @@ module PgSearch
74
80
  end
75
81
 
76
82
  def content_expressions
77
- columns.map { |column| %{coalesce(:model_table.#{column}::text, '')} }.join(" || ' ' || ")
83
+ columns.map do |column|
84
+ %{coalesce(:model_table.#{connection.quote_column_name(column)}::text, '')}
85
+ end.join(" || ' ' || ")
78
86
  end
79
87
 
80
88
  def columns
@@ -1,18 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "pg_search/multisearch/rebuilder"
2
4
 
3
5
  module PgSearch
4
6
  module Multisearch
5
7
  class << self
6
- def rebuild(model, clean_up=true)
7
- model.transaction do
8
- PgSearch::Document.where(:searchable_type => model.base_class.name).delete_all if clean_up
9
- Rebuilder.new(model).rebuild
8
+ def rebuild(model, deprecated_clean_up = nil, clean_up: true, transactional: 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
+
17
+ if transactional
18
+ model.transaction { execute(model, clean_up) }
19
+ else
20
+ execute(model, clean_up)
10
21
  end
11
22
  end
23
+
24
+ private
25
+
26
+ def execute(model, clean_up)
27
+ PgSearch::Document.where(searchable_type: model.base_class.name).delete_all if clean_up
28
+ Rebuilder.new(model).rebuild
29
+ end
12
30
  end
13
31
 
14
32
  class ModelNotMultisearchable < StandardError
15
33
  def initialize(model_class)
34
+ super
16
35
  @model_class = model_class
17
36
  end
18
37
 
@@ -22,5 +41,3 @@ module PgSearch
22
41
  end
23
42
  end
24
43
  end
25
-
26
-
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/class/attribute"
2
4
 
3
5
  module PgSearch
@@ -5,12 +7,12 @@ module PgSearch
5
7
  def self.included(mod)
6
8
  mod.class_eval do
7
9
  has_one :pg_search_document,
8
- :as => :searchable,
9
- :class_name => "PgSearch::Document",
10
- :dependent => :delete
10
+ as: :searchable,
11
+ class_name: "PgSearch::Document",
12
+ dependent: :delete
11
13
 
12
14
  after_save :update_pg_search_document,
13
- :if => -> { PgSearch.multisearch_enabled? }
15
+ if: -> { PgSearch.multisearch_enabled? }
14
16
  end
15
17
  end
16
18
 
@@ -31,6 +33,8 @@ module PgSearch
31
33
  end
32
34
 
33
35
  def should_update_pg_search_document?
36
+ return false if pg_search_document.destroyed?
37
+
34
38
  conditions = Array(pg_search_multisearchable_options[:update_if])
35
39
  conditions.all? { |condition| condition.to_proc.call(self) }
36
40
  end
@@ -46,7 +50,7 @@ module PgSearch
46
50
  if should_have_document
47
51
  create_or_update_pg_search_document
48
52
  else
49
- pg_search_document.destroy if pg_search_document
53
+ pg_search_document&.destroy
50
54
  end
51
55
  end
52
56
 
@@ -54,7 +58,7 @@ module PgSearch
54
58
  if !pg_search_document
55
59
  create_pg_search_document(pg_search_document_attrs)
56
60
  elsif should_update_pg_search_document?
57
- pg_search_document.update_attributes(pg_search_document_attrs)
61
+ pg_search_document.update(pg_search_document_attrs)
58
62
  end
59
63
  end
60
64
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PgSearch
2
4
  class Normalizer
3
5
  def initialize(config)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PgSearch
2
4
  class Railtie < Rails::Railtie
3
5
  rake_tasks do