pg_search 2.3.7 → 2.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +41 -34
- data/.github/workflows/codeql.yml +87 -0
- data/.standard.yml +2 -2
- data/CHANGELOG.md +12 -0
- data/Gemfile +4 -2
- data/README.md +88 -86
- data/Rakefile +1 -7
- data/lib/pg_search/configuration.rb +10 -26
- data/lib/pg_search/features/tsearch.rb +17 -25
- data/lib/pg_search/model.rb +6 -8
- data/lib/pg_search/multisearch/rebuilder.rb +4 -3
- data/lib/pg_search/normalizer.rb +12 -2
- data/lib/pg_search/scope_options.rb +10 -3
- data/lib/pg_search/version.rb +1 -1
- data/pg_search.gemspec +3 -3
- data/spec/integration/associations_spec.rb +12 -0
- data/spec/integration/deprecation_spec.rb +5 -1
- data/spec/integration/pagination_spec.rb +1 -0
- data/spec/integration/pg_search_spec.rb +30 -1
- data/spec/integration/single_table_inheritance_spec.rb +2 -0
- data/spec/lib/pg_search/configuration/association_spec.rb +2 -0
- data/spec/lib/pg_search/configuration/foreign_column_spec.rb +1 -0
- data/spec/lib/pg_search/features/trigram_spec.rb +5 -6
- data/spec/lib/pg_search/multisearch/rebuilder_spec.rb +53 -3
- data/spec/lib/pg_search/multisearch_spec.rb +2 -2
- data/spec/lib/pg_search/multisearchable_spec.rb +8 -0
- data/spec/lib/pg_search/normalizer_spec.rb +4 -4
- data/spec/lib/pg_search_spec.rb +4 -0
- data/spec/spec_helper.rb +0 -13
- metadata +9 -13
- data/spec/.rubocop.yml +0 -27
- data/spec/integration/.rubocop.yml +0 -11
|
@@ -49,12 +49,8 @@ module PgSearch
|
|
|
49
49
|
|
|
50
50
|
%w[
|
|
51
51
|
StartSel StopSel MaxFragments MaxWords MinWords ShortWord FragmentDelimiter HighlightAll
|
|
52
|
-
].
|
|
53
|
-
|
|
54
|
-
value = indifferent_options[:highlight][key]
|
|
55
|
-
|
|
56
|
-
hash[key] = ts_headline_option_value(value)
|
|
57
|
-
end
|
|
52
|
+
].to_h do |key|
|
|
53
|
+
[key, ts_headline_option_value(indifferent_options[:highlight][key])]
|
|
58
54
|
end
|
|
59
55
|
end
|
|
60
56
|
|
|
@@ -63,23 +59,20 @@ module PgSearch
|
|
|
63
59
|
|
|
64
60
|
%w[
|
|
65
61
|
start_sel stop_sel max_fragments max_words min_words short_word fragment_delimiter highlight_all
|
|
66
|
-
].
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
hash[key] = ts_headline_option_value(value)
|
|
81
|
-
end
|
|
82
|
-
end
|
|
62
|
+
].each_with_object({}) do |deprecated_key, hash|
|
|
63
|
+
value = indifferent_options[:highlight][deprecated_key]
|
|
64
|
+
next if value.nil?
|
|
65
|
+
|
|
66
|
+
key = deprecated_key.camelize
|
|
67
|
+
|
|
68
|
+
warn(
|
|
69
|
+
"pg_search 3.0 will no longer accept :#{deprecated_key} as an argument to :ts_headline, " \
|
|
70
|
+
"use :#{key} instead.",
|
|
71
|
+
category: :deprecated,
|
|
72
|
+
uplevel: 1
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
hash[key] = ts_headline_option_value(value)
|
|
83
76
|
end
|
|
84
77
|
end
|
|
85
78
|
|
|
@@ -96,8 +89,7 @@ module PgSearch
|
|
|
96
89
|
end
|
|
97
90
|
end
|
|
98
91
|
|
|
99
|
-
DISALLOWED_TSQUERY_CHARACTERS = /['
|
|
100
|
-
|
|
92
|
+
DISALLOWED_TSQUERY_CHARACTERS = /['?\\:]/ # standard:disable Lint/UselessConstantScoping
|
|
101
93
|
def tsquery_for_term(unsanitized_term)
|
|
102
94
|
if options[:negation] && unsanitized_term.start_with?("!")
|
|
103
95
|
unsanitized_term[0] = ""
|
data/lib/pg_search/model.rb
CHANGED
|
@@ -23,13 +23,14 @@ module PgSearch
|
|
|
23
23
|
|
|
24
24
|
def multisearchable(options = {})
|
|
25
25
|
include PgSearch::Multisearchable
|
|
26
|
+
|
|
26
27
|
class_attribute :pg_search_multisearchable_options
|
|
27
28
|
self.pg_search_multisearchable_options = options
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
31
|
|
|
31
|
-
def method_missing(
|
|
32
|
-
case
|
|
32
|
+
def method_missing(method, *, **)
|
|
33
|
+
case method
|
|
33
34
|
when :pg_search_rank
|
|
34
35
|
raise PgSearchRankNotSelected unless respond_to?(:pg_search_rank)
|
|
35
36
|
|
|
@@ -43,12 +44,9 @@ module PgSearch
|
|
|
43
44
|
end
|
|
44
45
|
end
|
|
45
46
|
|
|
46
|
-
def respond_to_missing?(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
attributes.key?(:pg_search_rank)
|
|
50
|
-
when :pg_search_highlight
|
|
51
|
-
attributes.key?(:pg_search_highlight)
|
|
47
|
+
def respond_to_missing?(method, *, **)
|
|
48
|
+
if method.in?(%i[pg_search_rank pg_search_highlight])
|
|
49
|
+
attributes.key?(method)
|
|
52
50
|
else
|
|
53
51
|
super
|
|
54
52
|
end
|
|
@@ -42,7 +42,7 @@ module PgSearch
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def primary_key
|
|
45
|
-
model.primary_key
|
|
45
|
+
connection.quote_column_name(model.primary_key)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def rebuild_sql_template
|
|
@@ -68,9 +68,10 @@ module PgSearch
|
|
|
68
68
|
def sti_clause
|
|
69
69
|
clause = ""
|
|
70
70
|
if model.column_names.include? model.inheritance_column
|
|
71
|
+
quoted_inheritance_column = connection.quote_column_name(model.inheritance_column)
|
|
71
72
|
clause = "WHERE"
|
|
72
|
-
clause = "#{clause} #{
|
|
73
|
-
clause = "#{clause} #{
|
|
73
|
+
clause = "#{clause} #{quoted_inheritance_column} IS NULL OR" if model.base_class == model
|
|
74
|
+
clause = "#{clause} #{quoted_inheritance_column} = #{model_name}"
|
|
74
75
|
end
|
|
75
76
|
clause
|
|
76
77
|
end
|
data/lib/pg_search/normalizer.rb
CHANGED
|
@@ -6,6 +6,8 @@ module PgSearch
|
|
|
6
6
|
@config = config
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
DISALLOWED_CHARACTERS = "'[''?\\:]'"
|
|
10
|
+
|
|
9
11
|
def add_normalization(sql_expression)
|
|
10
12
|
return sql_expression unless config.ignore.include?(:accents)
|
|
11
13
|
|
|
@@ -17,8 +19,16 @@ module PgSearch
|
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
Arel::Nodes::NamedFunction.new(
|
|
20
|
-
|
|
21
|
-
[
|
|
22
|
+
"regexp_replace",
|
|
23
|
+
[
|
|
24
|
+
Arel::Nodes::NamedFunction.new(
|
|
25
|
+
PgSearch.unaccent_function,
|
|
26
|
+
[sql_node]
|
|
27
|
+
),
|
|
28
|
+
Arel.sql(DISALLOWED_CHARACTERS),
|
|
29
|
+
Arel.sql("''"),
|
|
30
|
+
Arel.sql("'g'")
|
|
31
|
+
]
|
|
22
32
|
).to_sql
|
|
23
33
|
end
|
|
24
34
|
|
|
@@ -27,6 +27,7 @@ module PgSearch
|
|
|
27
27
|
def self.[](tsearch)
|
|
28
28
|
Module.new do
|
|
29
29
|
include WithPgSearchHighlight
|
|
30
|
+
|
|
30
31
|
define_method(:tsearch) { tsearch }
|
|
31
32
|
end
|
|
32
33
|
end
|
|
@@ -133,7 +134,7 @@ module PgSearch
|
|
|
133
134
|
end
|
|
134
135
|
end
|
|
135
136
|
|
|
136
|
-
FEATURE_CLASSES = {
|
|
137
|
+
FEATURE_CLASSES = { # standard:disable Lint/UselessConstantScoping
|
|
137
138
|
dmetaphone: Features::DMetaphone,
|
|
138
139
|
tsearch: Features::TSearch,
|
|
139
140
|
trigram: Features::Trigram
|
|
@@ -163,11 +164,17 @@ module PgSearch
|
|
|
163
164
|
end
|
|
164
165
|
|
|
165
166
|
def rank_join(rank_table_alias)
|
|
166
|
-
|
|
167
|
+
arel_table = model.arel_table
|
|
168
|
+
subquery_arel = subquery.arel.as(rank_table_alias)
|
|
169
|
+
|
|
170
|
+
arel_table
|
|
171
|
+
.join(subquery_arel)
|
|
172
|
+
.on(arel_table[model.primary_key].eq(subquery_arel[:pg_search_id]))
|
|
173
|
+
.join_sources
|
|
167
174
|
end
|
|
168
175
|
|
|
169
176
|
def include_table_aliasing_for_rank(scope)
|
|
170
|
-
return scope if scope.
|
|
177
|
+
return scope if scope.include?(PgSearchRankTableAliasing)
|
|
171
178
|
|
|
172
179
|
scope.all.spawn.tap do |new_scope|
|
|
173
180
|
new_scope.instance_eval { extend PgSearchRankTableAliasing }
|
data/lib/pg_search/version.rb
CHANGED
data/pg_search.gemspec
CHANGED
|
@@ -18,8 +18,8 @@ Gem::Specification.new do |s|
|
|
|
18
18
|
s.files = `git ls-files -z`.split("\x0")
|
|
19
19
|
s.require_paths = ["lib"]
|
|
20
20
|
|
|
21
|
-
s.add_dependency "activerecord", ">=
|
|
22
|
-
s.add_dependency "activesupport", ">=
|
|
21
|
+
s.add_dependency "activerecord", ">= 7.2"
|
|
22
|
+
s.add_dependency "activesupport", ">= 7.2"
|
|
23
23
|
|
|
24
|
-
s.required_ruby_version = ">= 3.
|
|
24
|
+
s.required_ruby_version = ">= 3.3"
|
|
25
25
|
end
|
|
@@ -20,6 +20,7 @@ describe "a pg_search_scope" do
|
|
|
20
20
|
|
|
21
21
|
model do
|
|
22
22
|
include PgSearch::Model
|
|
23
|
+
|
|
23
24
|
belongs_to :another_model, class_name: "AssociatedModel"
|
|
24
25
|
|
|
25
26
|
pg_search_scope :with_another, associated_against: {another_model: :title}
|
|
@@ -57,6 +58,7 @@ describe "a pg_search_scope" do
|
|
|
57
58
|
|
|
58
59
|
model do
|
|
59
60
|
include PgSearch::Model
|
|
61
|
+
|
|
60
62
|
belongs_to :another_model, class_name: "AssociatedModel"
|
|
61
63
|
|
|
62
64
|
pg_search_scope :with_associated, against: :title, associated_against: {another_model: :title}
|
|
@@ -93,6 +95,7 @@ describe "a pg_search_scope" do
|
|
|
93
95
|
|
|
94
96
|
model do
|
|
95
97
|
include PgSearch::Model
|
|
98
|
+
|
|
96
99
|
has_many :other_models, class_name: "AssociatedModelWithHasMany", foreign_key: "ModelWithHasMany_id"
|
|
97
100
|
|
|
98
101
|
pg_search_scope :with_associated, against: [:title], associated_against: {other_models: :title}
|
|
@@ -284,6 +287,7 @@ describe "a pg_search_scope" do
|
|
|
284
287
|
|
|
285
288
|
model do
|
|
286
289
|
include PgSearch::Model
|
|
290
|
+
|
|
287
291
|
belongs_to :another_model, class_name: "AssociatedModel"
|
|
288
292
|
|
|
289
293
|
pg_search_scope :with_associated, associated_against: {another_model: %i[title author]}
|
|
@@ -337,6 +341,7 @@ describe "a pg_search_scope" do
|
|
|
337
341
|
|
|
338
342
|
model do
|
|
339
343
|
include PgSearch::Model
|
|
344
|
+
|
|
340
345
|
belongs_to :another_model, class_name: "AssociatedModel"
|
|
341
346
|
|
|
342
347
|
pg_search_scope :with_associated, associated_against: {another_model: :number}
|
|
@@ -368,6 +373,7 @@ describe "a pg_search_scope" do
|
|
|
368
373
|
model do
|
|
369
374
|
has_many :children
|
|
370
375
|
include PgSearch::Model
|
|
376
|
+
|
|
371
377
|
pg_search_scope :search_name, against: :name
|
|
372
378
|
end
|
|
373
379
|
end
|
|
@@ -398,6 +404,8 @@ describe "a pg_search_scope" do
|
|
|
398
404
|
|
|
399
405
|
context "when merging a pg_search_scope into another model's scope" do
|
|
400
406
|
with_model :ModelWithAssociation do
|
|
407
|
+
table
|
|
408
|
+
|
|
401
409
|
model do
|
|
402
410
|
has_many :associated_models
|
|
403
411
|
end
|
|
@@ -411,6 +419,7 @@ describe "a pg_search_scope" do
|
|
|
411
419
|
|
|
412
420
|
model do
|
|
413
421
|
include PgSearch::Model
|
|
422
|
+
|
|
414
423
|
belongs_to :model_with_association
|
|
415
424
|
|
|
416
425
|
pg_search_scope :search_content, against: :content
|
|
@@ -444,6 +453,8 @@ describe "a pg_search_scope" do
|
|
|
444
453
|
|
|
445
454
|
context "when chained onto a has_many association" do
|
|
446
455
|
with_model :Company do
|
|
456
|
+
table
|
|
457
|
+
|
|
447
458
|
model do
|
|
448
459
|
has_many :positions
|
|
449
460
|
end
|
|
@@ -457,6 +468,7 @@ describe "a pg_search_scope" do
|
|
|
457
468
|
|
|
458
469
|
model do
|
|
459
470
|
include PgSearch::Model
|
|
471
|
+
|
|
460
472
|
pg_search_scope :search, against: :title, using: %i[tsearch trigram]
|
|
461
473
|
end
|
|
462
474
|
end
|
|
@@ -5,6 +5,8 @@ require "active_support/core_ext/kernel/reporting"
|
|
|
5
5
|
|
|
6
6
|
describe "Including the deprecated PgSearch module" do
|
|
7
7
|
with_model :SomeModel do
|
|
8
|
+
table
|
|
9
|
+
|
|
8
10
|
model do
|
|
9
11
|
silence_warnings do
|
|
10
12
|
include PgSearch
|
|
@@ -12,7 +14,9 @@ describe "Including the deprecated PgSearch module" do
|
|
|
12
14
|
end
|
|
13
15
|
end
|
|
14
16
|
|
|
15
|
-
with_model :AnotherModel
|
|
17
|
+
with_model :AnotherModel do
|
|
18
|
+
table
|
|
19
|
+
end
|
|
16
20
|
|
|
17
21
|
it "includes PgSearch::Model" do
|
|
18
22
|
expect(SomeModel.ancestors).to include PgSearch::Model
|
|
@@ -14,6 +14,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
|
14
14
|
|
|
15
15
|
model do
|
|
16
16
|
include PgSearch::Model
|
|
17
|
+
|
|
17
18
|
belongs_to :parent_model
|
|
18
19
|
end
|
|
19
20
|
end
|
|
@@ -24,6 +25,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
|
24
25
|
|
|
25
26
|
model do
|
|
26
27
|
include PgSearch::Model
|
|
28
|
+
|
|
27
29
|
has_many :models_with_pg_search
|
|
28
30
|
scope :active, -> { where(active: true) }
|
|
29
31
|
end
|
|
@@ -238,6 +240,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
|
238
240
|
|
|
239
241
|
model do
|
|
240
242
|
include PgSearch::Model
|
|
243
|
+
|
|
241
244
|
belongs_to :person
|
|
242
245
|
pg_search_scope :search_city, against: [:city]
|
|
243
246
|
end
|
|
@@ -250,6 +253,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
|
250
253
|
|
|
251
254
|
model do
|
|
252
255
|
include PgSearch::Model
|
|
256
|
+
|
|
253
257
|
has_many :houses
|
|
254
258
|
pg_search_scope :named, against: [:name]
|
|
255
259
|
scope :with_house_in_city, lambda { |city|
|
|
@@ -1046,6 +1050,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
|
1046
1050
|
|
|
1047
1051
|
model do
|
|
1048
1052
|
include PgSearch::Model
|
|
1053
|
+
|
|
1049
1054
|
has_many :comments
|
|
1050
1055
|
end
|
|
1051
1056
|
end
|
|
@@ -1083,10 +1088,34 @@ describe "an Active Record model which includes PgSearch" do
|
|
|
1083
1088
|
it "finds by a combination of the two" do
|
|
1084
1089
|
expect(Post.search_by_content_with_tsvector("phooey commentone").map(&:id)).to eq([expected.id])
|
|
1085
1090
|
end
|
|
1091
|
+
|
|
1092
|
+
it "finds by the tsvector column when ordering by an associated table" do
|
|
1093
|
+
# Reference the physical table name that Active Record would normally
|
|
1094
|
+
# leave unaliased. The old string rank join made AliasTracker think the
|
|
1095
|
+
# name was already used and alias this outer join, breaking the order.
|
|
1096
|
+
# https://github.com/Casecommons/pg_search/issues/206
|
|
1097
|
+
results = Post
|
|
1098
|
+
.joins(:comments)
|
|
1099
|
+
.search_by_content_with_tsvector("phooey")
|
|
1100
|
+
.order(Comment.table_name => {id: :asc})
|
|
1101
|
+
|
|
1102
|
+
expect(results).to eq([expected])
|
|
1103
|
+
end
|
|
1104
|
+
|
|
1105
|
+
it "finds by the tsvector column when ordering by an association alias" do
|
|
1106
|
+
results = Post
|
|
1107
|
+
.joins(:comments)
|
|
1108
|
+
.search_by_content_with_tsvector("phooey")
|
|
1109
|
+
.order(comments: {id: :asc})
|
|
1110
|
+
|
|
1111
|
+
expect(results).to eq([expected])
|
|
1112
|
+
end
|
|
1086
1113
|
end
|
|
1087
1114
|
|
|
1088
1115
|
context "when using multiple tsvector columns" do
|
|
1089
1116
|
with_model :ModelWithTsvector do
|
|
1117
|
+
table
|
|
1118
|
+
|
|
1090
1119
|
model do
|
|
1091
1120
|
include PgSearch::Model
|
|
1092
1121
|
|
|
@@ -1185,7 +1214,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
|
1185
1214
|
end
|
|
1186
1215
|
|
|
1187
1216
|
context "when the query includes accents" do
|
|
1188
|
-
let(:term) { "L#{%w[‘ ’ ʻ ʼ].sample}Content" }
|
|
1217
|
+
let(:term) { "L#{%w[‘ ’ ʻ ʼ ʹ ʽ ˈ ' ʼn].sample}Content" }
|
|
1189
1218
|
let(:included) { ModelWithPgSearch.create!(title: "Weird #{term}") }
|
|
1190
1219
|
let(:results) { ModelWithPgSearch.search_title_without_accents(term) }
|
|
1191
1220
|
|
|
@@ -12,6 +12,7 @@ describe "a pg_search_scope on an STI subclass" do
|
|
|
12
12
|
|
|
13
13
|
model do
|
|
14
14
|
include PgSearch::Model
|
|
15
|
+
|
|
15
16
|
pg_search_scope :search_content, against: :content
|
|
16
17
|
end
|
|
17
18
|
end
|
|
@@ -52,6 +53,7 @@ describe "a pg_search_scope on an STI subclass" do
|
|
|
52
53
|
|
|
53
54
|
model do
|
|
54
55
|
include PgSearch::Model
|
|
56
|
+
|
|
55
57
|
self.inheritance_column = "custom_type"
|
|
56
58
|
pg_search_scope :search_content, against: :content
|
|
57
59
|
end
|
|
@@ -19,6 +19,7 @@ describe PgSearch::Configuration::Association do
|
|
|
19
19
|
|
|
20
20
|
model do
|
|
21
21
|
include PgSearch::Model
|
|
22
|
+
|
|
22
23
|
has_one :avatar, class_name: "Avatar"
|
|
23
24
|
belongs_to :site
|
|
24
25
|
|
|
@@ -34,6 +35,7 @@ describe PgSearch::Configuration::Association do
|
|
|
34
35
|
|
|
35
36
|
model do
|
|
36
37
|
include PgSearch::Model
|
|
38
|
+
|
|
37
39
|
has_many :users, class_name: "User"
|
|
38
40
|
|
|
39
41
|
pg_search_scope :with_users, associated_against: {users: :name}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "spec_helper"
|
|
4
|
-
require "ostruct"
|
|
5
4
|
|
|
6
5
|
# standard:disable RSpec/MultipleMemoizedHelpers, RSpec/NestedGroups
|
|
7
6
|
describe PgSearch::Features::Trigram do
|
|
@@ -16,7 +15,8 @@ describe PgSearch::Features::Trigram do
|
|
|
16
15
|
]
|
|
17
16
|
}
|
|
18
17
|
let(:normalizer) { PgSearch::Normalizer.new(config) }
|
|
19
|
-
let(:config) {
|
|
18
|
+
let(:config) { instance_double(PgSearch::Configuration, ignore: ignore) }
|
|
19
|
+
let(:ignore) { [] }
|
|
20
20
|
|
|
21
21
|
let(:coalesced_columns) do
|
|
22
22
|
<<~SQL.squish
|
|
@@ -35,7 +35,6 @@ describe PgSearch::Features::Trigram do
|
|
|
35
35
|
|
|
36
36
|
describe "conditions" do
|
|
37
37
|
it "escapes the search document and query" do
|
|
38
|
-
config.ignore = []
|
|
39
38
|
expect(feature.conditions.to_sql).to eq("('#{query}' % (#{coalesced_columns}))")
|
|
40
39
|
end
|
|
41
40
|
|
|
@@ -45,15 +44,15 @@ describe PgSearch::Features::Trigram do
|
|
|
45
44
|
end
|
|
46
45
|
|
|
47
46
|
it 'uses the "<%" operator when searching by word_similarity' do
|
|
48
|
-
config.ignore = []
|
|
49
47
|
expect(feature.conditions.to_sql).to eq("('#{query}' <% (#{coalesced_columns}))")
|
|
50
48
|
end
|
|
51
49
|
end
|
|
52
50
|
|
|
53
51
|
context "when ignoring accents" do
|
|
52
|
+
let(:ignore) { [:accents] }
|
|
53
|
+
|
|
54
54
|
it "escapes the search document and query, but not the accent function" do
|
|
55
|
-
|
|
56
|
-
expect(feature.conditions.to_sql).to eq("(unaccent('#{query}') % (unaccent(#{coalesced_columns})))")
|
|
55
|
+
expect(feature.conditions.to_sql).to eq("(regexp_replace(unaccent('#{query}'), '[''?\\:]', '', 'g') % (regexp_replace(unaccent(#{coalesced_columns}), '[''?\\:]', '', 'g')))")
|
|
57
56
|
end
|
|
58
57
|
end
|
|
59
58
|
|
|
@@ -7,7 +7,9 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
|
7
7
|
with_table "pg_search_documents", &DOCUMENTS_SCHEMA
|
|
8
8
|
|
|
9
9
|
describe "when initialized with a model that is not multisearchable" do
|
|
10
|
-
with_model :not_multisearchable
|
|
10
|
+
with_model :not_multisearchable do
|
|
11
|
+
table
|
|
12
|
+
end
|
|
11
13
|
|
|
12
14
|
it "raises an exception" do
|
|
13
15
|
expect {
|
|
@@ -23,8 +25,11 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
|
23
25
|
context "when the model defines .rebuild_pg_search_documents" do
|
|
24
26
|
context "when multisearchable is not conditional" do
|
|
25
27
|
with_model :Model do
|
|
28
|
+
table
|
|
29
|
+
|
|
26
30
|
model do
|
|
27
31
|
include PgSearch::Model
|
|
32
|
+
|
|
28
33
|
multisearchable
|
|
29
34
|
|
|
30
35
|
def rebuild_pg_search_documents
|
|
@@ -53,6 +58,7 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
|
53
58
|
|
|
54
59
|
model do
|
|
55
60
|
include PgSearch::Model
|
|
61
|
+
|
|
56
62
|
multisearchable conditional_key => :active?
|
|
57
63
|
|
|
58
64
|
def rebuild_pg_search_documents
|
|
@@ -84,6 +90,7 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
|
84
90
|
|
|
85
91
|
model do
|
|
86
92
|
include PgSearch::Model
|
|
93
|
+
|
|
87
94
|
multisearchable against: :name
|
|
88
95
|
end
|
|
89
96
|
end
|
|
@@ -116,7 +123,7 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
|
116
123
|
expected_sql = <<~SQL.squish
|
|
117
124
|
INSERT INTO "pg_search_documents" (searchable_type, searchable_id, content, created_at, updated_at)
|
|
118
125
|
SELECT 'Model' AS searchable_type,
|
|
119
|
-
#{Model.quoted_table_name}.#{Model.primary_key} AS searchable_id,
|
|
126
|
+
#{Model.quoted_table_name}.#{Model.connection.quote_column_name(Model.primary_key)} AS searchable_id,
|
|
120
127
|
(
|
|
121
128
|
coalesce(#{Model.quoted_table_name}."name"::text, '')
|
|
122
129
|
) AS content,
|
|
@@ -147,6 +154,7 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
|
147
154
|
|
|
148
155
|
model do
|
|
149
156
|
include PgSearch::Model
|
|
157
|
+
|
|
150
158
|
multisearchable against: :name
|
|
151
159
|
end
|
|
152
160
|
end
|
|
@@ -166,6 +174,7 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
|
166
174
|
|
|
167
175
|
model do
|
|
168
176
|
include PgSearch::Model
|
|
177
|
+
|
|
169
178
|
multisearchable against: :name
|
|
170
179
|
end
|
|
171
180
|
end
|
|
@@ -178,7 +187,7 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
|
178
187
|
expected_sql = <<~SQL.squish
|
|
179
188
|
INSERT INTO "pg_search_documents" (searchable_type, searchable_id, content, created_at, updated_at)
|
|
180
189
|
SELECT 'ModelWithNonStandardPrimaryKey' AS searchable_type,
|
|
181
|
-
#{ModelWithNonStandardPrimaryKey.quoted_table_name}.non_standard_primary_key AS searchable_id,
|
|
190
|
+
#{ModelWithNonStandardPrimaryKey.quoted_table_name}."non_standard_primary_key" AS searchable_id,
|
|
182
191
|
(
|
|
183
192
|
coalesce(#{ModelWithNonStandardPrimaryKey.quoted_table_name}."name"::text, '')
|
|
184
193
|
) AS content,
|
|
@@ -205,8 +214,11 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
|
205
214
|
|
|
206
215
|
context "when :against includes non-column dynamic methods" do
|
|
207
216
|
with_model :Model do
|
|
217
|
+
table
|
|
218
|
+
|
|
208
219
|
model do
|
|
209
220
|
include PgSearch::Model
|
|
221
|
+
|
|
210
222
|
multisearchable against: [:foo]
|
|
211
223
|
|
|
212
224
|
def foo
|
|
@@ -244,6 +256,41 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
|
244
256
|
# standard:enable RSpec/ExampleLength
|
|
245
257
|
end
|
|
246
258
|
|
|
259
|
+
context "with an identifier that requires quoting" do
|
|
260
|
+
with_model :Model do
|
|
261
|
+
table do |t|
|
|
262
|
+
t.string :name
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
model do
|
|
266
|
+
include PgSearch::Model
|
|
267
|
+
|
|
268
|
+
multisearchable against: :name
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
it "quotes the primary key in the generated SQL" do
|
|
273
|
+
malicious_pk = %(id"; DROP TABLE pg_search_documents; --)
|
|
274
|
+
allow(Model).to receive(:primary_key).and_return(malicious_pk)
|
|
275
|
+
|
|
276
|
+
rebuilder = described_class.new(Model)
|
|
277
|
+
sql = rebuilder.send(:rebuild_sql)
|
|
278
|
+
|
|
279
|
+
expect(sql).to include(Model.connection.quote_column_name(malicious_pk))
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
it "quotes the inheritance column in the STI clause" do
|
|
283
|
+
malicious_column = %(type"; DROP TABLE pg_search_documents; --)
|
|
284
|
+
allow(Model).to receive(:inheritance_column).and_return(malicious_column)
|
|
285
|
+
allow(Model).to receive(:column_names).and_return(Model.column_names + [malicious_column])
|
|
286
|
+
|
|
287
|
+
rebuilder = described_class.new(Model)
|
|
288
|
+
sql = rebuilder.send(:rebuild_sql)
|
|
289
|
+
|
|
290
|
+
expect(sql).to include(Model.connection.quote_column_name(malicious_column))
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
247
294
|
context "when only additional_attributes is set" do
|
|
248
295
|
with_model :Model do
|
|
249
296
|
table do |t|
|
|
@@ -252,6 +299,7 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
|
252
299
|
|
|
253
300
|
model do
|
|
254
301
|
include PgSearch::Model
|
|
302
|
+
|
|
255
303
|
multisearchable against: :name,
|
|
256
304
|
additional_attributes: ->(obj) { {additional_attribute_column: "#{obj.class}::#{obj.id}"} }
|
|
257
305
|
end
|
|
@@ -281,6 +329,7 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
|
281
329
|
|
|
282
330
|
model do
|
|
283
331
|
include PgSearch::Model
|
|
332
|
+
|
|
284
333
|
multisearchable if: :active?
|
|
285
334
|
end
|
|
286
335
|
end
|
|
@@ -322,6 +371,7 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
|
322
371
|
|
|
323
372
|
model do
|
|
324
373
|
include PgSearch::Model
|
|
374
|
+
|
|
325
375
|
multisearchable unless: :inactive?
|
|
326
376
|
end
|
|
327
377
|
end
|
|
@@ -148,7 +148,7 @@ describe PgSearch::Multisearch do
|
|
|
148
148
|
expected_sql = <<~SQL.squish
|
|
149
149
|
INSERT INTO #{PgSearch::Document.quoted_table_name} (searchable_type, searchable_id, content, created_at, updated_at)
|
|
150
150
|
SELECT #{connection.quote(model.name)} AS searchable_type,
|
|
151
|
-
#{model.quoted_table_name}.
|
|
151
|
+
#{model.quoted_table_name}.#{connection.quote_column_name(model.primary_key)} AS searchable_id,
|
|
152
152
|
(
|
|
153
153
|
coalesce(#{model.quoted_table_name}."title"::text, '')
|
|
154
154
|
) AS content,
|
|
@@ -175,7 +175,7 @@ describe PgSearch::Multisearch do
|
|
|
175
175
|
expected_sql = <<~SQL.squish
|
|
176
176
|
INSERT INTO #{PgSearch::Document.quoted_table_name} (searchable_type, searchable_id, content, created_at, updated_at)
|
|
177
177
|
SELECT #{connection.quote(model.name)} AS searchable_type,
|
|
178
|
-
#{model.quoted_table_name}.
|
|
178
|
+
#{model.quoted_table_name}.#{connection.quote_column_name(model.primary_key)} AS searchable_id,
|
|
179
179
|
(
|
|
180
180
|
coalesce(#{model.quoted_table_name}."title"::text, '') || ' ' || coalesce(#{model.quoted_table_name}."content"::text, '')
|
|
181
181
|
) AS content,
|