diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a89774b1..226586b9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -36,33 +36,28 @@ jobs:
strategy:
fail-fast: false
matrix:
- ruby-version: ['2.7', '3.0', '3.1']
+ ruby-version: ['2.7', '3.0', '3.1', '3.2']
active-record-version-env:
- - ACTIVE_RECORD_VERSION="~> 5.2.0"
- ACTIVE_RECORD_VERSION="~> 6.0.0"
- ACTIVE_RECORD_VERSION="~> 6.1.0"
- ACTIVE_RECORD_VERSION="~> 7.0.0"
allow-failure: [false]
include:
- - ruby-version: '3.1'
+ - ruby-version: '3.2'
active-record-version-env: ACTIVE_RECORD_BRANCH="main"
allow-failure: true
- - ruby-version: '3.1'
+ - ruby-version: '3.2'
active-record-version-env: ACTIVE_RECORD_BRANCH="7-0-stable"
allow-failure: true
- - ruby-version: '3.1'
+ - ruby-version: '3.2'
active-record-version-env: ACTIVE_RECORD_BRANCH="6-1-stable"
allow-failure: true
- exclude:
- - ruby-version: '3.0'
- active-record-version-env: ACTIVE_RECORD_VERSION="~> 5.2.0"
- allow-failure: false
- - ruby-version: '3.1'
- active-record-version-env: ACTIVE_RECORD_VERSION="~> 5.2.0"
- allow-failure: false
+ - ruby-version: '3.3.0-preview1'
+ active-record-version-env: ACTIVE_RECORD_VERSION="~> 7.0.0"
+ allow-failure: true
continue-on-error: ${{ matrix.allow-failure }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
diff --git a/.rubocop.yml b/.rubocop.yml
index 5bb6075e..7d946471 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,137 +1,34 @@
require:
+ - standard
- rubocop-performance
- rubocop-rails
- rubocop-rake
- rubocop-rspec
+inherit_gem:
+ standard: config/base.yml
+
AllCops:
TargetRubyVersion: 2.7
- NewCops: enable
+ NewCops: disable
Exclude:
- bin/**/*
- vendor/**/*
-Style/StringLiterals:
- Enabled: false
-
-Layout/LineLength:
- Max: 120
-
-Metrics/MethodLength:
- Max: 15
-
-Metrics/BlockLength:
- Exclude:
- - spec/**/*
-
-Layout/ParameterAlignment:
- EnforcedStyle: with_fixed_indentation
-
-Style/NumericPredicate:
- Enabled: false
-
-Style/PercentLiteralDelimiters:
- PreferredDelimiters:
- '%w': '[]'
- '%W': '[]'
-
-Style/GuardClause:
- Enabled: false
-
-Naming/VariableNumber:
- EnforcedStyle: snake_case
-
-Bundler/OrderedGems:
- Enabled: false
-
-Bundler/DuplicatedGem:
- Enabled: false
-
-Style/EmptyMethod:
- EnforcedStyle: expanded
-
-Layout/FirstArrayElementIndentation:
- EnforcedStyle: consistent
-
-Style/Documentation:
- Enabled: false
-
-Style/WordArray:
- EnforcedStyle: percent
- MinSize: 3
-
-Style/HashEachMethods:
+Lint/RedundantCopDisableDirective:
Enabled: true
-Style/HashTransformKeys:
+Lint/RedundantCopEnableDirective:
Enabled: true
-Style/HashTransformValues:
- Enabled: true
-
-Rails/ApplicationRecord:
+Bundler/DuplicatedGem:
Enabled: false
-Rails/TimeZone:
+Rails/ApplicationRecord:
Enabled: false
-RSpec/ContextWording:
- Prefixes:
- - using
- - via
- - when
- - with
- - without
-
-Lint/RaiseException:
- Enabled: true
-
-Lint/StructNewOverride:
- Enabled: true
-
-Layout/SpaceAroundMethodCallOperator:
- Enabled: true
-
-Style/ExponentialNotation:
- Enabled: true
-
-RSpec/DescribedClass:
- Enabled: true
-
-RSpec/ExpectInHook:
+Rails/RakeEnvironment:
Enabled: false
-RSpec/FilePath:
- CustomTransform:
- TSearch: "tsearch"
- DMetaphone: "dmetaphone"
-
-Layout/EmptyLinesAroundAttributeAccessor:
- Enabled: true
-
-Lint/DeprecatedOpenSSLConstant:
- Enabled: true
-
-Style/SlicingWithRange:
- Enabled: true
-
-Lint/MixedRegexpCaptureTypes:
- Enabled: true
-
-Style/RedundantFetchBlock:
- Enabled: true
-
-Style/RedundantRegexpCharacterClass:
- Enabled: true
-
-Style/RedundantRegexpEscape:
- Enabled: true
-
-RSpec/MultipleExpectations:
- Max: 5
-
-RSpec/ExampleLength:
- Max: 15
-
-Rails/RakeEnvironment:
+Rails/TimeZone:
Enabled: false
diff --git a/.tool-versions b/.tool-versions
new file mode 100644
index 00000000..974865fc
--- /dev/null
+++ b/.tool-versions
@@ -0,0 +1 @@
+ruby 2.7.6
diff --git a/Gemfile b/Gemfile
index ea35689d..6d374066 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,15 +1,30 @@
# frozen_string_literal: true
-source 'https://rubygems.org'
+source "https://rubygems.org"
gemspec
-gem 'pg', '>= 0.21.0', platform: :ruby
+gem "pg", ">= 0.21.0", platform: :ruby
gem "activerecord-jdbcpostgresql-adapter", ">= 1.3.1", platform: :jruby
-if ENV['ACTIVE_RECORD_BRANCH']
- gem 'activerecord', git: 'https://github.com/rails/rails.git', branch: ENV.fetch('ACTIVE_RECORD_BRANCH', nil)
- gem 'arel', git: 'https://github.com/rails/arel.git' if ENV.fetch('ACTIVE_RECORD_BRANCH', nil) == 'master'
+if ENV["ACTIVE_RECORD_BRANCH"]
+ gem "activerecord", git: "https://github.com/rails/rails.git", branch: ENV.fetch("ACTIVE_RECORD_BRANCH", nil)
+ gem "arel", git: "https://github.com/rails/arel.git" if ENV.fetch("ACTIVE_RECORD_BRANCH", nil) == "master"
end
-gem 'activerecord', ENV.fetch('ACTIVE_RECORD_VERSION', nil) if ENV['ACTIVE_RECORD_VERSION']
+gem "activerecord", ENV.fetch("ACTIVE_RECORD_VERSION", nil) if ENV["ACTIVE_RECORD_VERSION"]
+
+gem "pry"
+gem "rake"
+gem "rspec"
+gem "rubocop"
+gem "rubocop-performance"
+gem "rubocop-rails"
+gem "rubocop-rake"
+gem "rubocop-rspec"
+gem "simplecov"
+gem "simplecov-lcov"
+gem "standard", ">= 1.23.0"
+gem "undercover"
+gem "warning"
+gem "with_model"
diff --git a/README.md b/README.md
index 3b70c3b5..b452b37e 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ Read the blog post introducing PgSearch at https://tanzu.vmware.com/content/blog
## REQUIREMENTS
* Ruby 2.7+
-* ActiveRecord 5.2+
+* Active Record 6.0+
* PostgreSQL 9.2+
* [PostgreSQL extensions](https://github.com/Casecommons/pg_search/wiki/Installing-PostgreSQL-Extensions) for certain features
@@ -979,7 +979,7 @@ Sentence.word_similarity_like("word") # => [sentence]
### Limiting Fields When Combining Features
Sometimes when doing queries combining different features you
-might want to searching against only some of the fields with certain features.
+might want to search against only some of the fields with certain features.
For example perhaps you want to only do a trigram search against the shorter fields
so that you don't need to reduce the threshold excessively. You can specify
which fields using the 'only' option:
diff --git a/Rakefile b/Rakefile
index fd5be07e..6167eb4c 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,9 +1,9 @@
# frozen_string_literal: true
-require 'bundler'
+require "bundler"
Bundler::GemHelper.install_tasks
-require 'rspec/core/rake_task'
+require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec)
require "rubocop/rake_task"
diff --git a/lib/pg_search/configuration.rb b/lib/pg_search/configuration.rb
index ce37edf9..5dd88f4d 100644
--- a/lib/pg_search/configuration.rb
+++ b/lib/pg_search/configuration.rb
@@ -80,7 +80,7 @@ def order_within_rank
attr_reader :options
def default_options
- { using: :tsearch }
+ {using: :tsearch}
end
VALID_KEYS = %w[
diff --git a/lib/pg_search/configuration/association.rb b/lib/pg_search/configuration/association.rb
index d9c17108..3a40e5a6 100644
--- a/lib/pg_search/configuration/association.rb
+++ b/lib/pg_search/configuration/association.rb
@@ -39,22 +39,17 @@ def selects
def selects_for_singular_association
columns.map do |column|
- if column.tsvector_column
- "tsvector_agg(#{column.full_name}) AS #{column.alias}"
- else
- case postgresql_version
- when 0..90000
- "array_to_string(array_agg(#{column.full_name}::text), ' ') AS #{column.alias}"
- else
- "string_agg(#{column.full_name}::text, ' ') AS #{column.alias}"
- end
- end
+ "#{column.full_name}::text AS #{column.alias}"
end.join(", ")
end
def selects_for_multiple_association
columns.map do |column|
- "string_agg(#{column.full_name}::text, ' ') AS #{column.alias}"
+ if column.tsvector_column
+ "tsvector_agg(#{column.full_name}) AS #{column.alias}"
+ else
+ "string_agg(#{column.full_name}::text, ' ') AS #{column.alias}"
+ end
end.join(", ")
end
diff --git a/lib/pg_search/configuration/column.rb b/lib/pg_search/configuration/column.rb
index 1cf9dedb..30d89fc2 100644
--- a/lib/pg_search/configuration/column.rb
+++ b/lib/pg_search/configuration/column.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'digest'
+require "digest"
module PgSearch
class Configuration
@@ -8,8 +8,8 @@ class Column
attr_reader :weight, :tsvector_column, :name
def initialize(column_name, weight, model)
- @name = column_name.to_s
- @column_name = column_name.to_s
+ @name = column_name.to_s
+ @column_name = column_name
if weight.is_a?(Hash)
@weight = weight[:weight]
@tsvector_column = weight[:tsvector_column]
@@ -21,6 +21,8 @@ def initialize(column_name, weight, model)
end
def full_name
+ return @column_name if @column_name.is_a?(Arel::Nodes::SqlLiteral)
+
"#{table_name}.#{column_name}"
end
@@ -28,7 +30,7 @@ def to_sql
if tsvector_column
"coalesce(#{expression}, '')"
else
- "coalesce(#{expression}::text, '')"
+ "coalesce((#{expression})::text, '')"
end
end
@@ -39,7 +41,7 @@ def table_name
end
def column_name
- @connection.quote_column_name(@column_name)
+ @connection.quote_column_name(@name)
end
def expression
diff --git a/lib/pg_search/configuration/foreign_column.rb b/lib/pg_search/configuration/foreign_column.rb
index 3886c160..b4956b01 100644
--- a/lib/pg_search/configuration/foreign_column.rb
+++ b/lib/pg_search/configuration/foreign_column.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'digest'
+require "digest"
module PgSearch
class Configuration
diff --git a/lib/pg_search/document.rb b/lib/pg_search/document.rb
index e0f3d3ec..e41eaaa1 100644
--- a/lib/pg_search/document.rb
+++ b/lib/pg_search/document.rb
@@ -1,12 +1,12 @@
# frozen_string_literal: true
-require 'logger'
+require "logger"
module PgSearch
class Document < ActiveRecord::Base
include PgSearch::Model
- self.table_name = 'pg_search_documents'
+ self.table_name = "pg_search_documents"
belongs_to :searchable, polymorphic: true
# The logger might not have loaded yet.
@@ -17,12 +17,12 @@ def self.logger
pg_search_scope :search, lambda { |*args|
options = if PgSearch.multisearch_options.respond_to?(:call)
- PgSearch.multisearch_options.call(*args)
- else
- { query: args.first }.merge(PgSearch.multisearch_options)
- end
+ PgSearch.multisearch_options.call(*args)
+ else
+ {query: args.first}.merge(PgSearch.multisearch_options)
+ end
- { against: :content }.merge(options)
+ {against: :content}.merge(options)
}
end
end
diff --git a/lib/pg_search/features/dmetaphone.rb b/lib/pg_search/features/dmetaphone.rb
index 295f79be..eb6852c6 100644
--- a/lib/pg_search/features/dmetaphone.rb
+++ b/lib/pg_search/features/dmetaphone.rb
@@ -7,7 +7,7 @@ module Features
class DMetaphone
def initialize(query, options, columns, model, normalizer)
dmetaphone_normalizer = Normalizer.new(normalizer)
- options = (options || {}).merge(dictionary: 'simple')
+ options = (options || {}).merge(dictionary: "simple")
@tsearch = TSearch.new(query, options, columns, model, dmetaphone_normalizer)
end
diff --git a/lib/pg_search/features/trigram.rb b/lib/pg_search/features/trigram.rb
index 47f3cc68..80bdfed9 100644
--- a/lib/pg_search/features/trigram.rb
+++ b/lib/pg_search/features/trigram.rb
@@ -35,17 +35,17 @@ def word_similarity?
def similarity_function
if word_similarity?
- 'word_similarity'
+ "word_similarity"
else
- 'similarity'
+ "similarity"
end
end
def infix_operator
if word_similarity?
- '<%'
+ "<%"
else
- '%'
+ "%"
end
end
diff --git a/lib/pg_search/features/tsearch.rb b/lib/pg_search/features/tsearch.rb
index a0e5e89f..a4136bde 100644
--- a/lib/pg_search/features/tsearch.rb
+++ b/lib/pg_search/features/tsearch.rb
@@ -1,11 +1,11 @@
# frozen_string_literal: true
require "active_support/core_ext/module/delegation"
-require 'active_support/deprecation'
+require "active_support/deprecation"
module PgSearch
module Features
- class TSearch < Feature # rubocop:disable Metrics/ClassLength
+ class TSearch < Feature
def self.valid_options
super + %i[dictionary prefix negation any_word normalization tsvector_column highlight]
end
@@ -36,7 +36,7 @@ def ts_headline
end
def ts_headline_options
- return '' unless options[:highlight].is_a?(Hash)
+ return "" unless options[:highlight].is_a?(Hash)
headline_options
.merge(deprecated_headline_options)
@@ -58,7 +58,7 @@ def headline_options
end
end
- def deprecated_headline_options # rubocop:disable Metrics/MethodLength
+ def deprecated_headline_options
indifferent_options = options.with_indifferent_access
%w[
@@ -94,11 +94,11 @@ def ts_headline_option_value(value)
end
end
- DISALLOWED_TSQUERY_CHARACTERS = /['?\\:‘’ʻʼ]/.freeze
+ DISALLOWED_TSQUERY_CHARACTERS = /['?\\:‘’ʻʼ]/
def tsquery_for_term(unsanitized_term)
if options[:negation] && unsanitized_term.start_with?("!")
- unsanitized_term[0] = ''
+ unsanitized_term[0] = ""
negated = true
end
@@ -116,7 +116,7 @@ def tsquery_for_term(unsanitized_term)
# If :negated is true, then the term will have ! prepended to the front.
def tsquery_expression(term_sql, negated:, prefix:)
terms = [
- (Arel::Nodes.build_quoted('!') if negated),
+ (Arel::Nodes.build_quoted("!") if negated),
Arel::Nodes.build_quoted("' "),
term_sql,
Arel::Nodes.build_quoted(" '"),
@@ -133,7 +133,7 @@ def tsquery
query_terms = query.split.compact
tsquery_terms = query_terms.map { |term| tsquery_for_term(term) }
- tsquery_terms.join(options[:any_word] ? ' || ' : ' && ')
+ tsquery_terms.join(options[:any_word] ? " || " : " && ")
end
def tsdocument
@@ -151,7 +151,7 @@ def tsdocument
end
end
- tsdocument_terms.join(' || ')
+ tsdocument_terms.join(" || ")
end
# From http://www.postgresql.org/docs/8.3/static/textsearch-controls.html
@@ -192,15 +192,10 @@ def columns_to_use
end
def column_to_tsvector(search_column)
- tsvector =
- if search_column.tsvector_column
- search_column.to_sql
- else
- Arel::Nodes::NamedFunction.new(
- "to_tsvector",
- [dictionary, Arel.sql(normalize(search_column.to_sql))]
- ).to_sql
- end
+ tsvector = Arel::Nodes::NamedFunction.new(
+ "to_tsvector",
+ [dictionary, Arel.sql(normalize(search_column.to_sql))]
+ ).to_sql
if search_column.weight.nil?
tsvector
diff --git a/lib/pg_search/migration/dmetaphone_generator.rb b/lib/pg_search/migration/dmetaphone_generator.rb
index 46e7888c..556a22e6 100644
--- a/lib/pg_search/migration/dmetaphone_generator.rb
+++ b/lib/pg_search/migration/dmetaphone_generator.rb
@@ -1,12 +1,12 @@
# frozen_string_literal: true
-require 'pg_search/migration/generator'
+require "pg_search/migration/generator"
module PgSearch
module Migration
class DmetaphoneGenerator < Generator
def migration_name
- 'add_pg_search_dmetaphone_support_functions'
+ "add_pg_search_dmetaphone_support_functions"
end
end
end
diff --git a/lib/pg_search/migration/generator.rb b/lib/pg_search/migration/generator.rb
index b0a422ae..2cf06a46 100644
--- a/lib/pg_search/migration/generator.rb
+++ b/lib/pg_search/migration/generator.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require 'active_record'
-require 'rails/generators/base'
+require "active_record"
+require "rails/generators/base"
module PgSearch
module Migration
@@ -10,19 +10,19 @@ class Generator < Rails::Generators::Base
def self.inherited(subclass)
super
- subclass.source_root File.expand_path('templates', __dir__)
+ subclass.source_root File.expand_path("templates", __dir__)
end
def create_migration
now = Time.now.utc
- filename = "#{now.strftime('%Y%m%d%H%M%S')}_#{migration_name}.rb"
+ filename = "#{now.strftime("%Y%m%d%H%M%S")}_#{migration_name}.rb"
template "#{migration_name}.rb.erb", "db/migrate/#{filename}", migration_version
end
private
def read_sql_file(filename)
- sql_directory = File.expand_path('../../../sql', __dir__)
+ sql_directory = File.expand_path("../../../sql", __dir__)
source_path = File.join(sql_directory, "#{filename}.sql")
File.read(source_path).strip
end
diff --git a/lib/pg_search/migration/multisearch_generator.rb b/lib/pg_search/migration/multisearch_generator.rb
index c97fed4b..8f8cd860 100644
--- a/lib/pg_search/migration/multisearch_generator.rb
+++ b/lib/pg_search/migration/multisearch_generator.rb
@@ -1,12 +1,12 @@
# frozen_string_literal: true
-require 'pg_search/migration/generator'
+require "pg_search/migration/generator"
module PgSearch
module Migration
class MultisearchGenerator < Generator
def migration_name
- 'create_pg_search_documents'
+ "create_pg_search_documents"
end
end
end
diff --git a/lib/pg_search/model.rb b/lib/pg_search/model.rb
index eaa5ddf6..ceb7a0f1 100644
--- a/lib/pg_search/model.rb
+++ b/lib/pg_search/model.rb
@@ -7,12 +7,12 @@ module Model
module ClassMethods
def pg_search_scope(name, options)
options_proc = if options.respond_to?(:call)
- options
- elsif options.respond_to?(:merge)
- ->(query) { { query: query }.merge(options) }
- else
- raise ArgumentError, 'pg_search_scope expects a Hash or Proc'
- end
+ options
+ elsif options.respond_to?(:merge)
+ ->(query) { {query: query}.merge(options) }
+ else
+ raise ArgumentError, "pg_search_scope expects a Hash or Proc"
+ end
define_singleton_method(name) do |*args|
config = Configuration.new(options_proc.call(*args), self)
diff --git a/lib/pg_search/multisearchable.rb b/lib/pg_search/multisearchable.rb
index 10a142f6..5309c1c0 100644
--- a/lib/pg_search/multisearchable.rb
+++ b/lib/pg_search/multisearchable.rb
@@ -7,12 +7,12 @@ module Multisearchable
def self.included(mod)
mod.class_eval do
has_one :pg_search_document,
- as: :searchable,
- class_name: "PgSearch::Document",
- dependent: :delete
+ as: :searchable,
+ class_name: "PgSearch::Document",
+ dependent: :delete
after_save :update_pg_search_document,
- if: -> { PgSearch.multisearch_enabled? }
+ if: -> { PgSearch.multisearch_enabled? }
end
end
@@ -39,7 +39,7 @@ def should_update_pg_search_document?
conditions.all? { |condition| condition.to_proc.call(self) }
end
- def update_pg_search_document # rubocop:disable Metrics/AbcSize
+ def update_pg_search_document
if_conditions = Array(pg_search_multisearchable_options[:if])
unless_conditions = Array(pg_search_multisearchable_options[:unless])
diff --git a/lib/pg_search/normalizer.rb b/lib/pg_search/normalizer.rb
index aed3f721..370b6a83 100644
--- a/lib/pg_search/normalizer.rb
+++ b/lib/pg_search/normalizer.rb
@@ -10,11 +10,11 @@ def add_normalization(sql_expression)
return sql_expression unless config.ignore.include?(:accents)
sql_node = case sql_expression
- when Arel::Nodes::Node
- sql_expression
- else
- Arel.sql(sql_expression)
- end
+ when Arel::Nodes::Node
+ sql_expression
+ else
+ Arel.sql(sql_expression)
+ end
Arel::Nodes::NamedFunction.new(
PgSearch.unaccent_function,
diff --git a/lib/pg_search/scope_options.rb b/lib/pg_search/scope_options.rb
index 369a603a..b03d13d0 100644
--- a/lib/pg_search/scope_options.rb
+++ b/lib/pg_search/scope_options.rb
@@ -91,9 +91,9 @@ def subquery
def conditions
config.features
- .reject { |_feature_name, feature_options| feature_options && feature_options[:sort_only] }
- .map { |feature_name, _feature_options| feature_for(feature_name).conditions }
- .inject { |accumulator, expression| Arel::Nodes::Or.new(accumulator, expression) }
+ .reject { |_feature_name, feature_options| feature_options && feature_options[:sort_only] }
+ .map { |feature_name, _feature_options| feature_for(feature_name).conditions }
+ .inject { |accumulator, expression| Arel::Nodes::Or.new(accumulator, expression) }
end
def order_within_rank
@@ -108,7 +108,7 @@ def subquery_join
if config.associations.any?
config.associations.map do |association|
association.join(primary_key)
- end.join(' ')
+ end.join(" ")
end
end
diff --git a/lib/pg_search/tasks.rb b/lib/pg_search/tasks.rb
index 04fec234..3b5740f4 100644
--- a/lib/pg_search/tasks.rb
+++ b/lib/pg_search/tasks.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require 'rake'
-require 'pg_search'
+require "rake"
+require "pg_search"
namespace :pg_search do
namespace :multisearch do
diff --git a/lib/pg_search/version.rb b/lib/pg_search/version.rb
index 2a007da3..afff11ae 100644
--- a/lib/pg_search/version.rb
+++ b/lib/pg_search/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module PgSearch
- VERSION = '2.3.6'
+ VERSION = "2.3.6"
end
diff --git a/pg_search.gemspec b/pg_search.gemspec
index ada1908b..103a9a38 100644
--- a/pg_search.gemspec
+++ b/pg_search.gemspec
@@ -1,39 +1,25 @@
# frozen_string_literal: true
-$LOAD_PATH.push File.expand_path('lib', __dir__)
-require 'pg_search/version'
+$LOAD_PATH.push File.expand_path("lib", __dir__)
+require "pg_search/version"
Gem::Specification.new do |s|
- s.name = 'pg_search'
- s.version = PgSearch::VERSION
- s.platform = Gem::Platform::RUBY
- s.authors = ['Grant Hutchins', 'Case Commons, LLC']
- s.email = %w[gems@nertzy.com casecommons-dev@googlegroups.com]
- s.homepage = 'https://github.com/Casecommons/pg_search'
- s.summary = "PgSearch builds Active Record named scopes that take advantage of PostgreSQL's full text search"
+ s.name = "pg_search"
+ s.version = PgSearch::VERSION
+ s.platform = Gem::Platform::RUBY
+ s.authors = ["Grant Hutchins", "Case Commons, LLC"]
+ s.email = %w[gems@nertzy.com casecommons-dev@googlegroups.com]
+ s.homepage = "https://github.com/Casecommons/pg_search"
+ s.summary = "PgSearch builds Active Record named scopes that take advantage of PostgreSQL's full text search"
s.description = "PgSearch builds Active Record named scopes that take advantage of PostgreSQL's full text search"
- s.licenses = ['MIT']
+ s.licenses = ["MIT"]
s.metadata["rubygems_mfa_required"] = "true"
- s.files = `git ls-files -z`.split("\x0")
- s.require_paths = ['lib']
+ s.files = `git ls-files -z`.split("\x0")
+ s.require_paths = ["lib"]
- s.add_dependency 'activerecord', '>= 5.2'
- s.add_dependency 'activesupport', '>= 5.2'
+ s.add_dependency "activerecord", ">= 6.0"
+ s.add_dependency "activesupport", ">= 6.0"
- s.add_development_dependency 'pry'
- s.add_development_dependency 'rake'
- s.add_development_dependency 'rspec'
- s.add_development_dependency 'rubocop'
- s.add_development_dependency 'rubocop-performance'
- s.add_development_dependency 'rubocop-rails'
- s.add_development_dependency 'rubocop-rake'
- s.add_development_dependency 'rubocop-rspec'
- s.add_development_dependency 'simplecov'
- s.add_development_dependency 'simplecov-lcov'
- s.add_development_dependency 'undercover'
- s.add_development_dependency 'warning'
- s.add_development_dependency 'with_model'
-
- s.required_ruby_version = '>= 2.7'
+ s.required_ruby_version = ">= 2.7"
end
diff --git a/spec/.rubocop.yml b/spec/.rubocop.yml
index 6e3e3040..ee624fbe 100644
--- a/spec/.rubocop.yml
+++ b/spec/.rubocop.yml
@@ -1,14 +1,27 @@
inherit_from:
- ../.rubocop.yml
-Layout/LineLength:
- Enabled: false
+RSpec/ContextWording:
+ Prefixes:
+ - using
+ - via
+ - when
+ - with
+ - without
-Lint/SuppressedException:
- Enabled: false
+RSpec/DescribedClass:
+ Enabled: true
-Lint/UselessAssignment:
- Enabled: false
+RSpec/ExampleLength:
+ Max: 15
-Style/BlockDelimiters:
+RSpec/ExpectInHook:
Enabled: false
+
+RSpec/FilePath:
+ CustomTransform:
+ TSearch: "tsearch"
+ DMetaphone: "dmetaphone"
+
+RSpec/MultipleExpectations:
+ Max: 5
diff --git a/spec/integration/.rubocop.yml b/spec/integration/.rubocop.yml
index 09ebbc02..b76a90e7 100644
--- a/spec/integration/.rubocop.yml
+++ b/spec/integration/.rubocop.yml
@@ -4,8 +4,8 @@ inherit_from:
RSpec/DescribeClass:
Enabled: false
-RSpec/MultipleExpectations:
+RSpec/ExampleLength:
Enabled: false
-RSpec/ExampleLength:
+RSpec/MultipleExpectations:
Enabled: false
diff --git a/spec/integration/associations_spec.rb b/spec/integration/associations_spec.rb
index a0801ed6..e929c2af 100644
--- a/spec/integration/associations_spec.rb
+++ b/spec/integration/associations_spec.rb
@@ -20,23 +20,23 @@
model do
include PgSearch::Model
- belongs_to :another_model, class_name: 'AssociatedModel'
+ belongs_to :another_model, class_name: "AssociatedModel"
- pg_search_scope :with_another, associated_against: { another_model: :title }
+ pg_search_scope :with_another, associated_against: {another_model: :title}
end
end
it "returns rows that match the query in the columns of the associated model only" do
- associated = AssociatedModel.create!(title: 'abcdef')
+ associated = AssociatedModel.create!(title: "abcdef")
included = [
- ModelWithoutAgainst.create!(title: 'abcdef', another_model: associated),
- ModelWithoutAgainst.create!(title: 'ghijkl', another_model: associated)
+ ModelWithoutAgainst.create!(title: "abcdef", another_model: associated),
+ ModelWithoutAgainst.create!(title: "ghijkl", another_model: associated)
]
excluded = [
- ModelWithoutAgainst.create!(title: 'abcdef')
+ ModelWithoutAgainst.create!(title: "abcdef")
]
- results = ModelWithoutAgainst.with_another('abcdef')
+ results = ModelWithoutAgainst.with_another("abcdef")
expect(results.map(&:title)).to match_array(included.map(&:title))
expect(results).not_to include(excluded)
end
@@ -45,34 +45,34 @@
context "via a belongs_to association" do
with_model :AssociatedModel do
table do |t|
- t.string 'title'
+ t.string "title"
end
end
with_model :ModelWithBelongsTo do
table do |t|
- t.string 'title'
- t.belongs_to 'another_model', index: false
+ t.string "title"
+ t.belongs_to "another_model", index: false
end
model do
include PgSearch::Model
- belongs_to :another_model, class_name: 'AssociatedModel'
+ belongs_to :another_model, class_name: "AssociatedModel"
- pg_search_scope :with_associated, against: :title, associated_against: { another_model: :title }
+ pg_search_scope :with_associated, against: :title, associated_against: {another_model: :title}
end
end
it "returns rows that match the query in either its own columns or the columns of the associated model" do
- associated = AssociatedModel.create!(title: 'abcdef')
+ associated = AssociatedModel.create!(title: "abcdef")
included = [
- ModelWithBelongsTo.create!(title: 'ghijkl', another_model: associated),
- ModelWithBelongsTo.create!(title: 'abcdef')
+ ModelWithBelongsTo.create!(title: "ghijkl", another_model: associated),
+ ModelWithBelongsTo.create!(title: "abcdef")
]
- excluded = ModelWithBelongsTo.create!(title: 'mnopqr',
- another_model: AssociatedModel.create!(title: 'stuvwx'))
+ excluded = ModelWithBelongsTo.create!(title: "mnopqr",
+ another_model: AssociatedModel.create!(title: "stuvwx"))
- results = ModelWithBelongsTo.with_associated('abcdef')
+ results = ModelWithBelongsTo.with_associated("abcdef")
expect(results.map(&:title)).to match_array(included.map(&:title))
expect(results).not_to include(excluded)
end
@@ -81,61 +81,61 @@
context "via a has_many association" do
with_model :AssociatedModelWithHasMany do
table do |t|
- t.string 'title'
- t.belongs_to 'ModelWithHasMany', index: false
+ t.string "title"
+ t.belongs_to "ModelWithHasMany", index: false
end
end
with_model :ModelWithHasMany do
table do |t|
- t.string 'title'
+ t.string "title"
end
model do
include PgSearch::Model
- has_many :other_models, class_name: 'AssociatedModelWithHasMany', foreign_key: 'ModelWithHasMany_id'
+ has_many :other_models, class_name: "AssociatedModelWithHasMany", foreign_key: "ModelWithHasMany_id"
- pg_search_scope :with_associated, against: [:title], associated_against: { other_models: :title }
+ pg_search_scope :with_associated, against: [:title], associated_against: {other_models: :title}
end
end
it "returns rows that match the query in either its own columns or the columns of the associated model" do
included = [
- ModelWithHasMany.create!(title: 'abcdef', other_models: [
- AssociatedModelWithHasMany.create!(title: 'foo'),
- AssociatedModelWithHasMany.create!(title: 'bar')
+ ModelWithHasMany.create!(title: "abcdef", other_models: [
+ AssociatedModelWithHasMany.create!(title: "foo"),
+ AssociatedModelWithHasMany.create!(title: "bar")
]),
- ModelWithHasMany.create!(title: 'ghijkl', other_models: [
- AssociatedModelWithHasMany.create!(title: 'foo bar'),
- AssociatedModelWithHasMany.create!(title: 'mnopqr')
+ ModelWithHasMany.create!(title: "ghijkl", other_models: [
+ AssociatedModelWithHasMany.create!(title: "foo bar"),
+ AssociatedModelWithHasMany.create!(title: "mnopqr")
]),
- ModelWithHasMany.create!(title: 'foo bar')
+ ModelWithHasMany.create!(title: "foo bar")
]
- excluded = ModelWithHasMany.create!(title: 'stuvwx', other_models: [
- AssociatedModelWithHasMany.create!(title: 'abcdef')
+ excluded = ModelWithHasMany.create!(title: "stuvwx", other_models: [
+ AssociatedModelWithHasMany.create!(title: "abcdef")
])
- results = ModelWithHasMany.with_associated('foo bar')
+ results = ModelWithHasMany.with_associated("foo bar")
expect(results.map(&:title)).to match_array(included.map(&:title))
expect(results).not_to include(excluded)
end
it "uses an unscoped relation of the associated model" do
- excluded = ModelWithHasMany.create!(title: 'abcdef', other_models: [
- AssociatedModelWithHasMany.create!(title: 'abcdef')
+ excluded = ModelWithHasMany.create!(title: "abcdef", other_models: [
+ AssociatedModelWithHasMany.create!(title: "abcdef")
])
included = [
- ModelWithHasMany.create!(title: 'abcdef', other_models: [
- AssociatedModelWithHasMany.create!(title: 'foo'),
- AssociatedModelWithHasMany.create!(title: 'bar')
+ ModelWithHasMany.create!(title: "abcdef", other_models: [
+ AssociatedModelWithHasMany.create!(title: "foo"),
+ AssociatedModelWithHasMany.create!(title: "bar")
])
]
results = ModelWithHasMany
- .limit(1)
- .order(Arel.sql("#{ModelWithHasMany.quoted_table_name}.id ASC"))
- .with_associated('foo bar')
+ .limit(1)
+ .order(Arel.sql("#{ModelWithHasMany.quoted_table_name}.id ASC"))
+ .with_associated("foo bar")
expect(results.map(&:title)).to match_array(included.map(&:title))
expect(results).not_to include(excluded)
@@ -146,36 +146,36 @@
context "when on different tables" do
with_model :FirstAssociatedModel do
table do |t|
- t.string 'title'
- t.belongs_to 'ModelWithManyAssociations', index: false
+ t.string "title"
+ t.belongs_to "ModelWithManyAssociations", index: false
end
end
with_model :SecondAssociatedModel do
table do |t|
- t.string 'title'
+ t.string "title"
end
end
with_model :ModelWithManyAssociations do
table do |t|
- t.string 'title'
- t.belongs_to 'model_of_second_type', index: false
+ t.string "title"
+ t.belongs_to "model_of_second_type", index: false
end
model do
include PgSearch::Model
has_many :models_of_first_type,
- class_name: 'FirstAssociatedModel',
- foreign_key: 'ModelWithManyAssociations_id'
+ class_name: "FirstAssociatedModel",
+ foreign_key: "ModelWithManyAssociations_id"
belongs_to :model_of_second_type,
- class_name: 'SecondAssociatedModel'
+ class_name: "SecondAssociatedModel"
pg_search_scope :with_associated,
- against: :title,
- associated_against: { models_of_first_type: :title, model_of_second_type: :title }
+ against: :title,
+ associated_against: {models_of_first_type: :title, model_of_second_type: :title}
end
end
@@ -184,25 +184,25 @@
unmatching_second = SecondAssociatedModel.create!(title: "uiop")
included = [
- ModelWithManyAssociations.create!(title: 'abcdef', models_of_first_type: [
- FirstAssociatedModel.create!(title: 'foo'),
- FirstAssociatedModel.create!(title: 'bar')
+ ModelWithManyAssociations.create!(title: "abcdef", models_of_first_type: [
+ FirstAssociatedModel.create!(title: "foo"),
+ FirstAssociatedModel.create!(title: "bar")
]),
- ModelWithManyAssociations.create!(title: 'ghijkl', models_of_first_type: [
- FirstAssociatedModel.create!(title: 'foo bar'),
- FirstAssociatedModel.create!(title: 'mnopqr')
+ ModelWithManyAssociations.create!(title: "ghijkl", models_of_first_type: [
+ FirstAssociatedModel.create!(title: "foo bar"),
+ FirstAssociatedModel.create!(title: "mnopqr")
]),
- ModelWithManyAssociations.create!(title: 'foo bar'),
- ModelWithManyAssociations.create!(title: 'qwerty', model_of_second_type: matching_second)
+ ModelWithManyAssociations.create!(title: "foo bar"),
+ ModelWithManyAssociations.create!(title: "qwerty", model_of_second_type: matching_second)
]
excluded = [
- ModelWithManyAssociations.create!(title: 'stuvwx', models_of_first_type: [
- FirstAssociatedModel.create!(title: 'abcdef')
+ ModelWithManyAssociations.create!(title: "stuvwx", models_of_first_type: [
+ FirstAssociatedModel.create!(title: "abcdef")
]),
- ModelWithManyAssociations.create!(title: 'qwerty', model_of_second_type: unmatching_second)
+ ModelWithManyAssociations.create!(title: "qwerty", model_of_second_type: unmatching_second)
]
- results = ModelWithManyAssociations.with_associated('foo bar')
+ results = ModelWithManyAssociations.with_associated("foo bar")
expect(results.map(&:title)).to match_array(included.map(&:title))
excluded.each { |object| expect(results).not_to include(object) }
end
@@ -211,58 +211,58 @@
context "when on the same table" do
with_model :DoublyAssociatedModel do
table do |t|
- t.string 'title'
- t.belongs_to 'ModelWithDoubleAssociation', index: false
- t.belongs_to 'ModelWithDoubleAssociation_again', index: false
+ t.string "title"
+ t.belongs_to "ModelWithDoubleAssociation", index: false
+ t.belongs_to "ModelWithDoubleAssociation_again", index: false
end
end
with_model :ModelWithDoubleAssociation do
table do |t|
- t.string 'title'
+ t.string "title"
end
model do
include PgSearch::Model
has_many :things,
- class_name: 'DoublyAssociatedModel',
- foreign_key: 'ModelWithDoubleAssociation_id'
+ class_name: "DoublyAssociatedModel",
+ foreign_key: "ModelWithDoubleAssociation_id"
has_many :thingamabobs,
- class_name: 'DoublyAssociatedModel',
- foreign_key: 'ModelWithDoubleAssociation_again_id'
+ class_name: "DoublyAssociatedModel",
+ foreign_key: "ModelWithDoubleAssociation_again_id"
pg_search_scope :with_associated, against: :title,
- associated_against: { things: :title, thingamabobs: :title }
+ associated_against: {things: :title, thingamabobs: :title}
end
end
it "returns rows that match the query in either its own columns or the columns of the associated model" do
included = [
- ModelWithDoubleAssociation.create!(title: 'abcdef', things: [
- DoublyAssociatedModel.create!(title: 'foo'),
- DoublyAssociatedModel.create!(title: 'bar')
+ ModelWithDoubleAssociation.create!(title: "abcdef", things: [
+ DoublyAssociatedModel.create!(title: "foo"),
+ DoublyAssociatedModel.create!(title: "bar")
]),
- ModelWithDoubleAssociation.create!(title: 'ghijkl', things: [
- DoublyAssociatedModel.create!(title: 'foo bar'),
- DoublyAssociatedModel.create!(title: 'mnopqr')
+ ModelWithDoubleAssociation.create!(title: "ghijkl", things: [
+ DoublyAssociatedModel.create!(title: "foo bar"),
+ DoublyAssociatedModel.create!(title: "mnopqr")
]),
- ModelWithDoubleAssociation.create!(title: 'foo bar'),
- ModelWithDoubleAssociation.create!(title: 'qwerty', thingamabobs: [
+ ModelWithDoubleAssociation.create!(title: "foo bar"),
+ ModelWithDoubleAssociation.create!(title: "qwerty", thingamabobs: [
DoublyAssociatedModel.create!(title: "foo bar")
])
]
excluded = [
- ModelWithDoubleAssociation.create!(title: 'stuvwx', things: [
- DoublyAssociatedModel.create!(title: 'abcdef')
+ ModelWithDoubleAssociation.create!(title: "stuvwx", things: [
+ DoublyAssociatedModel.create!(title: "abcdef")
]),
- ModelWithDoubleAssociation.create!(title: 'qwerty', thingamabobs: [
+ ModelWithDoubleAssociation.create!(title: "qwerty", thingamabobs: [
DoublyAssociatedModel.create!(title: "uiop")
])
]
- results = ModelWithDoubleAssociation.with_associated('foo bar')
+ results = ModelWithDoubleAssociation.with_associated("foo bar")
expect(results.map(&:title)).to match_array(included.map(&:title))
excluded.each { |object| expect(results).not_to include(object) }
end
@@ -272,21 +272,21 @@
context "when against multiple attributes on one association" do
with_model :AssociatedModel do
table do |t|
- t.string 'title'
- t.text 'author'
+ t.string "title"
+ t.text "author"
end
end
with_model :ModelWithAssociation do
table do |t|
- t.belongs_to 'another_model', index: false
+ t.belongs_to "another_model", index: false
end
model do
include PgSearch::Model
- belongs_to :another_model, class_name: 'AssociatedModel'
+ belongs_to :another_model, class_name: "AssociatedModel"
- pg_search_scope :with_associated, associated_against: { another_model: %i[title author] }
+ pg_search_scope :with_associated, associated_against: {another_model: %i[title author]}
end
end
@@ -314,7 +314,7 @@
)
]
- results = ModelWithAssociation.with_associated('foo bar')
+ results = ModelWithAssociation.with_associated("foo bar")
expect(results.to_sql.scan("INNER JOIN #{AssociatedModel.quoted_table_name}").length).to eq(1)
included.each { |object| expect(results).to include(object) }
@@ -325,21 +325,21 @@
context "when against non-text columns" do
with_model :AssociatedModel do
table do |t|
- t.integer 'number'
+ t.integer "number"
end
end
with_model :Model do
table do |t|
- t.integer 'number'
- t.belongs_to 'another_model', index: false
+ t.integer "number"
+ t.belongs_to "another_model", index: false
end
model do
include PgSearch::Model
- belongs_to :another_model, class_name: 'AssociatedModel'
+ belongs_to :another_model, class_name: "AssociatedModel"
- pg_search_scope :with_associated, associated_against: { another_model: :number }
+ pg_search_scope :with_associated, associated_against: {another_model: :number}
end
end
@@ -353,7 +353,7 @@
Model.create!(number: 123)
]
- results = Model.with_associated('123')
+ results = Model.with_associated("123")
expect(results.map(&:number)).to match_array(included.map(&:number))
expect(results).not_to include(excluded)
end
@@ -384,10 +384,10 @@
# https://github.com/Casecommons/pg_search/issues/14
it "supports queries with periods" do
- included = Parent.create!(name: 'bar.foo')
- excluded = Parent.create!(name: 'foo.bar')
+ included = Parent.create!(name: "bar.foo")
+ excluded = Parent.create!(name: "foo.bar")
- results = Parent.search_name('bar.foo').includes(:children)
+ results = Parent.search_name("bar.foo").includes(:children)
results.to_a
expect(results).to include(included)
@@ -477,7 +477,7 @@
Position.create!(company_id: company.id, title: "penn 1")
]
- results = company.positions.search('teller 1')
+ results = company.positions.search("teller 1")
expect(results).to include(*included)
expect(results).not_to include(*excluded)
diff --git a/spec/integration/pg_search_spec.rb b/spec/integration/pg_search_spec.rb
index 64a86d7e..b19ff0e8 100644
--- a/spec/integration/pg_search_spec.rb
+++ b/spec/integration/pg_search_spec.rb
@@ -6,10 +6,10 @@
describe "an Active Record model which includes PgSearch" do
with_model :ModelWithPgSearch do
table do |t|
- t.string 'title'
- t.text 'content'
- t.integer 'parent_model_id'
- t.integer 'importance'
+ t.string "title"
+ t.text "content"
+ t.integer "parent_model_id"
+ t.integer "importance"
end
model do
@@ -39,26 +39,38 @@
context "when passed a lambda" do
it "builds a dynamic scope" do
ModelWithPgSearch.pg_search_scope :search_title_or_content,
- lambda { |query, pick_content|
- {
- query: query.gsub("-remove-", ""),
- against: pick_content ? :content : :title
- }
- }
+ lambda { |query, pick_content|
+ {
+ query: query.gsub("-remove-", ""),
+ against: pick_content ? :content : :title
+ }
+ }
- included = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
- excluded = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
+ included = ModelWithPgSearch.create!(title: "foo", content: "bar")
+ ModelWithPgSearch.create!(title: "bar", content: "foo")
- expect(ModelWithPgSearch.search_title_or_content('fo-remove-o', false)).to eq([included])
- expect(ModelWithPgSearch.search_title_or_content('b-remove-ar', true)).to eq([included])
+ expect(ModelWithPgSearch.search_title_or_content("fo-remove-o", false)).to eq([included])
+ expect(ModelWithPgSearch.search_title_or_content("b-remove-ar", true)).to eq([included])
+ end
+ end
+
+ context "when passed an invalid argument" do
+ it "builds a dynamic scope" do
+ expect {
+ ModelWithPgSearch.pg_search_scope :search_title_or_content, :some_symbol
+ }.to(
+ raise_exception(ArgumentError).with_message(
+ "pg_search_scope expects a Hash or Proc"
+ )
+ )
end
end
context "when an unknown option is passed in" do
it "raises an exception when invoked" do
ModelWithPgSearch.pg_search_scope :with_unknown_option,
- against: :content,
- foo: :bar
+ against: :content,
+ foo: :bar
expect {
ModelWithPgSearch.with_unknown_option("foo")
@@ -68,7 +80,7 @@
context "with a lambda" do
it "raises an exception when invoked" do
ModelWithPgSearch.pg_search_scope :with_unknown_option,
- ->(*) { { against: :content, foo: :bar } }
+ ->(*) { {against: :content, foo: :bar} }
expect {
ModelWithPgSearch.with_unknown_option("foo")
@@ -80,8 +92,8 @@
context "when an unknown :using is passed" do
it "raises an exception when invoked" do
ModelWithPgSearch.pg_search_scope :with_unknown_using,
- against: :content,
- using: :foo
+ against: :content,
+ using: :foo
expect {
ModelWithPgSearch.with_unknown_using("foo")
@@ -91,7 +103,7 @@
context "with a lambda" do
it "raises an exception when invoked" do
ModelWithPgSearch.pg_search_scope :with_unknown_using,
- ->(*) { { against: :content, using: :foo } }
+ ->(*) { {against: :content, using: :foo} }
expect {
ModelWithPgSearch.with_unknown_using("foo")
@@ -103,8 +115,8 @@
context "when an unknown :ignoring is passed" do
it "raises an exception when invoked" do
ModelWithPgSearch.pg_search_scope :with_unknown_ignoring,
- against: :content,
- ignoring: :foo
+ against: :content,
+ ignoring: :foo
expect {
ModelWithPgSearch.with_unknown_ignoring("foo")
@@ -114,7 +126,7 @@
context "with a lambda" do
it "raises an exception when invoked" do
ModelWithPgSearch.pg_search_scope :with_unknown_ignoring,
- ->(*) { { against: :content, ignoring: :foo } }
+ ->(*) { {against: :content, ignoring: :foo} }
expect {
ModelWithPgSearch.with_unknown_ignoring("foo")
@@ -168,15 +180,15 @@
context "when chained after a select() scope" do
it "honors the select" do
- included = ModelWithPgSearch.create!(content: 'foo', title: 'bar')
- excluded = ModelWithPgSearch.create!(content: 'bar', title: 'foo')
+ included = ModelWithPgSearch.create!(content: "foo", title: "bar")
+ excluded = ModelWithPgSearch.create!(content: "bar", title: "foo")
- results = ModelWithPgSearch.select('id, title').search_content('foo')
+ results = ModelWithPgSearch.select("id, title").search_content("foo")
expect(results).to include(included)
expect(results).not_to include(excluded)
- expect(results.first.attributes.key?('content')).to be false
+ expect(results.first.attributes.key?("content")).to be false
expect(results.select { |record| record.title == "bar" }).to eq [included]
expect(results.reject { |record| record.title == "bar" }).to be_empty
@@ -185,15 +197,15 @@
context "when chained before a select() scope" do
it "honors the select" do
- included = ModelWithPgSearch.create!(content: 'foo', title: 'bar')
- excluded = ModelWithPgSearch.create!(content: 'bar', title: 'foo')
+ included = ModelWithPgSearch.create!(content: "foo", title: "bar")
+ excluded = ModelWithPgSearch.create!(content: "bar", title: "foo")
- results = ModelWithPgSearch.search_content('foo').select('id, title')
+ results = ModelWithPgSearch.search_content("foo").select("id, title")
expect(results).to include(included)
expect(results).not_to include(excluded)
- expect(results.first.attributes.key?('content')).to be false
+ expect(results.first.attributes.key?("content")).to be false
expect(results.select { |record| record.title == "bar" }).to eq [included]
expect(results.reject { |record| record.title == "bar" }).to be_empty
@@ -202,15 +214,15 @@
context "when surrouned by select() scopes" do
it "honors the select" do
- included = ModelWithPgSearch.create!(content: 'foo', title: 'bar')
- excluded = ModelWithPgSearch.create!(content: 'bar', title: 'foo')
+ included = ModelWithPgSearch.create!(content: "foo", title: "bar")
+ excluded = ModelWithPgSearch.create!(content: "bar", title: "foo")
- results = ModelWithPgSearch.select('id').search_content('foo').select('title')
+ results = ModelWithPgSearch.select("id").search_content("foo").select("title")
expect(results).to include(included)
expect(results).not_to include(excluded)
- expect(results.first.attributes.key?('content')).to be false
+ expect(results.first.attributes.key?("content")).to be false
expect(results.select { |record| record.title == "bar" }).to eq [included]
expect(results.reject { |record| record.title == "bar" }).to be_empty
@@ -241,7 +253,7 @@
has_many :houses
pg_search_scope :named, against: [:name]
scope :with_house_in_city, lambda { |city|
- joins(:houses).where(House.table_name.to_sym => { city: city })
+ joins(:houses).where(House.table_name.to_sym => {city: city})
}
scope :house_search_city, lambda { |query|
joins(:houses).merge(House.search_city(query))
@@ -285,7 +297,7 @@
context "when chaining merged scopes" do
it "does not raise an exception" do
- relation = Person.named('foo').house_search_city('bar')
+ relation = Person.named("foo").house_search_city("bar")
expect { relation.to_a }.not_to raise_error
end
@@ -298,49 +310,49 @@
end
it "does not raise an exception" do
- relation = ModelWithPgSearch.search_content('foo').search_title('bar')
+ relation = ModelWithPgSearch.search_content("foo").search_title("bar")
expect { relation.to_a }.not_to raise_error
end
end
it "returns an empty array when a blank query is passed in" do
- ModelWithPgSearch.create!(content: 'foo')
+ ModelWithPgSearch.create!(content: "foo")
- results = ModelWithPgSearch.search_content('')
+ results = ModelWithPgSearch.search_content("")
expect(results).to eq([])
end
it "returns rows where the column contains the term in the query" do
- included = ModelWithPgSearch.create!(content: 'foo')
- excluded = ModelWithPgSearch.create!(content: 'bar')
+ included = ModelWithPgSearch.create!(content: "foo")
+ excluded = ModelWithPgSearch.create!(content: "bar")
- results = ModelWithPgSearch.search_content('foo')
+ results = ModelWithPgSearch.search_content("foo")
expect(results).to include(included)
expect(results).not_to include(excluded)
end
it "returns the correct count" do
- ModelWithPgSearch.create!(content: 'foo')
- ModelWithPgSearch.create!(content: 'bar')
+ ModelWithPgSearch.create!(content: "foo")
+ ModelWithPgSearch.create!(content: "bar")
- results = ModelWithPgSearch.search_content('foo')
+ results = ModelWithPgSearch.search_content("foo")
expect(results.count).to eq 1
end
it "returns the correct count(:all)" do
- ModelWithPgSearch.create!(content: 'foo')
- ModelWithPgSearch.create!(content: 'bar')
+ ModelWithPgSearch.create!(content: "foo")
+ ModelWithPgSearch.create!(content: "bar")
- results = ModelWithPgSearch.search_content('foo')
+ results = ModelWithPgSearch.search_content("foo")
expect(results.count(:all)).to eq 1
end
it "supports #select" do
- record = ModelWithPgSearch.create!(content: 'foo')
- other_record = ModelWithPgSearch.create!(content: 'bar')
+ record = ModelWithPgSearch.create!(content: "foo")
+ ModelWithPgSearch.create!(content: "bar")
- records_with_only_id = ModelWithPgSearch.search_content('foo').select('id')
+ records_with_only_id = ModelWithPgSearch.search_content("foo").select("id")
expect(records_with_only_id.length).to eq 1
returned_record = records_with_only_id.first
@@ -349,36 +361,36 @@
end
it "supports #pluck" do
- record = ModelWithPgSearch.create!(content: 'foo')
- other_record = ModelWithPgSearch.create!(content: 'bar')
+ record = ModelWithPgSearch.create!(content: "foo")
+ ModelWithPgSearch.create!(content: "bar")
- ids = ModelWithPgSearch.search_content('foo').pluck('id')
+ ids = ModelWithPgSearch.search_content("foo").pluck("id")
expect(ids).to eq [record.id]
end
it "supports adding where clauses using the pg_search.rank" do
- once = ModelWithPgSearch.create!(content: 'foo bar')
- twice = ModelWithPgSearch.create!(content: 'foo foo')
+ ModelWithPgSearch.create!(content: "foo bar")
+ twice = ModelWithPgSearch.create!(content: "foo foo")
- records = ModelWithPgSearch.search_content('foo')
- .where("#{PgSearch::Configuration.alias(ModelWithPgSearch.table_name)}.rank > 0.07")
+ records = ModelWithPgSearch.search_content("foo")
+ .where("#{PgSearch::Configuration.alias(ModelWithPgSearch.table_name)}.rank > 0.07")
expect(records).to eq [twice]
end
it "returns rows where the column contains all the terms in the query in any order" do
- included = [ModelWithPgSearch.create!(content: 'foo bar'),
- ModelWithPgSearch.create!(content: 'bar foo')]
- excluded = ModelWithPgSearch.create!(content: 'foo')
+ included = [ModelWithPgSearch.create!(content: "foo bar"),
+ ModelWithPgSearch.create!(content: "bar foo")]
+ excluded = ModelWithPgSearch.create!(content: "foo")
- results = ModelWithPgSearch.search_content('foo bar')
+ results = ModelWithPgSearch.search_content("foo bar")
expect(results).to match_array(included)
expect(results).not_to include(excluded)
end
it "returns rows that match the query but not its case" do
included = [ModelWithPgSearch.create!(content: "foo"),
- ModelWithPgSearch.create!(content: "FOO")]
+ ModelWithPgSearch.create!(content: "FOO")]
results = ModelWithPgSearch.search_content("Foo")
expect(results).to match_array(included)
@@ -397,8 +409,8 @@
end
it "returns rows that match the query but not rows that are prefixed by the query" do
- included = ModelWithPgSearch.create!(content: 'pre')
- excluded = ModelWithPgSearch.create!(content: 'prefix')
+ included = ModelWithPgSearch.create!(content: "pre")
+ excluded = ModelWithPgSearch.create!(content: "prefix")
results = ModelWithPgSearch.search_content("pre")
expect(results).to eq([included])
@@ -407,36 +419,36 @@
it "returns rows that match the query exactly and not those that match the query when stemmed by the default english dictionary" do
included = ModelWithPgSearch.create!(content: "jumped")
- excluded = [ModelWithPgSearch.create!(content: "jump"),
- ModelWithPgSearch.create!(content: "jumping")]
+ ModelWithPgSearch.create!(content: "jump")
+ ModelWithPgSearch.create!(content: "jumping")
results = ModelWithPgSearch.search_content("jumped")
expect(results).to eq([included])
end
it "returns rows that match sorted by rank" do
- loser = ModelWithPgSearch.create!(content: 'foo')
- winner = ModelWithPgSearch.create!(content: 'foo foo')
+ loser = ModelWithPgSearch.create!(content: "foo")
+ winner = ModelWithPgSearch.create!(content: "foo foo")
results = ModelWithPgSearch.search_content("foo").with_pg_search_rank
expect(results[0].pg_search_rank).to be > results[1].pg_search_rank
expect(results).to eq([winner, loser])
end
- it 'preserves column selection when with_pg_search_rank is chained after a select()' do
- loser = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
+ it "preserves column selection when with_pg_search_rank is chained after a select()" do
+ ModelWithPgSearch.create!(title: "foo", content: "bar")
- results = ModelWithPgSearch.search_content('bar').select(:content).with_pg_search_rank
+ results = ModelWithPgSearch.search_content("bar").select(:content).with_pg_search_rank
expect(results.length).to be 1
- expect(results.first.as_json.keys).to contain_exactly('id', 'content', 'pg_search_rank')
+ expect(results.first.as_json.keys).to contain_exactly("id", "content", "pg_search_rank")
end
- it 'allows pg_search_rank along with a join' do
+ it "allows pg_search_rank along with a join" do
parent_1 = ParentModel.create!(id: 98)
parent_2 = ParentModel.create!(id: 99)
- loser = ModelWithPgSearch.create!(content: 'foo', parent_model: parent_2)
- winner = ModelWithPgSearch.create!(content: 'foo foo', parent_model: parent_1)
+ loser = ModelWithPgSearch.create!(content: "foo", parent_model: parent_2)
+ winner = ModelWithPgSearch.create!(content: "foo foo", parent_model: parent_1)
results = ModelWithPgSearch.joins(:parent_model).merge(ParentModel.active).search_content("foo").with_pg_search_rank
expect(results.map(&:id)).to eq [winner.id, loser.id]
@@ -445,8 +457,8 @@
end
it "returns results that match sorted by primary key for records that rank the same" do
- sorted_results = [ModelWithPgSearch.create!(content: 'foo'),
- ModelWithPgSearch.create!(content: 'foo')].sort_by(&:id)
+ sorted_results = [ModelWithPgSearch.create!(content: "foo"),
+ ModelWithPgSearch.create!(content: "foo")].sort_by(&:id)
results = ModelWithPgSearch.search_content("foo")
expect(results).to eq(sorted_results)
@@ -454,16 +466,16 @@
it "returns results that match a query with multiple space-separated search terms" do
included = [
- ModelWithPgSearch.create!(content: 'foo bar'),
- ModelWithPgSearch.create!(content: 'bar foo'),
- ModelWithPgSearch.create!(content: 'bar foo baz')
+ ModelWithPgSearch.create!(content: "foo bar"),
+ ModelWithPgSearch.create!(content: "bar foo"),
+ ModelWithPgSearch.create!(content: "bar foo baz")
]
excluded = [
- ModelWithPgSearch.create!(content: 'foo'),
- ModelWithPgSearch.create!(content: 'foo baz')
+ ModelWithPgSearch.create!(content: "foo"),
+ ModelWithPgSearch.create!(content: "foo baz")
]
- results = ModelWithPgSearch.search_content('foo bar')
+ results = ModelWithPgSearch.search_content("foo bar")
expect(results).to match_array(included)
expect(results).not_to include(excluded)
end
@@ -493,7 +505,7 @@
# WARNING: searching timestamps is not something PostgreSQL
# full-text search is good at. Use at your own risk.
pg_search_scope :search_timestamps,
- against: %i[created_at updated_at]
+ against: %i[created_at updated_at]
end
end
@@ -514,15 +526,15 @@
it "returns rows whose columns contain all of the terms in the query across columns" do
included = [
- ModelWithPgSearch.create!(title: 'foo', content: 'bar'),
- ModelWithPgSearch.create!(title: 'bar', content: 'foo')
+ ModelWithPgSearch.create!(title: "foo", content: "bar"),
+ ModelWithPgSearch.create!(title: "bar", content: "foo")
]
excluded = [
- ModelWithPgSearch.create!(title: 'foo', content: 'foo'),
- ModelWithPgSearch.create!(title: 'bar', content: 'bar')
+ ModelWithPgSearch.create!(title: "foo", content: "foo"),
+ ModelWithPgSearch.create!(title: "bar", content: "bar")
]
- results = ModelWithPgSearch.search_title_and_content('foo bar')
+ results = ModelWithPgSearch.search_title_and_content("foo bar")
expect(results).to match_array(included)
excluded.each do |result|
@@ -531,17 +543,17 @@
end
it "returns rows where at one column contains all of the terms in the query and another does not" do
- in_title = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
- in_content = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
+ in_title = ModelWithPgSearch.create!(title: "foo", content: "bar")
+ in_content = ModelWithPgSearch.create!(title: "bar", content: "foo")
- results = ModelWithPgSearch.search_title_and_content('foo')
- expect(results).to match_array([in_title, in_content])
+ results = ModelWithPgSearch.search_title_and_content("foo")
+ expect(results).to contain_exactly(in_title, in_content)
end
# Searching with a NULL column will prevent any matches unless we coalesce it.
it "returns rows where at one column contains all of the terms in the query and another is NULL" do
- included = ModelWithPgSearch.create!(title: 'foo', content: nil)
- results = ModelWithPgSearch.search_title_and_content('foo')
+ included = ModelWithPgSearch.create!(title: "foo", content: nil)
+ results = ModelWithPgSearch.search_title_and_content("foo")
expect(results).to eq([included])
end
end
@@ -552,21 +564,21 @@
end
it "returns rows where one searchable column and the query share enough trigrams" do
- included = ModelWithPgSearch.create!(title: 'abcdefghijkl', content: nil)
- results = ModelWithPgSearch.with_trigrams('cdefhijkl')
+ included = ModelWithPgSearch.create!(title: "abcdefghijkl", content: nil)
+ results = ModelWithPgSearch.with_trigrams("cdefhijkl")
expect(results).to eq([included])
end
it "returns rows where multiple searchable columns and the query share enough trigrams" do
- included = ModelWithPgSearch.create!(title: 'abcdef', content: 'ghijkl')
- results = ModelWithPgSearch.with_trigrams('cdefhijkl')
+ included = ModelWithPgSearch.create!(title: "abcdef", content: "ghijkl")
+ results = ModelWithPgSearch.with_trigrams("cdefhijkl")
expect(results).to eq([included])
end
context "when a threshold is specified" do
before do
- ModelWithPgSearch.pg_search_scope :with_strict_trigrams, against: %i[title content], using: { trigram: { threshold: 0.5 } }
- ModelWithPgSearch.pg_search_scope :with_permissive_trigrams, against: %i[title content], using: { trigram: { threshold: 0.1 } }
+ ModelWithPgSearch.pg_search_scope :with_strict_trigrams, against: %i[title content], using: {trigram: {threshold: 0.5}}
+ ModelWithPgSearch.pg_search_scope :with_permissive_trigrams, against: %i[title content], using: {trigram: {threshold: 0.1}}
end
it "uses the threshold in the trigram expression" do
@@ -591,16 +603,16 @@
context "when using tsearch" do
before do
ModelWithPgSearch.pg_search_scope :search_title_with_prefixes,
- against: :title,
- using: {
- tsearch: { prefix: true }
- }
+ against: :title,
+ using: {
+ tsearch: {prefix: true}
+ }
end
context "with prefix: true" do
it "returns rows that match the query and that are prefixed by the query" do
- included = ModelWithPgSearch.create!(title: 'prefix')
- excluded = ModelWithPgSearch.create!(title: 'postfix')
+ included = ModelWithPgSearch.create!(title: "prefix")
+ excluded = ModelWithPgSearch.create!(title: "postfix")
results = ModelWithPgSearch.search_title_with_prefixes("pre")
expect(results).to eq([included])
@@ -608,8 +620,8 @@
end
it "returns rows that match the query when the query has a hyphen" do
- included = ModelWithPgSearch.create!(title: 'foo-bar')
- excluded = ModelWithPgSearch.create!(title: 'foo bar')
+ included = ModelWithPgSearch.create!(title: "foo-bar")
+ excluded = ModelWithPgSearch.create!(title: "foo bar")
results = ModelWithPgSearch.search_title_with_prefixes("foo-bar")
expect(results).to include(included)
@@ -620,16 +632,16 @@
context "with the english dictionary" do
before do
ModelWithPgSearch.pg_search_scope :search_content_with_english,
- against: :content,
- using: {
- tsearch: { dictionary: :english }
- }
+ against: :content,
+ using: {
+ tsearch: {dictionary: :english}
+ }
end
it "returns rows that match the query when stemmed by the english dictionary" do
included = [ModelWithPgSearch.create!(content: "jump"),
- ModelWithPgSearch.create!(content: "jumped"),
- ModelWithPgSearch.create!(content: "jumping")]
+ ModelWithPgSearch.create!(content: "jumped"),
+ ModelWithPgSearch.create!(content: "jumping")]
results = ModelWithPgSearch.search_content_with_english("jump")
expect(results).to match_array(included)
@@ -639,14 +651,14 @@
describe "highlighting" do
before do
["Strip Down", "Down", "Down and Out", "Won't Let You Down"].each do |name|
- ModelWithPgSearch.create! title: 'Just a title', content: name
+ ModelWithPgSearch.create! title: "Just a title", content: name
end
end
context "with highlight turned on" do
before do
ModelWithPgSearch.pg_search_scope :search_content,
- against: :content
+ against: :content
end
it "adds a #pg_search_highlight method to each returned model record" do
@@ -661,31 +673,31 @@
expect(result.pg_search_highlight).to eq("Won't Let You Down")
end
- it 'preserves column selection when with_pg_search_highlight is chained after a select()' do
+ it "preserves column selection when with_pg_search_highlight is chained after a select()" do
result = ModelWithPgSearch.search_content("Let").select(:content).with_pg_search_highlight.first
- expect(result.as_json.keys).to contain_exactly('id', 'content', 'pg_search_highlight')
+ expect(result.as_json.keys).to contain_exactly("id", "content", "pg_search_highlight")
end
end
context "with custom highlighting options" do
before do
- ModelWithPgSearch.create! content: "#{'text ' * 2}Let #{'text ' * 2}Let #{'text ' * 2}"
+ ModelWithPgSearch.create! content: "#{"text " * 2}Let #{"text " * 2}Let #{"text " * 2}"
ModelWithPgSearch.pg_search_scope :search_content,
- against: :content,
- using: {
- tsearch: {
- highlight: {
- StartSel: '',
- StopSel: '',
- FragmentDelimiter: '',
- MaxFragments: 2,
- MaxWords: 2,
- MinWords: 1
- }
- }
- }
+ against: :content,
+ using: {
+ tsearch: {
+ highlight: {
+ StartSel: '',
+ StopSel: "",
+ FragmentDelimiter: '',
+ MaxFragments: 2,
+ MaxWords: 2,
+ MinWords: 1
+ }
+ }
+ }
end
it "applies the options to the excerpts" do
@@ -714,10 +726,10 @@
context "with a normalization specified" do
before do
ModelWithPgSearch.pg_search_scope :search_content_with_normalization,
- against: :content,
- using: {
- tsearch: { normalization: 2 }
- }
+ against: :content,
+ using: {
+ tsearch: {normalization: 2}
+ }
end
it "ranks the results for documents with less text higher" do
@@ -731,8 +743,8 @@
context "with no normalization" do
before do
ModelWithPgSearch.pg_search_scope :search_content_without_normalization,
- against: :content,
- using: :tsearch
+ against: :content,
+ using: :tsearch
end
it "ranks the results equally" do
@@ -747,14 +759,14 @@
context "when against columns ranked with arrays" do
before do
ModelWithPgSearch.pg_search_scope :search_weighted_by_array_of_arrays,
- against: [[:content, 'B'], [:title, 'A']]
+ against: [[:content, "B"], [:title, "A"]]
end
it "returns results sorted by weighted rank" do
- loser = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
- winner = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
+ loser = ModelWithPgSearch.create!(title: "bar", content: "foo")
+ winner = ModelWithPgSearch.create!(title: "foo", content: "bar")
- results = ModelWithPgSearch.search_weighted_by_array_of_arrays('foo').with_pg_search_rank
+ results = ModelWithPgSearch.search_weighted_by_array_of_arrays("foo").with_pg_search_rank
expect(results[0].pg_search_rank).to be > results[1].pg_search_rank
expect(results).to eq([winner, loser])
end
@@ -763,14 +775,14 @@
context "when against columns ranked with a hash" do
before do
ModelWithPgSearch.pg_search_scope :search_weighted_by_hash,
- against: { content: 'B', title: 'A' }
+ against: {content: "B", title: "A"}
end
it "returns results sorted by weighted rank" do
- loser = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
- winner = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
+ loser = ModelWithPgSearch.create!(title: "bar", content: "foo")
+ winner = ModelWithPgSearch.create!(title: "foo", content: "bar")
- results = ModelWithPgSearch.search_weighted_by_hash('foo').with_pg_search_rank
+ results = ModelWithPgSearch.search_weighted_by_hash("foo").with_pg_search_rank
expect(results[0].pg_search_rank).to be > results[1].pg_search_rank
expect(results).to eq([winner, loser])
end
@@ -779,14 +791,14 @@
context "when against columns of which only some are ranked" do
before do
ModelWithPgSearch.pg_search_scope :search_weighted,
- against: [:content, [:title, 'A']]
+ against: [:content, [:title, "A"]]
end
it "returns results sorted by weighted rank using an implied low rank for unranked columns" do
- loser = ModelWithPgSearch.create!(title: 'bar', content: 'foo')
- winner = ModelWithPgSearch.create!(title: 'foo', content: 'bar')
+ loser = ModelWithPgSearch.create!(title: "bar", content: "foo")
+ winner = ModelWithPgSearch.create!(title: "foo", content: "bar")
- results = ModelWithPgSearch.search_weighted('foo').with_pg_search_rank
+ results = ModelWithPgSearch.search_weighted("foo").with_pg_search_rank
expect(results[0].pg_search_rank).to be > results[1].pg_search_rank
expect(results).to eq([winner, loser])
end
@@ -795,17 +807,17 @@
context "when searching any_word option" do
before do
ModelWithPgSearch.pg_search_scope :search_title_with_any_word,
- against: :title,
- using: {
- tsearch: { any_word: true }
- }
+ against: :title,
+ using: {
+ tsearch: {any_word: true}
+ }
ModelWithPgSearch.pg_search_scope :search_title_with_all_words,
- against: :title
+ against: :title
end
it "returns all results containing any word in their title" do
- numbers = %w[one two three four].map { |number| ModelWithPgSearch.create!(title: number) }
+ %w[one two three four].map { |number| ModelWithPgSearch.create!(title: number) }
results = ModelWithPgSearch.search_title_with_any_word("one two three four")
@@ -820,10 +832,10 @@
context "with :negation" do
before do
ModelWithPgSearch.pg_search_scope :search_with_negation,
- against: :title,
- using: {
- tsearch: { negation: true }
- }
+ against: :title,
+ using: {
+ tsearch: {negation: true}
+ }
end
it "doesn't return results that contain terms prepended with '!'" do
@@ -847,10 +859,10 @@
context "without :negation" do
before do
ModelWithPgSearch.pg_search_scope :search_without_negation,
- against: :title,
- using: {
- tsearch: {}
- }
+ against: :title,
+ using: {
+ tsearch: {}
+ }
end
it "return results that contain terms prepended with '!'" do
@@ -873,38 +885,37 @@
context "when using dmetaphone" do
before do
ModelWithPgSearch.pg_search_scope :with_dmetaphones,
- against: %i[title content],
- using: :dmetaphone
+ against: %i[title content],
+ using: :dmetaphone
end
it "returns rows where one searchable column and the query share enough dmetaphones" do
- included = ModelWithPgSearch.create!(title: 'Geoff', content: nil)
- excluded = ModelWithPgSearch.create!(title: 'Bob', content: nil)
- results = ModelWithPgSearch.with_dmetaphones('Jeff')
+ included = ModelWithPgSearch.create!(title: "Geoff", content: nil)
+ ModelWithPgSearch.create!(title: "Bob", content: nil)
+ results = ModelWithPgSearch.with_dmetaphones("Jeff")
expect(results).to eq([included])
end
it "returns rows where multiple searchable columns and the query share enough dmetaphones" do
- included = ModelWithPgSearch.create!(title: 'Geoff', content: 'George')
- excluded = ModelWithPgSearch.create!(title: 'Bob', content: 'Jones')
- results = ModelWithPgSearch.with_dmetaphones('Jeff Jorge')
+ included = ModelWithPgSearch.create!(title: "Geoff", content: "George")
+ ModelWithPgSearch.create!(title: "Bob", content: "Jones")
+ results = ModelWithPgSearch.with_dmetaphones("Jeff Jorge")
expect(results).to eq([included])
end
it "returns rows that match dmetaphones that are English stopwords" do
- included = ModelWithPgSearch.create!(title: 'White', content: nil)
- excluded = ModelWithPgSearch.create!(title: 'Black', content: nil)
- results = ModelWithPgSearch.with_dmetaphones('Wight')
+ included = ModelWithPgSearch.create!(title: "White", content: nil)
+ ModelWithPgSearch.create!(title: "Black", content: nil)
+ results = ModelWithPgSearch.with_dmetaphones("Wight")
expect(results).to eq([included])
end
it "can handle terms that do not have a dmetaphone equivalent" do
- term_with_blank_metaphone = "w"
-
- included = ModelWithPgSearch.create!(title: 'White', content: nil)
- excluded = ModelWithPgSearch.create!(title: 'Black', content: nil)
+ included = ModelWithPgSearch.create!(title: "White", content: nil)
+ ModelWithPgSearch.create!(title: "Black", content: nil)
- results = ModelWithPgSearch.with_dmetaphones('Wight W')
+ # "W" does not have a dmetaphone equivalent
+ results = ModelWithPgSearch.with_dmetaphones("Wight W")
expect(results).to eq([included])
end
end
@@ -912,35 +923,35 @@
context "when using multiple features" do
before do
ModelWithPgSearch.pg_search_scope :with_tsearch,
- against: :title,
- using: [
- [:tsearch, { dictionary: 'english' }]
- ]
+ against: :title,
+ using: [
+ [:tsearch, {dictionary: "english"}]
+ ]
ModelWithPgSearch.pg_search_scope :with_trigram,
- against: :title,
- using: :trigram
+ against: :title,
+ using: :trigram
ModelWithPgSearch.pg_search_scope :with_trigram_and_ignoring_accents,
- against: :title,
- ignoring: :accents,
- using: :trigram
+ against: :title,
+ ignoring: :accents,
+ using: :trigram
ModelWithPgSearch.pg_search_scope :with_tsearch_and_trigram,
- against: :title,
- using: [
- [:tsearch, { dictionary: 'english' }],
- :trigram
- ]
+ against: :title,
+ using: [
+ [:tsearch, {dictionary: "english"}],
+ :trigram
+ ]
ModelWithPgSearch.pg_search_scope :complex_search,
- against: %i[content title],
- ignoring: :accents,
- using: {
- tsearch: { dictionary: 'english' },
- dmetaphone: {},
- trigram: {}
- }
+ against: %i[content title],
+ ignoring: :accents,
+ using: {
+ tsearch: {dictionary: "english"},
+ dmetaphone: {},
+ trigram: {}
+ }
end
it "returns rows that match using any of the features" do
@@ -982,13 +993,13 @@
end
context "with feature-specific configuration" do
- let(:tsearch_config) { { dictionary: 'english' } }
- let(:trigram_config) { { foo: 'bar' } }
+ let(:tsearch_config) { {dictionary: "english"} }
+ let(:trigram_config) { {foo: "bar"} }
before do
ModelWithPgSearch.pg_search_scope :with_tsearch_and_trigram_using_hash,
- against: :title,
- using: { tsearch: tsearch_config, trigram: trigram_config }
+ against: :title,
+ using: {tsearch: tsearch_config, trigram: trigram_config}
end
it "passes the custom configuration down to the specified feature" do
@@ -1029,8 +1040,8 @@
with_model :Post do
table do |t|
- t.text 'content'
- t.tsvector 'content_tsvector'
+ t.text "content"
+ t.tsvector "content_tsvector"
end
model do
@@ -1039,8 +1050,8 @@
end
end
- let!(:expected) { Post.create!(content: 'phooey') }
- let!(:unexpected) { Post.create!(content: 'longcat is looooooooong') }
+ let!(:expected) { Post.create!(content: "phooey") }
+ let!(:unexpected) { Post.create!(content: "longcat is looooooooong") }
before do
ActiveRecord::Base.connection.execute <<~SQL.squish
@@ -1048,17 +1059,17 @@
SET content_tsvector = to_tsvector('english'::regconfig, #{Post.quoted_table_name}."content")
SQL
- expected.comments.create(body: 'commentone')
- unexpected.comments.create(body: 'commentwo')
+ expected.comments.create(body: "commentone")
+ unexpected.comments.create(body: "commentwo")
Post.pg_search_scope :search_by_content_with_tsvector,
- associated_against: { comments: [:body] },
- using: {
- tsearch: {
- tsvector_column: 'content_tsvector',
- dictionary: 'english'
- }
- }
+ associated_against: {comments: [:body]},
+ using: {
+ tsearch: {
+ tsvector_column: "content_tsvector",
+ dictionary: "english"
+ }
+ }
end
it "finds by the tsvector column" do
@@ -1069,7 +1080,7 @@
expect(Post.search_by_content_with_tsvector("commentone").map(&:id)).to eq([expected.id])
end
- it 'finds by a combination of the two' do
+ it "finds by a combination of the two" do
expect(Post.search_by_content_with_tsvector("phooey commentone").map(&:id)).to eq([expected.id])
end
end
@@ -1183,7 +1194,7 @@
expected = "setweight(coalesce(#{ModelWithTsvector.quoted_table_name}.\"title_tsvector\", ''), 'A') || "\
"coalesce(#{ModelWithTsvector.quoted_table_name}.\"content_tsvector\", '') || "\
"setweight(coalesce(#{ModelWithTsvector.quoted_table_name}.\"message_tsvector\", ''), 'B')"
-
+
expect(ModelWithTsvector.search_by_multiple_tsvector_columns("something").to_sql).to include(expected)
end
end
@@ -1228,7 +1239,7 @@
with_model :AnotherModel do
table do |t|
t.string :content_tsvector # the type of the column doesn't matter
- t.belongs_to :model_with_tsvector
+ t.belongs_to :model_with_tsvector, index: { name: :boopilooopi_id_ix }
end
end
@@ -1248,7 +1259,7 @@
table do |t|
t.text 'content'
t.tsvector 'content_tsvector'
- t.belongs_to :model_with_tsvector
+ t.belongs_to :model_with_tsvector, index: { name: :hoopaboopa_id_ix }
end
end
@@ -1318,7 +1329,7 @@
table do |t|
t.text 'content'
t.tsvector 'content_tsvector'
- t.belongs_to :model_with_tsvector
+ t.belongs_to :model_with_tsvector, index: { name: :anoterh_model_id_ix }
end
end
@@ -1385,17 +1396,17 @@
include PgSearch::Model
pg_search_scope :search_by_multiple_tsvector_columns,
- against: ['content', 'message'],
- using: {
- tsearch: {
- tsvector_column: ['content_tsvector', 'message_tsvector'],
- dictionary: 'english'
- }
- }
+ against: ["content", "message"],
+ using: {
+ tsearch: {
+ tsvector_column: ["content_tsvector", "message_tsvector"],
+ dictionary: "english"
+ }
+ }
end
end
- it 'concats tsvector columns' do
+ it "concats tsvector columns" do
expected = "#{ModelWithTsvector.quoted_table_name}.\"content_tsvector\" || " \
"#{ModelWithTsvector.quoted_table_name}.\"message_tsvector\""
@@ -1406,17 +1417,17 @@
context "when using a tsvector column with" do
with_model :ModelWithTsvector do
table do |t|
- t.text 'content'
- t.tsvector 'content_tsvector'
+ t.text "content"
+ t.tsvector "content_tsvector"
end
model { include PgSearch::Model }
end
- let!(:expected) { ModelWithTsvector.create!(content: 'tiling is grouty') }
+ let!(:expected) { ModelWithTsvector.create!(content: "tiling is grouty") }
before do
- ModelWithTsvector.create!(content: 'longcat is looooooooong')
+ ModelWithTsvector.create!(content: "longcat is looooooooong")
ActiveRecord::Base.connection.execute <<~SQL.squish
UPDATE #{ModelWithTsvector.quoted_table_name}
@@ -1424,13 +1435,13 @@
SQL
ModelWithTsvector.pg_search_scope :search_by_content_with_tsvector,
- against: :content,
- using: {
- tsearch: {
- tsvector_column: 'content_tsvector',
- dictionary: 'english'
- }
- }
+ against: :content,
+ using: {
+ tsearch: {
+ tsvector_column: "content_tsvector",
+ dictionary: "english"
+ }
+ }
end
it "does not use to_tsvector in the query" do
@@ -1464,8 +1475,8 @@
context "when ignoring accents" do
before do
ModelWithPgSearch.pg_search_scope :search_title_without_accents,
- against: :title,
- ignoring: :accents
+ against: :title,
+ ignoring: :accents
end
it "returns rows that match the query but not its accents" do
@@ -1484,7 +1495,7 @@
let(:results) { ModelWithPgSearch.search_title_without_accents(term) }
before do
- ModelWithPgSearch.create!(title: 'FooBar')
+ ModelWithPgSearch.create!(title: "FooBar")
end
it "does not create an erroneous tsquery expression" do
@@ -1496,25 +1507,25 @@
context "when passed a :ranked_by expression" do
before do
ModelWithPgSearch.pg_search_scope :search_content_with_default_rank,
- against: :content
+ against: :content
ModelWithPgSearch.pg_search_scope :search_content_with_importance_as_rank,
- against: :content,
- ranked_by: "importance"
+ against: :content,
+ ranked_by: "importance"
ModelWithPgSearch.pg_search_scope :search_content_with_importance_as_rank_multiplier,
- against: :content,
- ranked_by: ":tsearch * importance"
+ against: :content,
+ ranked_by: ":tsearch * importance"
end
it "returns records with a rank attribute equal to the :ranked_by expression" do
- ModelWithPgSearch.create!(content: 'foo', importance: 10)
+ ModelWithPgSearch.create!(content: "foo", importance: 10)
results = ModelWithPgSearch.search_content_with_importance_as_rank("foo").with_pg_search_rank
expect(results.first.pg_search_rank).to eq(10)
end
it "substitutes :tsearch with the tsearch rank expression in the :ranked_by expression" do
- ModelWithPgSearch.create!(content: 'foo', importance: 10)
+ ModelWithPgSearch.create!(content: "foo", importance: 10)
tsearch_result =
ModelWithPgSearch.search_content_with_default_rank("foo").with_pg_search_rank.first
@@ -1523,8 +1534,8 @@
multiplied_result =
ModelWithPgSearch.search_content_with_importance_as_rank_multiplier("foo")
- .with_pg_search_rank
- .first
+ .with_pg_search_rank
+ .first
multiplied_rank = multiplied_result.pg_search_rank
@@ -1533,9 +1544,9 @@
it "returns results in descending order of the value of the rank expression" do
records = [
- ModelWithPgSearch.create!(content: 'foo', importance: 1),
- ModelWithPgSearch.create!(content: 'foo', importance: 3),
- ModelWithPgSearch.create!(content: 'foo', importance: 2)
+ ModelWithPgSearch.create!(content: "foo", importance: 1),
+ ModelWithPgSearch.create!(content: "foo", importance: 3),
+ ModelWithPgSearch.create!(content: "foo", importance: 2)
]
results = ModelWithPgSearch.search_content_with_importance_as_rank("foo")
@@ -1548,32 +1559,32 @@
before do
ModelWithPgSearch.pg_search_scope scope_name,
- against: :content,
- ranked_by: ":#{feature}"
+ against: :content,
+ ranked_by: ":#{feature}"
- ModelWithPgSearch.create!(content: 'foo')
+ ModelWithPgSearch.create!(content: "foo")
end
context "when .with_pg_search_rank is chained after" do
specify "its results respond to #pg_search_rank" do
- result = ModelWithPgSearch.send(scope_name, 'foo').with_pg_search_rank.first
+ result = ModelWithPgSearch.send(scope_name, "foo").with_pg_search_rank.first
expect(result).to respond_to(:pg_search_rank)
end
it "returns the rank when #pg_search_rank is called on a result" do
- results = ModelWithPgSearch.send(scope_name, 'foo').with_pg_search_rank
+ results = ModelWithPgSearch.send(scope_name, "foo").with_pg_search_rank
expect(results.first.pg_search_rank).to be_a Float
end
end
context "when .with_pg_search_rank is not chained after" do
specify "its results do not respond to #pg_search_rank" do
- result = ModelWithPgSearch.send(scope_name, 'foo').first
+ result = ModelWithPgSearch.send(scope_name, "foo").first
expect(result).not_to respond_to(:pg_search_rank)
end
it "raises PgSearch::PgSearchRankNotSelected when #pg_search_rank is called on a result" do
- result = ModelWithPgSearch.send(scope_name, 'foo').first
+ result = ModelWithPgSearch.send(scope_name, "foo").first
expect {
result.pg_search_rank
}.to raise_exception(PgSearch::PgSearchRankNotSelected)
@@ -1585,14 +1596,14 @@
context "when using the tsearch ranking algorithm" do
it "sorts results by the tsearch rank" do
ModelWithPgSearch.pg_search_scope :search_content_ranked_by_tsearch,
- using: :tsearch,
- against: :content,
- ranked_by: ":tsearch"
+ using: :tsearch,
+ against: :content,
+ ranked_by: ":tsearch"
- once = ModelWithPgSearch.create!(content: 'foo bar')
- twice = ModelWithPgSearch.create!(content: 'foo foo')
+ once = ModelWithPgSearch.create!(content: "foo bar")
+ twice = ModelWithPgSearch.create!(content: "foo foo")
- results = ModelWithPgSearch.search_content_ranked_by_tsearch('foo')
+ results = ModelWithPgSearch.search_content_ranked_by_tsearch("foo")
expect(results.find_index(twice)).to be < results.find_index(once)
end
end
@@ -1600,14 +1611,14 @@
context "when using the trigram ranking algorithm" do
it "sorts results by the trigram rank" do
ModelWithPgSearch.pg_search_scope :search_content_ranked_by_trigram,
- using: :trigram,
- against: :content,
- ranked_by: ":trigram"
+ using: :trigram,
+ against: :content,
+ ranked_by: ":trigram"
- close = ModelWithPgSearch.create!(content: 'abcdef')
- exact = ModelWithPgSearch.create!(content: 'abc')
+ close = ModelWithPgSearch.create!(content: "abcdef")
+ exact = ModelWithPgSearch.create!(content: "abc")
- results = ModelWithPgSearch.search_content_ranked_by_trigram('abc')
+ results = ModelWithPgSearch.search_content_ranked_by_trigram("abc")
expect(results.find_index(exact)).to be < results.find_index(close)
end
end
@@ -1615,14 +1626,14 @@
context "when using the dmetaphone ranking algorithm" do
it "sorts results by the dmetaphone rank" do
ModelWithPgSearch.pg_search_scope :search_content_ranked_by_dmetaphone,
- using: :dmetaphone,
- against: :content,
- ranked_by: ":dmetaphone"
+ using: :dmetaphone,
+ against: :content,
+ ranked_by: ":dmetaphone"
- once = ModelWithPgSearch.create!(content: 'Phoo Bar')
- twice = ModelWithPgSearch.create!(content: 'Phoo Fu')
+ once = ModelWithPgSearch.create!(content: "Phoo Bar")
+ twice = ModelWithPgSearch.create!(content: "Phoo Fu")
- results = ModelWithPgSearch.search_content_ranked_by_dmetaphone('foo')
+ results = ModelWithPgSearch.search_content_ranked_by_dmetaphone("foo")
expect(results.find_index(twice)).to be < results.find_index(once)
end
end
@@ -1631,17 +1642,17 @@
context "when there is a sort only feature" do
it "excludes that feature from the conditions, but uses it in the sorting" do
ModelWithPgSearch.pg_search_scope :search_content_ranked_by_dmetaphone,
- against: :content,
- using: {
- tsearch: { any_word: true, prefix: true },
- dmetaphone: { any_word: true, prefix: true, sort_only: true }
- },
- ranked_by: ":tsearch + (0.5 * :dmetaphone)"
+ against: :content,
+ using: {
+ tsearch: {any_word: true, prefix: true},
+ dmetaphone: {any_word: true, prefix: true, sort_only: true}
+ },
+ ranked_by: ":tsearch + (0.5 * :dmetaphone)"
exact = ModelWithPgSearch.create!(content: "ash hines")
one_exact_one_close = ModelWithPgSearch.create!(content: "ash heinz")
one_exact = ModelWithPgSearch.create!(content: "ash smith")
- one_close = ModelWithPgSearch.create!(content: "leigh heinz")
+ ModelWithPgSearch.create!(content: "leigh heinz")
results = ModelWithPgSearch.search_content_ranked_by_dmetaphone("ash hines")
expect(results).to eq [exact, one_exact_one_close, one_exact]
diff --git a/spec/integration/single_table_inheritance_spec.rb b/spec/integration/single_table_inheritance_spec.rb
index 8a068152..e4cb8668 100644
--- a/spec/integration/single_table_inheritance_spec.rb
+++ b/spec/integration/single_table_inheritance_spec.rb
@@ -6,8 +6,8 @@
context "with the standard type column" do
with_model :SuperclassModel do
table do |t|
- t.text 'content'
- t.string 'type'
+ t.text "content"
+ t.string "type"
end
model do
@@ -46,13 +46,13 @@
context "with a custom type column" do
with_model :SuperclassModel do
table do |t|
- t.text 'content'
- t.string 'custom_type'
+ t.text "content"
+ t.string "custom_type"
end
model do
include PgSearch::Model
- self.inheritance_column = 'custom_type'
+ self.inheritance_column = "custom_type"
pg_search_scope :search_content, against: :content
end
end
diff --git a/spec/lib/pg_search/configuration/association_spec.rb b/spec/lib/pg_search/configuration/association_spec.rb
index 6c7d79d2..05725e69 100644
--- a/spec/lib/pg_search/configuration/association_spec.rb
+++ b/spec/lib/pg_search/configuration/association_spec.rb
@@ -1,222 +1,143 @@
+# frozen_string_literal: true
+
require "spec_helper"
+# rubocop:disable RSpec/NestedGroups
describe PgSearch::Configuration::Association do
- context "through a belongs_to association" do
- with_model :AssociatedModel do
- table do |t|
- t.string "title"
- end
+ with_model :Avatar do
+ table do |t|
+ t.string :url
+ t.references :user
end
+ end
- with_model :Model do
- table do |t|
- t.string "title"
- t.belongs_to :another_model
- end
-
- model do
- include PgSearch
- belongs_to :another_model, :class_name => 'AssociatedModel'
-
- pg_search_scope :with_another, :associated_against => {:another_model => :title}
- end
- end
-
- let(:association) { described_class.new(Model, :another_model, :title) }
-
- describe "#table_name" do
- it "returns the table name for the associated model" do
- expect(association.table_name).to eq AssociatedModel.table_name
- end
+ with_model :User do
+ table do |t|
+ t.string :name
+ t.belongs_to :site
end
- describe "#join" do
- context "given any postgresql_version" do
- let(:column_select) do
- "\"#{association.table_name}\".\"title\""
- end
-
- let(:expected_sql) do
- <<-EOS.gsub(/\s+/, ' ').strip
- LEFT OUTER JOIN
- (SELECT model_id AS id,
- #{column_select} AS #{association.columns.first.alias}
- FROM \"#{Model.table_name}\"
- INNER JOIN \"#{association.table_name}\"
- ON \"#{association.table_name}\".\"id\" = \"#{Model.table_name}\".\"another_model_id\") #{association.subselect_alias}
- ON #{association.subselect_alias}.id = model_id
- EOS
- end
-
- it "returns the correct SQL join (v1)" do
- allow(Model.connection).to receive(:postgresql_version).and_return(1)
- expect(association.join("model_id")).to eq(expected_sql)
- end
+ model do
+ include PgSearch::Model
+ has_one :avatar, class_name: "Avatar"
+ belongs_to :site
- it "returns the correct SQL join (v100)" do
- allow(Model.connection).to receive(:postgresql_version).and_return(100_000)
- expect(association.join("model_id")).to eq(expected_sql)
- end
- end
- end
-
- describe "#subselect_alias" do
- it "returns a consistent string" do
- subselect_alias = association.subselect_alias
- expect(subselect_alias).to be_a String
- expect(association.subselect_alias).to eq subselect_alias
- end
+ pg_search_scope :with_avatar, associated_against: {avatar: :url}
+ pg_search_scope :with_site, associated_against: {site: :title}
end
end
- context "through a has_one association" do
- with_model :Model do
- table do |t|
- t.string "title"
- end
-
- model do
- include PgSearch
- has_one :another_model, :class_name => 'AssociatedModel', foreign_key: 'primary_model_id'
-
- pg_search_scope :with_another, :associated_against => {:another_model => :title}
- end
+ with_model :Site do
+ table do |t|
+ t.string :title
end
- with_model :AssociatedModel do
- table do |t|
- t.string "title"
- t.belongs_to :primary_model
- end
+ model do
+ include PgSearch::Model
+ has_many :users, class_name: "User"
- model do
- belongs_to :primary_model, :class_name => 'Model'
- end
+ pg_search_scope :with_users, associated_against: {users: :name}
end
+ end
- let(:association) { described_class.new(Model, :another_model, :title) }
+ context "with has_one" do
+ let(:association) { described_class.new(User, :avatar, :url) }
describe "#table_name" do
it "returns the table name for the associated model" do
- expect(association.table_name).to eq AssociatedModel.table_name
+ expect(association.table_name).to eq Avatar.table_name
end
end
describe "#join" do
- context "given any postgresql_version" do
- let(:column_select) do
- "\"#{association.table_name}\".\"title\""
- end
-
- let(:expected_sql) do
- <<-EOS.gsub(/\s+/, ' ').strip
- LEFT OUTER JOIN
- (SELECT model_id AS id,
- #{column_select} AS #{association.columns.first.alias}
- FROM \"#{Model.table_name}\"
- INNER JOIN \"#{association.table_name}\"
- ON \"#{association.table_name}\".\"primary_model_id\" = \"#{Model.table_name}\".\"id\") #{association.subselect_alias}
- ON #{association.subselect_alias}.id = model_id
- EOS
- end
-
- it "returns the correct SQL join (v1)" do
- allow(Model.connection).to receive(:postgresql_version).and_return(1)
- expect(association.join("model_id")).to eq(expected_sql)
- end
-
- it "returns the correct SQL join (v100)" do
- allow(Model.connection).to receive(:postgresql_version).and_return(100_000)
- expect(association.join("model_id")).to eq(expected_sql)
- end
+ let(:expected_sql) do
+ <<~SQL.squish
+ LEFT OUTER JOIN
+ (SELECT model_id AS id,
+ #{column_select} AS #{association.columns.first.alias}
+ FROM "#{User.table_name}"
+ INNER JOIN "#{association.table_name}"
+ ON "#{association.table_name}"."user_id" = "#{User.table_name}"."id") #{association.subselect_alias}
+ ON #{association.subselect_alias}.id = model_id
+ SQL
+ end
+ let(:column_select) do
+ "\"#{association.table_name}\".\"url\"::text"
end
- end
- describe "#subselect_alias" do
- it "returns a consistent string" do
- subselect_alias = association.subselect_alias
- expect(subselect_alias).to be_a String
- expect(association.subselect_alias).to eq subselect_alias
+ it "returns the correct SQL join" do
+ expect(association.join("model_id")).to eq(expected_sql)
end
end
end
- context "through a has_many association" do
- with_model :Model do
- table do |t|
- t.string "title"
- end
-
- model do
- include PgSearch
- has_many :associated_models, :class_name => 'AssociatedModel', foreign_key: 'primary_model_id'
+ context "with belongs_to" do
+ let(:association) { described_class.new(User, :site, :title) }
- pg_search_scope :with_another, :associated_against => {:another_model => :title}
+ describe "#table_name" do
+ it "returns the table name for the associated model" do
+ expect(association.table_name).to eq Site.table_name
end
end
- with_model :AssociatedModel do
- table do |t|
- t.string "title"
- t.belongs_to :primary_model
+ describe "#join" do
+ let(:expected_sql) do
+ <<~SQL.squish
+ LEFT OUTER JOIN
+ (SELECT model_id AS id,
+ #{column_select} AS #{association.columns.first.alias}
+ FROM "#{User.table_name}"
+ INNER JOIN "#{association.table_name}"
+ ON "#{association.table_name}"."id" = "#{User.table_name}"."site_id") #{association.subselect_alias}
+ ON #{association.subselect_alias}.id = model_id
+ SQL
+ end
+ let(:column_select) do
+ "\"#{association.table_name}\".\"title\"::text"
end
- model do
- belongs_to :primary_model, :class_name => 'Model'
+ it "returns the correct SQL join" do
+ expect(association.join("model_id")).to eq(expected_sql)
end
end
+ end
- let(:association) { described_class.new(Model, :associated_models, :title) }
+ context "with has_many" do
+ let(:association) { described_class.new(Site, :users, :name) }
describe "#table_name" do
it "returns the table name for the associated model" do
- expect(association.table_name).to eq AssociatedModel.table_name
+ expect(association.table_name).to eq User.table_name
end
end
describe "#join" do
let(:expected_sql) do
- <<-EOS.gsub(/\s+/, ' ').strip
+ <<~SQL.squish
LEFT OUTER JOIN
(SELECT model_id AS id,
- #{column_select} AS #{association.columns.first.alias}
- FROM \"#{Model.table_name}\"
- INNER JOIN \"#{association.table_name}\"
- ON \"#{association.table_name}\".\"primary_model_id\" = \"#{Model.table_name}\".\"id\"
+ string_agg("#{association.table_name}"."name"::text, ' ') AS #{association.columns.first.alias}
+ FROM "#{Site.table_name}"
+ INNER JOIN "#{association.table_name}"
+ ON "#{association.table_name}"."site_id" = "#{Site.table_name}"."id"
GROUP BY model_id) #{association.subselect_alias}
ON #{association.subselect_alias}.id = model_id
- EOS
+ SQL
end
- context "given postgresql_version 0..90_000" do
- let(:column_select) do
- "array_to_string(array_agg(\"#{association.table_name}\".\"title\"::text), ' ')"
- end
-
- it "returns the correct SQL join" do
- allow(Model.connection).to receive(:postgresql_version).and_return(1)
- expect(association.join("model_id")).to eq(expected_sql)
- end
+ it "returns the correct SQL join" do
+ expect(association.join("model_id")).to eq(expected_sql)
end
- context "given any other postgresql_version" do
- let(:column_select) do
- "string_agg(\"#{association.table_name}\".\"title\"::text, ' ')"
- end
-
- it "returns the correct SQL join" do
- allow(Model.connection).to receive(:postgresql_version).and_return(100_000)
- expect(association.join("model_id")).to eq(expected_sql)
+ describe "#subselect_alias" do
+ it "returns a consistent string" do
+ subselect_alias = association.subselect_alias
+ expect(subselect_alias).to be_a String
+ expect(association.subselect_alias).to eq subselect_alias
end
end
end
-
- describe "#subselect_alias" do
- it "returns a consistent string" do
- subselect_alias = association.subselect_alias
- expect(subselect_alias).to be_a String
- expect(association.subselect_alias).to eq subselect_alias
- end
- end
end
end
+
+# rubocop:enable RSpec/NestedGroups
diff --git a/spec/lib/pg_search/configuration/column_spec.rb b/spec/lib/pg_search/configuration/column_spec.rb
index 50f56b1e..9e933713 100644
--- a/spec/lib/pg_search/configuration/column_spec.rb
+++ b/spec/lib/pg_search/configuration/column_spec.rb
@@ -7,6 +7,7 @@
with_model :Model do
table do |t|
t.string :name
+ t.json :object
end
end
@@ -14,18 +15,29 @@
column = described_class.new("name", nil, Model)
expect(column.full_name).to eq(%(#{Model.quoted_table_name}."name"))
end
+
+ it "returns nested json attributes" do
+ column = described_class.new(Arel.sql("object->>'name'"), nil, Model)
+ expect(column.full_name).to eq(%(object->>'name'))
+ end
end
describe "#to_sql" do
with_model :Model do
table do |t|
t.string :name
+ t.json :object
end
end
it "returns an expression that casts the column to text and coalesces it with an empty string" do
column = described_class.new("name", nil, Model)
- expect(column.to_sql).to eq(%{coalesce(#{Model.quoted_table_name}."name"::text, '')})
+ expect(column.to_sql).to eq(%{coalesce((#{Model.quoted_table_name}."name")::text, '')})
+ end
+
+ it "returns an expression that casts the nested json attribute to text and coalesces it with an empty string" do
+ column = described_class.new(Arel.sql("object->>'name'"), nil, Model)
+ expect(column.to_sql).to eq(%{coalesce((object->>'name')::text, '')})
end
end
end
diff --git a/spec/lib/pg_search/configuration/foreign_column_spec.rb b/spec/lib/pg_search/configuration/foreign_column_spec.rb
index 105b8eba..3537a203 100644
--- a/spec/lib/pg_search/configuration/foreign_column_spec.rb
+++ b/spec/lib/pg_search/configuration/foreign_column_spec.rb
@@ -18,16 +18,16 @@
model do
include PgSearch::Model
- belongs_to :another_model, class_name: 'AssociatedModel'
+ belongs_to :another_model, class_name: "AssociatedModel"
- pg_search_scope :with_another, associated_against: { another_model: :title }
+ pg_search_scope :with_another, associated_against: {another_model: :title}
end
end
it "returns a consistent string" do
association = PgSearch::Configuration::Association.new(Model,
- :another_model,
- :title)
+ :another_model,
+ :title)
foreign_column = described_class.new("title", nil, Model, association)
column_alias = foreign_column.alias
diff --git a/spec/lib/pg_search/features/dmetaphone_spec.rb b/spec/lib/pg_search/features/dmetaphone_spec.rb
index 995f76b4..8327619d 100644
--- a/spec/lib/pg_search/features/dmetaphone_spec.rb
+++ b/spec/lib/pg_search/features/dmetaphone_spec.rb
@@ -23,7 +23,7 @@
feature = described_class.new(query, options, columns, Model, normalizer)
expect(feature.rank.to_sql).to eq(
- %{(ts_rank((to_tsvector('simple', pg_search_dmetaphone(coalesce(#{Model.quoted_table_name}."name"::text, ''))) || to_tsvector('simple', pg_search_dmetaphone(coalesce(#{Model.quoted_table_name}."content"::text, '')))), (to_tsquery('simple', ''' ' || pg_search_dmetaphone('query') || ' ''')), 0))}
+ %{(ts_rank((to_tsvector('simple', pg_search_dmetaphone(coalesce((#{Model.quoted_table_name}."name")::text, ''))) || to_tsvector('simple', pg_search_dmetaphone(coalesce((#{Model.quoted_table_name}."content")::text, '')))), (to_tsquery('simple', ''' ' || pg_search_dmetaphone('query') || ' ''')), 0))}
)
end
end
@@ -48,7 +48,7 @@
feature = described_class.new(query, options, columns, Model, normalizer)
expect(feature.conditions.to_sql).to eq(
- %{((to_tsvector('simple', pg_search_dmetaphone(coalesce(#{Model.quoted_table_name}."name"::text, ''))) || to_tsvector('simple', pg_search_dmetaphone(coalesce(#{Model.quoted_table_name}."content"::text, '')))) @@ (to_tsquery('simple', ''' ' || pg_search_dmetaphone('query') || ' ''')))}
+ %{((to_tsvector('simple', pg_search_dmetaphone(coalesce((#{Model.quoted_table_name}."name")::text, ''))) || to_tsvector('simple', pg_search_dmetaphone(coalesce((#{Model.quoted_table_name}."content")::text, '')))) @@ (to_tsquery('simple', ''' ' || pg_search_dmetaphone('query') || ' ''')))}
)
end
end
diff --git a/spec/lib/pg_search/features/trigram_spec.rb b/spec/lib/pg_search/features/trigram_spec.rb
index 995c5dae..5686043a 100644
--- a/spec/lib/pg_search/features/trigram_spec.rb
+++ b/spec/lib/pg_search/features/trigram_spec.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
-require 'spec_helper'
-require 'ostruct'
+require "spec_helper"
+require "ostruct"
# rubocop:disable RSpec/MultipleMemoizedHelpers, RSpec/NestedGroups
describe PgSearch::Features::Trigram do
subject(:feature) { described_class.new(query, options, columns, Model, normalizer) }
- let(:query) { 'lolwut' }
+ let(:query) { "lolwut" }
let(:options) { {} }
let(:columns) {
[
@@ -16,13 +16,13 @@
]
}
let(:normalizer) { PgSearch::Normalizer.new(config) }
- let(:config) { OpenStruct.new(ignore: []) } # rubocop:disable Style/OpenStructUse
+ let(:config) { OpenStruct.new(ignore: []) }
let(:coalesced_columns) do
<<~SQL.squish
- coalesce(#{Model.quoted_table_name}."name"::text, '')
+ coalesce((#{Model.quoted_table_name}."name")::text, '')
|| ' '
- || coalesce(#{Model.quoted_table_name}."content"::text, '')
+ || coalesce((#{Model.quoted_table_name}."content")::text, '')
SQL
end
@@ -33,15 +33,15 @@
end
end
- describe 'conditions' do
- it 'escapes the search document and query' do
+ describe "conditions" do
+ it "escapes the search document and query" do
config.ignore = []
expect(feature.conditions.to_sql).to eq("('#{query}' % (#{coalesced_columns}))")
end
- context 'when searching by word_similarity' do
+ context "when searching by word_similarity" do
let(:options) do
- { word_similarity: true }
+ {word_similarity: true}
end
it 'uses the "<%" operator when searching by word_similarity' do
@@ -50,17 +50,17 @@
end
end
- context 'when ignoring accents' do
- it 'escapes the search document and query, but not the accent function' do
+ context "when ignoring accents" do
+ it "escapes the search document and query, but not the accent function" do
config.ignore = [:accents]
expect(feature.conditions.to_sql).to eq("(unaccent('#{query}') % (unaccent(#{coalesced_columns})))")
end
end
- context 'when a threshold is specified' do
- context 'when searching by similarity' do
+ context "when a threshold is specified" do
+ context "when searching by similarity" do
let(:options) do
- { threshold: 0.5 }
+ {threshold: 0.5}
end
it 'uses a minimum similarity expression instead of the "%" operator' do
@@ -70,9 +70,9 @@
end
end
- context 'when searching by word_similarity' do
+ context "when searching by word_similarity" do
let(:options) do
- { threshold: 0.5, word_similarity: true }
+ {threshold: 0.5, word_similarity: true}
end
it 'uses a minimum similarity expression instead of the "<%" operator' do
@@ -83,28 +83,28 @@
end
end
- context 'when only certain columns are selected' do
- context 'with one column' do
- let(:options) { { only: :name } }
+ context "when only certain columns are selected" do
+ context "with one column" do
+ let(:options) { {only: :name} }
- it 'only searches against the select column' do
- coalesced_column = "coalesce(#{Model.quoted_table_name}.\"name\"::text, '')"
+ it "only searches against the select column" do
+ coalesced_column = "coalesce((#{Model.quoted_table_name}.\"name\")::text, '')"
expect(feature.conditions.to_sql).to eq("('#{query}' % (#{coalesced_column}))")
end
end
- context 'with multiple columns' do
- let(:options) { { only: %i[name content] } }
+ context "with multiple columns" do
+ let(:options) { {only: %i[name content]} }
- it 'concatenates when multiples columns are selected' do
+ it "concatenates when multiples columns are selected" do
expect(feature.conditions.to_sql).to eq("('#{query}' % (#{coalesced_columns}))")
end
end
end
end
- describe '#rank' do
- it 'returns an expression using the similarity() function' do
+ describe "#rank" do
+ it "returns an expression using the similarity() function" do
expect(feature.rank.to_sql).to eq("(similarity('#{query}', (#{coalesced_columns})))")
end
end
diff --git a/spec/lib/pg_search/features/tsearch_spec.rb b/spec/lib/pg_search/features/tsearch_spec.rb
index d702d69c..8b7456c1 100644
--- a/spec/lib/pg_search/features/tsearch_spec.rb
+++ b/spec/lib/pg_search/features/tsearch_spec.rb
@@ -24,7 +24,7 @@
feature = described_class.new(query, options, columns, Model, normalizer)
expect(feature.rank.to_sql).to eq(
- %{(ts_rank((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 0))}
+ %{(ts_rank((to_tsvector('simple', coalesce((#{Model.quoted_table_name}."name")::text, '')) || to_tsvector('simple', coalesce((#{Model.quoted_table_name}."content")::text, ''))), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 0))}
)
end
@@ -35,7 +35,7 @@
PgSearch::Configuration::Column.new(:name, nil, Model),
PgSearch::Configuration::Column.new(:content, nil, Model)
]
- options = { tsvector_column: :my_tsvector, normalization: 2 }
+ options = {tsvector_column: :my_tsvector, normalization: 2}
config = instance_double(PgSearch::Configuration, :config, ignore: [])
normalizer = PgSearch::Normalizer.new(config)
@@ -67,7 +67,7 @@
feature = described_class.new(query, options, columns, Model, normalizer)
expect(feature.conditions.to_sql).to eq(
- %{((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))) @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))}
+ %{((to_tsvector('simple', coalesce((#{Model.quoted_table_name}."name")::text, '')) || to_tsvector('simple', coalesce((#{Model.quoted_table_name}."content")::text, ''))) @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))}
)
end
@@ -78,13 +78,13 @@
PgSearch::Configuration::Column.new(:name, nil, Model),
PgSearch::Configuration::Column.new(:content, nil, Model)
]
- options = { negation: true }
+ options = {negation: true}
config = instance_double(PgSearch::Configuration, :config, ignore: [])
normalizer = PgSearch::Normalizer.new(config)
feature = described_class.new(query, options, columns, Model, normalizer)
expect(feature.conditions.to_sql).to eq(
- %{((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))) @@ (to_tsquery('simple', '!' || ''' ' || 'query' || ' ''')))}
+ %{((to_tsvector('simple', coalesce((#{Model.quoted_table_name}."name")::text, '')) || to_tsvector('simple', coalesce((#{Model.quoted_table_name}."content")::text, ''))) @@ (to_tsquery('simple', '!' || ''' ' || 'query' || ' ''')))}
)
end
end
@@ -96,25 +96,25 @@
PgSearch::Configuration::Column.new(:name, nil, Model),
PgSearch::Configuration::Column.new(:content, nil, Model)
]
- options = { negation: false }
+ options = {negation: false}
config = instance_double(PgSearch::Configuration, :config, ignore: [])
normalizer = PgSearch::Normalizer.new(config)
feature = described_class.new(query, options, columns, Model, normalizer)
expect(feature.conditions.to_sql).to eq(
- %{((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))) @@ (to_tsquery('simple', ''' ' || '!query' || ' ''')))}
+ %{((to_tsvector('simple', coalesce((#{Model.quoted_table_name}."name")::text, '')) || to_tsvector('simple', coalesce((#{Model.quoted_table_name}."content")::text, ''))) @@ (to_tsquery('simple', ''' ' || '!query' || ' ''')))}
)
end
end
context "when options[:tsvector_column] is a string" do
- it 'uses the tsvector column' do
+ it "uses the tsvector column" do
query = "query"
columns = [
PgSearch::Configuration::Column.new(:name, nil, Model),
PgSearch::Configuration::Column.new(:content, nil, Model)
]
- options = { tsvector_column: "my_tsvector" }
+ options = {tsvector_column: "my_tsvector"}
config = instance_double(PgSearch::Configuration, :config, ignore: [])
normalizer = PgSearch::Normalizer.new(config)
@@ -126,13 +126,13 @@
end
context "when options[:tsvector_column] is an array of strings" do
- it 'uses the tsvector column' do
+ it "uses the tsvector column" do
query = "query"
columns = [
PgSearch::Configuration::Column.new(:name, nil, Model),
PgSearch::Configuration::Column.new(:content, nil, Model)
]
- options = { tsvector_column: ["tsvector1", "tsvector2"] }
+ options = {tsvector_column: ["tsvector1", "tsvector2"]}
config = instance_double(PgSearch::Configuration, :config, ignore: [])
normalizer = PgSearch::Normalizer.new(config)
@@ -181,13 +181,13 @@
feature = described_class.new(query, options, columns, Model, normalizer)
expect(feature.highlight.to_sql).to eq(
- "(ts_headline('simple', (coalesce(#{Model.quoted_table_name}.\"name\"::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), ''))"
+ "(ts_headline('simple', (coalesce((#{Model.quoted_table_name}.\"name\")::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), ''))"
)
end
context "when options[:dictionary] is passed" do
# rubocop:disable RSpec/ExampleLength
- it 'uses the provided dictionary' do
+ it "uses the provided dictionary" do
query = "query"
columns = [
PgSearch::Configuration::Column.new(:name, nil, Model),
@@ -206,7 +206,7 @@
feature = described_class.new(query, options, columns, Model, normalizer)
- expected_sql = %{(ts_headline('spanish', (coalesce(#{Model.quoted_table_name}."name"::text, '') || ' ' || coalesce(#{Model.quoted_table_name}."content"::text, '')), (to_tsquery('spanish', ''' ' || 'query' || ' ''')), 'StartSel = "", StopSel = ""'))}
+ expected_sql = %{(ts_headline('spanish', (coalesce((#{Model.quoted_table_name}."name")::text, '') || ' ' || coalesce((#{Model.quoted_table_name}."content")::text, '')), (to_tsquery('spanish', ''' ' || 'query' || ' ''')), 'StartSel = "", StopSel = ""'))}
expect(feature.highlight.to_sql).to eq(expected_sql)
end
@@ -223,13 +223,13 @@
options = {
highlight: {
StartSel: '',
- StopSel: '',
+ StopSel: "",
MaxWords: 123,
MinWords: 456,
ShortWord: 4,
HighlightAll: true,
MaxFragments: 3,
- FragmentDelimiter: '…'
+ FragmentDelimiter: "…"
}
}
@@ -238,7 +238,7 @@
feature = described_class.new(query, options, columns, Model, normalizer)
- expected_sql = %{(ts_headline('simple', (coalesce(#{Model.quoted_table_name}."name"::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 'StartSel = "", StopSel = "", MaxFragments = 3, MaxWords = 123, MinWords = 456, ShortWord = 4, FragmentDelimiter = "…", HighlightAll = TRUE'))}
+ expected_sql = %{(ts_headline('simple', (coalesce((#{Model.quoted_table_name}."name")::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 'StartSel = "", StopSel = "", MaxFragments = 3, MaxWords = 123, MinWords = 456, ShortWord = 4, FragmentDelimiter = "…", HighlightAll = TRUE'))}
expect(feature.highlight.to_sql).to eq(expected_sql)
end
@@ -253,13 +253,13 @@
options = {
highlight: {
start_sel: '',
- stop_sel: '',
+ stop_sel: "",
max_words: 123,
min_words: 456,
short_word: 4,
highlight_all: false,
max_fragments: 3,
- fragment_delimiter: '…'
+ fragment_delimiter: "…"
}
}
@@ -269,7 +269,7 @@
feature = described_class.new(query, options, columns, Model, normalizer)
highlight_sql = ActiveSupport::Deprecation.silence { feature.highlight.to_sql }
- expected_sql = %{(ts_headline('simple', (coalesce(#{Model.quoted_table_name}."name"::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 'StartSel = "", StopSel = "", MaxFragments = 3, MaxWords = 123, MinWords = 456, ShortWord = 4, FragmentDelimiter = "…", HighlightAll = FALSE'))}
+ expected_sql = %{(ts_headline('simple', (coalesce((#{Model.quoted_table_name}."name")::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 'StartSel = "", StopSel = "", MaxFragments = 3, MaxWords = 123, MinWords = 456, ShortWord = 4, FragmentDelimiter = "…", HighlightAll = FALSE'))}
expect(highlight_sql).to eq(expected_sql)
end
diff --git a/spec/lib/pg_search/multisearch/rebuilder_spec.rb b/spec/lib/pg_search/multisearch/rebuilder_spec.rb
index 5a462644..58a48c02 100644
--- a/spec/lib/pg_search/multisearch/rebuilder_spec.rb
+++ b/spec/lib/pg_search/multisearch/rebuilder_spec.rb
@@ -6,10 +6,10 @@
describe PgSearch::Multisearch::Rebuilder do
with_table "pg_search_documents", &DOCUMENTS_SCHEMA
- describe 'when initialized with a model that is not multisearchable' do
+ describe "when initialized with a model that is not multisearchable" do
with_model :not_multisearchable
- it 'raises an exception' do
+ it "raises an exception" do
expect {
described_class.new(NotMultisearchable)
}.to raise_exception(
@@ -253,7 +253,7 @@ def foo
model do
include PgSearch::Model
multisearchable against: :name,
- additional_attributes: ->(obj) { { additional_attribute_column: "#{obj.class}::#{obj.id}" } }
+ additional_attributes: ->(obj) { {additional_attribute_column: "#{obj.class}::#{obj.id}"} }
end
end
diff --git a/spec/lib/pg_search/multisearchable_spec.rb b/spec/lib/pg_search/multisearchable_spec.rb
index c0814e58..c155741b 100644
--- a/spec/lib/pg_search/multisearchable_spec.rb
+++ b/spec/lib/pg_search/multisearchable_spec.rb
@@ -149,7 +149,7 @@
end
context "when searching against a single column" do
- let(:multisearchable_options) { { against: :some_content } }
+ let(:multisearchable_options) { {against: :some_content} }
let(:text) { "foo bar" }
before do
@@ -159,7 +159,7 @@
record.save
end
- describe '#content' do
+ describe "#content" do
subject { super().pg_search_document.content }
it { is_expected.to eq(text) }
@@ -167,17 +167,17 @@
end
context "when searching against multiple columns" do
- let(:multisearchable_options) { { against: %i[attr_1 attr_2] } }
+ let(:multisearchable_options) { {against: %i[attr_1 attr_2]} }
before do
without_partial_double_verification do
- allow(record).to receive(:attr_1).and_return('1')
- allow(record).to receive(:attr_2).and_return('2')
+ allow(record).to receive(:attr_1).and_return("1")
+ allow(record).to receive(:attr_2).and_return("2")
end
record.save
end
- describe '#content' do
+ describe "#content" do
subject { super().pg_search_document.content }
it { is_expected.to eq("1 2") }
@@ -195,7 +195,7 @@
end
context "when searching against a single column" do
- let(:multisearchable_options) { { against: :some_content } }
+ let(:multisearchable_options) { {against: :some_content} }
let(:text) { "foo bar" }
before do
@@ -205,7 +205,7 @@
record.save
end
- describe '#content' do
+ describe "#content" do
subject { super().pg_search_document.content }
it { is_expected.to eq(text) }
@@ -213,17 +213,17 @@
end
context "when searching against multiple columns" do
- let(:multisearchable_options) { { against: %i[attr_1 attr_2] } }
+ let(:multisearchable_options) { {against: %i[attr_1 attr_2]} }
before do
without_partial_double_verification do
- allow(record).to receive(:attr_1).and_return('1')
- allow(record).to receive(:attr_2).and_return('2')
+ allow(record).to receive(:attr_1).and_return("1")
+ allow(record).to receive(:attr_2).and_return("2")
end
record.save
end
- describe '#content' do
+ describe "#content" do
subject { super().pg_search_document.content }
it { is_expected.to eq("1 2") }
@@ -234,7 +234,7 @@
let(:multisearchable_options) do
{
additional_attributes: lambda do |record|
- { foo: record.bar }
+ {foo: record.bar}
end
}
end
@@ -247,7 +247,7 @@
record.save
expect(record)
.to have_received(:create_pg_search_document)
- .with(content: '', foo: text)
+ .with(content: "", foo: text)
end
end
end
@@ -269,7 +269,7 @@
record.save
expect(record)
.to have_received(:create_pg_search_document)
- .with(content: '')
+ .with(content: "")
end
end
diff --git a/spec/lib/pg_search_spec.rb b/spec/lib/pg_search_spec.rb
index 80cca230..6a178f2d 100644
--- a/spec/lib/pg_search_spec.rb
+++ b/spec/lib/pg_search_spec.rb
@@ -49,7 +49,7 @@ def clear_searchable_cache
end
end
- let!(:soundalike_record) { MultisearchableModel.create!(title: 'foning') }
+ let!(:soundalike_record) { MultisearchableModel.create!(title: "foning") }
let(:query) { "Phoning" }
it { is_expected.to include(soundalike_record) }
@@ -65,9 +65,9 @@ def clear_searchable_cache
allow(described_class).to receive(:multisearch_options) do
lambda do |query, soundalike|
if soundalike
- { using: :dmetaphone, query: query }
+ {using: :dmetaphone, query: query}
else
- { query: query }
+ {query: query}
end
end
end
@@ -83,7 +83,7 @@ def clear_searchable_cache
end
end
- let!(:soundalike_record) { MultisearchableModel.create!(title: 'foning') }
+ let!(:soundalike_record) { MultisearchableModel.create!(title: "foning") }
let(:query) { "Phoning" }
context "with soundalike true" do
@@ -103,8 +103,8 @@ def clear_searchable_cache
context "with standard type column" do
with_model :SuperclassModel do
table do |t|
- t.text 'content'
- t.string 'type'
+ t.text "content"
+ t.string "type"
end
end
@@ -191,12 +191,12 @@ def clear_searchable_cache
context "with custom type column" do
with_model :SuperclassModel do
table do |t|
- t.text 'content'
- t.string 'inherit'
+ t.text "content"
+ t.string "inherit"
end
model do
- self.inheritance_column = 'inherit'
+ self.inheritance_column = "inherit"
end
end
@@ -254,7 +254,7 @@ def clear_searchable_cache
multisearch_enabled_inside = described_class.multisearch_enabled?
raise
end
- rescue StandardError
+ rescue
end
multisearch_enabled_after = described_class.multisearch_enabled?
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index e25191aa..0498959b 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,19 +1,19 @@
# frozen_string_literal: true
-require 'warning'
+require "warning"
# Ignore Ruby 2.7 warnings from Active Record
Warning.ignore :keyword_separation
# https://github.com/grodowski/undercover#setting-up-required-lcov-reporting
-require 'simplecov'
-require 'simplecov-lcov'
+require "simplecov"
+require "simplecov-lcov"
SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
SimpleCov.start do
add_filter(%r{^/spec/})
enable_coverage(:branch)
end
-require 'undercover'
+require "undercover"
require "bundler/setup"
require "pg_search"
@@ -29,11 +29,11 @@
mocks.verify_partial_doubles = true
end
- config.example_status_persistence_file_path = 'tmp/examples.txt'
+ config.example_status_persistence_file_path = "tmp/examples.txt"
end
-require 'support/database'
-require 'support/with_model'
+require "support/database"
+require "support/with_model"
DOCUMENTS_SCHEMA = lambda do |t|
t.belongs_to :searchable, polymorphic: true, index: true
diff --git a/spec/support/database.rb b/spec/support/database.rb
index 11d3ef00..23ada4e5 100644
--- a/spec/support/database.rb
+++ b/spec/support/database.rb
@@ -10,10 +10,10 @@
end
begin
- connection_options = { adapter: 'postgresql', database: 'pg_search_test', min_messages: 'warning' }
+ connection_options = {adapter: "postgresql", database: "pg_search_test", min_messages: "warning"}
if ENV["CI"]
- connection_options[:username] = 'postgres'
- connection_options[:password] = 'postgres'
+ connection_options[:username] = "postgres"
+ connection_options[:password] = "postgres"
end
ActiveRecord::Base.establish_connection(connection_options)
connection = ActiveRecord::Base.connection
@@ -40,7 +40,7 @@ def install_extension(name)
return unless extension.none?
connection.execute "CREATE EXTENSION #{name};"
-rescue StandardError => e
+rescue => e
at_exit do
puts "-" * 80
puts "Please install the #{name} extension"
@@ -52,7 +52,7 @@ def install_extension(name)
def install_extension_if_missing(name, query, expected_result)
result = ActiveRecord::Base.connection.select_value(query)
raise "Unexpected output for #{query}: #{result.inspect}" unless result.casecmp(expected_result).zero?
-rescue StandardError
+rescue
install_extension(name)
end
@@ -62,10 +62,9 @@ def install_extension_if_missing(name, query, expected_result)
def load_sql(filename)
connection = ActiveRecord::Base.connection
- file_contents = File.read(File.join(File.dirname(__FILE__), '..', '..', 'sql', filename))
+ file_contents = File.read(File.join(File.dirname(__FILE__), "..", "..", "sql", filename))
connection.execute(file_contents)
end
load_sql("dmetaphone.sql")
-
-load_sql("tsvector_agg.sql") unless connection.select_value("SELECT 1 FROM pg_catalog.pg_aggregate WHERE aggfnoid = 'tsvector_agg'::REGPROC") == "1"
+load_sql("tsvector_agg.sql")
diff --git a/sql/tsvector_agg.sql b/sql/tsvector_agg.sql
index 6848d975..c5221685 100644
--- a/sql/tsvector_agg.sql
+++ b/sql/tsvector_agg.sql
@@ -4,7 +4,7 @@ $function$ BEGIN
END; $function$
LANGUAGE plpgsql;
-CREATE AGGREGATE tsvector_agg(tsvector) (
+CREATE OR REPLACE AGGREGATE tsvector_agg(tsvector) (
SFUNC=concat_tsvectors,
STYPE=tsvector,
INITCOND=''