pg_search 2.3.2 → 2.3.7
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/dependabot.yml +11 -0
- data/.github/workflows/ci.yml +80 -0
- data/.jrubyrc +1 -0
- data/.standard.yml +6 -0
- data/CHANGELOG.md +55 -20
- data/CODE_OF_CONDUCT.md +76 -0
- data/Gemfile +19 -6
- data/LICENSE +1 -1
- data/README.md +106 -43
- data/Rakefile +9 -6
- data/lib/pg_search/configuration/column.rb +6 -4
- data/lib/pg_search/configuration/foreign_column.rb +1 -1
- data/lib/pg_search/configuration.rb +13 -3
- data/lib/pg_search/document.rb +9 -9
- data/lib/pg_search/features/dmetaphone.rb +5 -7
- data/lib/pg_search/features/feature.rb +1 -1
- data/lib/pg_search/features/trigram.rb +4 -4
- data/lib/pg_search/features/tsearch.rb +26 -24
- data/lib/pg_search/migration/dmetaphone_generator.rb +2 -2
- data/lib/pg_search/migration/generator.rb +5 -5
- data/lib/pg_search/migration/multisearch_generator.rb +2 -2
- data/lib/pg_search/migration/templates/add_pg_search_dmetaphone_support_functions.rb.erb +6 -6
- data/lib/pg_search/migration/templates/create_pg_search_documents.rb.erb +2 -2
- data/lib/pg_search/model.rb +6 -6
- data/lib/pg_search/multisearch/rebuilder.rb +2 -2
- data/lib/pg_search/multisearch.rb +23 -4
- data/lib/pg_search/multisearchable.rb +7 -7
- data/lib/pg_search/normalizer.rb +5 -5
- data/lib/pg_search/scope_options.rb +31 -13
- data/lib/pg_search/tasks.rb +3 -3
- data/lib/pg_search/version.rb +1 -1
- data/lib/pg_search.rb +5 -5
- data/pg_search.gemspec +16 -24
- data/spec/.rubocop.yml +20 -7
- data/spec/integration/.rubocop.yml +11 -0
- data/spec/integration/associations_spec.rb +121 -160
- data/spec/integration/deprecation_spec.rb +7 -8
- data/spec/integration/pg_search_spec.rb +390 -332
- data/spec/integration/single_table_inheritance_spec.rb +5 -5
- data/spec/lib/pg_search/configuration/association_spec.rb +21 -19
- data/spec/lib/pg_search/configuration/column_spec.rb +13 -1
- data/spec/lib/pg_search/configuration/foreign_column_spec.rb +4 -4
- data/spec/lib/pg_search/features/dmetaphone_spec.rb +4 -4
- data/spec/lib/pg_search/features/trigram_spec.rb +32 -28
- data/spec/lib/pg_search/features/tsearch_spec.rb +57 -33
- data/spec/lib/pg_search/multisearch/rebuilder_spec.rb +94 -63
- data/spec/lib/pg_search/multisearch_spec.rb +57 -29
- data/spec/lib/pg_search/multisearchable_spec.rb +160 -107
- data/spec/lib/pg_search/normalizer_spec.rb +12 -10
- data/spec/lib/pg_search_spec.rb +75 -64
- data/spec/spec_helper.rb +21 -9
- data/spec/support/database.rb +10 -8
- metadata +20 -134
- data/.autotest +0 -5
- data/.codeclimate.yml +0 -17
- data/.rubocop.yml +0 -56
- data/.travis.yml +0 -50
|
@@ -6,8 +6,8 @@ describe "a pg_search_scope on an STI subclass" do
|
|
|
6
6
|
context "with the standard type column" do
|
|
7
7
|
with_model :SuperclassModel do
|
|
8
8
|
table do |t|
|
|
9
|
-
t.text
|
|
10
|
-
t.string
|
|
9
|
+
t.text "content"
|
|
10
|
+
t.string "type"
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
model do
|
|
@@ -46,13 +46,13 @@ describe "a pg_search_scope on an STI subclass" do
|
|
|
46
46
|
context "with a custom type column" do
|
|
47
47
|
with_model :SuperclassModel do
|
|
48
48
|
table do |t|
|
|
49
|
-
t.text
|
|
50
|
-
t.string
|
|
49
|
+
t.text "content"
|
|
50
|
+
t.string "custom_type"
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
model do
|
|
54
54
|
include PgSearch::Model
|
|
55
|
-
self.inheritance_column =
|
|
55
|
+
self.inheritance_column = "custom_type"
|
|
56
56
|
pg_search_scope :search_content, against: :content
|
|
57
57
|
end
|
|
58
58
|
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
|
|
5
|
+
# standard:disable RSpec/NestedGroups
|
|
5
6
|
describe PgSearch::Configuration::Association do
|
|
6
7
|
with_model :Avatar do
|
|
7
8
|
table do |t|
|
|
@@ -21,8 +22,8 @@ describe PgSearch::Configuration::Association do
|
|
|
21
22
|
has_one :avatar, class_name: "Avatar"
|
|
22
23
|
belongs_to :site
|
|
23
24
|
|
|
24
|
-
pg_search_scope :with_avatar, associated_against: {
|
|
25
|
-
pg_search_scope :with_site, associated_against: {
|
|
25
|
+
pg_search_scope :with_avatar, associated_against: {avatar: :url}
|
|
26
|
+
pg_search_scope :with_site, associated_against: {site: :title}
|
|
26
27
|
end
|
|
27
28
|
end
|
|
28
29
|
|
|
@@ -35,11 +36,11 @@ describe PgSearch::Configuration::Association do
|
|
|
35
36
|
include PgSearch::Model
|
|
36
37
|
has_many :users, class_name: "User"
|
|
37
38
|
|
|
38
|
-
pg_search_scope :with_users, associated_against: {
|
|
39
|
+
pg_search_scope :with_users, associated_against: {users: :name}
|
|
39
40
|
end
|
|
40
41
|
end
|
|
41
42
|
|
|
42
|
-
context "has_one" do
|
|
43
|
+
context "with has_one" do
|
|
43
44
|
let(:association) { described_class.new(User, :avatar, :url) }
|
|
44
45
|
|
|
45
46
|
describe "#table_name" do
|
|
@@ -50,13 +51,13 @@ describe PgSearch::Configuration::Association do
|
|
|
50
51
|
|
|
51
52
|
describe "#join" do
|
|
52
53
|
let(:expected_sql) do
|
|
53
|
-
|
|
54
|
+
<<~SQL.squish
|
|
54
55
|
LEFT OUTER JOIN
|
|
55
56
|
(SELECT model_id AS id,
|
|
56
57
|
#{column_select} AS #{association.columns.first.alias}
|
|
57
|
-
FROM
|
|
58
|
-
INNER JOIN
|
|
59
|
-
ON
|
|
58
|
+
FROM "#{User.table_name}"
|
|
59
|
+
INNER JOIN "#{association.table_name}"
|
|
60
|
+
ON "#{association.table_name}"."user_id" = "#{User.table_name}"."id") #{association.subselect_alias}
|
|
60
61
|
ON #{association.subselect_alias}.id = model_id
|
|
61
62
|
SQL
|
|
62
63
|
end
|
|
@@ -70,7 +71,7 @@ describe PgSearch::Configuration::Association do
|
|
|
70
71
|
end
|
|
71
72
|
end
|
|
72
73
|
|
|
73
|
-
context "belongs_to" do
|
|
74
|
+
context "with belongs_to" do
|
|
74
75
|
let(:association) { described_class.new(User, :site, :title) }
|
|
75
76
|
|
|
76
77
|
describe "#table_name" do
|
|
@@ -81,13 +82,13 @@ describe PgSearch::Configuration::Association do
|
|
|
81
82
|
|
|
82
83
|
describe "#join" do
|
|
83
84
|
let(:expected_sql) do
|
|
84
|
-
|
|
85
|
+
<<~SQL.squish
|
|
85
86
|
LEFT OUTER JOIN
|
|
86
87
|
(SELECT model_id AS id,
|
|
87
88
|
#{column_select} AS #{association.columns.first.alias}
|
|
88
|
-
FROM
|
|
89
|
-
INNER JOIN
|
|
90
|
-
ON
|
|
89
|
+
FROM "#{User.table_name}"
|
|
90
|
+
INNER JOIN "#{association.table_name}"
|
|
91
|
+
ON "#{association.table_name}"."id" = "#{User.table_name}"."site_id") #{association.subselect_alias}
|
|
91
92
|
ON #{association.subselect_alias}.id = model_id
|
|
92
93
|
SQL
|
|
93
94
|
end
|
|
@@ -101,7 +102,7 @@ describe PgSearch::Configuration::Association do
|
|
|
101
102
|
end
|
|
102
103
|
end
|
|
103
104
|
|
|
104
|
-
context "has_many" do
|
|
105
|
+
context "with has_many" do
|
|
105
106
|
let(:association) { described_class.new(Site, :users, :name) }
|
|
106
107
|
|
|
107
108
|
describe "#table_name" do
|
|
@@ -112,13 +113,13 @@ describe PgSearch::Configuration::Association do
|
|
|
112
113
|
|
|
113
114
|
describe "#join" do
|
|
114
115
|
let(:expected_sql) do
|
|
115
|
-
|
|
116
|
+
<<~SQL.squish
|
|
116
117
|
LEFT OUTER JOIN
|
|
117
118
|
(SELECT model_id AS id,
|
|
118
|
-
string_agg(
|
|
119
|
-
FROM
|
|
120
|
-
INNER JOIN
|
|
121
|
-
ON
|
|
119
|
+
string_agg("#{association.table_name}"."name"::text, ' ') AS #{association.columns.first.alias}
|
|
120
|
+
FROM "#{Site.table_name}"
|
|
121
|
+
INNER JOIN "#{association.table_name}"
|
|
122
|
+
ON "#{association.table_name}"."site_id" = "#{Site.table_name}"."id"
|
|
122
123
|
GROUP BY model_id) #{association.subselect_alias}
|
|
123
124
|
ON #{association.subselect_alias}.id = model_id
|
|
124
125
|
SQL
|
|
@@ -138,3 +139,4 @@ describe PgSearch::Configuration::Association do
|
|
|
138
139
|
end
|
|
139
140
|
end
|
|
140
141
|
end
|
|
142
|
+
# standard:enable RSpec/NestedGroups
|
|
@@ -7,6 +7,7 @@ describe PgSearch::Configuration::Column do
|
|
|
7
7
|
with_model :Model do
|
|
8
8
|
table do |t|
|
|
9
9
|
t.string :name
|
|
10
|
+
t.json :object
|
|
10
11
|
end
|
|
11
12
|
end
|
|
12
13
|
|
|
@@ -14,18 +15,29 @@ describe PgSearch::Configuration::Column do
|
|
|
14
15
|
column = described_class.new("name", nil, Model)
|
|
15
16
|
expect(column.full_name).to eq(%(#{Model.quoted_table_name}."name"))
|
|
16
17
|
end
|
|
18
|
+
|
|
19
|
+
it "returns nested json attributes" do
|
|
20
|
+
column = described_class.new(Arel.sql("object->>'name'"), nil, Model)
|
|
21
|
+
expect(column.full_name).to eq(%(object->>'name'))
|
|
22
|
+
end
|
|
17
23
|
end
|
|
18
24
|
|
|
19
25
|
describe "#to_sql" do
|
|
20
26
|
with_model :Model do
|
|
21
27
|
table do |t|
|
|
22
28
|
t.string :name
|
|
29
|
+
t.json :object
|
|
23
30
|
end
|
|
24
31
|
end
|
|
25
32
|
|
|
26
33
|
it "returns an expression that casts the column to text and coalesces it with an empty string" do
|
|
27
34
|
column = described_class.new("name", nil, Model)
|
|
28
|
-
expect(column.to_sql).to eq(%{coalesce(#{Model.quoted_table_name}."name"::text, '')})
|
|
35
|
+
expect(column.to_sql).to eq(%{coalesce((#{Model.quoted_table_name}."name")::text, '')})
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "returns an expression that casts the nested json attribute to text and coalesces it with an empty string" do
|
|
39
|
+
column = described_class.new(Arel.sql("object->>'name'"), nil, Model)
|
|
40
|
+
expect(column.to_sql).to eq(%{coalesce((object->>'name')::text, '')})
|
|
29
41
|
end
|
|
30
42
|
end
|
|
31
43
|
end
|
|
@@ -18,16 +18,16 @@ describe PgSearch::Configuration::ForeignColumn do
|
|
|
18
18
|
|
|
19
19
|
model do
|
|
20
20
|
include PgSearch::Model
|
|
21
|
-
belongs_to :another_model, class_name:
|
|
21
|
+
belongs_to :another_model, class_name: "AssociatedModel"
|
|
22
22
|
|
|
23
|
-
pg_search_scope :with_another, associated_against: {
|
|
23
|
+
pg_search_scope :with_another, associated_against: {another_model: :title}
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
it "returns a consistent string" do
|
|
28
28
|
association = PgSearch::Configuration::Association.new(Model,
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
:another_model,
|
|
30
|
+
:title)
|
|
31
31
|
foreign_column = described_class.new("title", nil, Model, association)
|
|
32
32
|
|
|
33
33
|
column_alias = foreign_column.alias
|
|
@@ -18,12 +18,12 @@ describe PgSearch::Features::DMetaphone do
|
|
|
18
18
|
PgSearch::Configuration::Column.new(:content, nil, Model)
|
|
19
19
|
]
|
|
20
20
|
options = {}
|
|
21
|
-
config =
|
|
21
|
+
config = instance_double(PgSearch::Configuration, :config, ignore: [])
|
|
22
22
|
normalizer = PgSearch::Normalizer.new(config)
|
|
23
23
|
|
|
24
24
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
|
25
25
|
expect(feature.rank.to_sql).to eq(
|
|
26
|
-
%{(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))}
|
|
26
|
+
%{(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))}
|
|
27
27
|
)
|
|
28
28
|
end
|
|
29
29
|
end
|
|
@@ -43,12 +43,12 @@ describe PgSearch::Features::DMetaphone do
|
|
|
43
43
|
PgSearch::Configuration::Column.new(:content, nil, Model)
|
|
44
44
|
]
|
|
45
45
|
options = {}
|
|
46
|
-
config =
|
|
46
|
+
config = instance_double(PgSearch::Configuration, :config, ignore: [])
|
|
47
47
|
normalizer = PgSearch::Normalizer.new(config)
|
|
48
48
|
|
|
49
49
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
|
50
50
|
expect(feature.conditions.to_sql).to eq(
|
|
51
|
-
%{((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') || ' ''')))}
|
|
51
|
+
%{((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') || ' ''')))}
|
|
52
52
|
)
|
|
53
53
|
end
|
|
54
54
|
end
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require "ostruct"
|
|
5
5
|
|
|
6
|
+
# standard:disable RSpec/MultipleMemoizedHelpers, RSpec/NestedGroups
|
|
6
7
|
describe PgSearch::Features::Trigram do
|
|
7
8
|
subject(:feature) { described_class.new(query, options, columns, Model, normalizer) }
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
let(:query) { "lolwut" }
|
|
9
11
|
let(:options) { {} }
|
|
10
12
|
let(:columns) {
|
|
11
13
|
[
|
|
@@ -17,8 +19,10 @@ describe PgSearch::Features::Trigram do
|
|
|
17
19
|
let(:config) { OpenStruct.new(ignore: []) }
|
|
18
20
|
|
|
19
21
|
let(:coalesced_columns) do
|
|
20
|
-
|
|
21
|
-
coalesce(#{Model.quoted_table_name}."name"
|
|
22
|
+
<<~SQL.squish
|
|
23
|
+
coalesce((#{Model.quoted_table_name}."name")::text, '')
|
|
24
|
+
|| ' '
|
|
25
|
+
|| coalesce((#{Model.quoted_table_name}."content")::text, '')
|
|
22
26
|
SQL
|
|
23
27
|
end
|
|
24
28
|
|
|
@@ -29,15 +33,15 @@ describe PgSearch::Features::Trigram do
|
|
|
29
33
|
end
|
|
30
34
|
end
|
|
31
35
|
|
|
32
|
-
describe
|
|
33
|
-
it
|
|
36
|
+
describe "conditions" do
|
|
37
|
+
it "escapes the search document and query" do
|
|
34
38
|
config.ignore = []
|
|
35
39
|
expect(feature.conditions.to_sql).to eq("('#{query}' % (#{coalesced_columns}))")
|
|
36
40
|
end
|
|
37
41
|
|
|
38
|
-
context
|
|
42
|
+
context "when searching by word_similarity" do
|
|
39
43
|
let(:options) do
|
|
40
|
-
{
|
|
44
|
+
{word_similarity: true}
|
|
41
45
|
end
|
|
42
46
|
|
|
43
47
|
it 'uses the "<%" operator when searching by word_similarity' do
|
|
@@ -46,17 +50,17 @@ describe PgSearch::Features::Trigram do
|
|
|
46
50
|
end
|
|
47
51
|
end
|
|
48
52
|
|
|
49
|
-
context
|
|
50
|
-
it
|
|
53
|
+
context "when ignoring accents" do
|
|
54
|
+
it "escapes the search document and query, but not the accent function" do
|
|
51
55
|
config.ignore = [:accents]
|
|
52
56
|
expect(feature.conditions.to_sql).to eq("(unaccent('#{query}') % (unaccent(#{coalesced_columns})))")
|
|
53
57
|
end
|
|
54
58
|
end
|
|
55
59
|
|
|
56
|
-
context
|
|
57
|
-
context
|
|
60
|
+
context "when a threshold is specified" do
|
|
61
|
+
context "when searching by similarity" do
|
|
58
62
|
let(:options) do
|
|
59
|
-
{
|
|
63
|
+
{threshold: 0.5}
|
|
60
64
|
end
|
|
61
65
|
|
|
62
66
|
it 'uses a minimum similarity expression instead of the "%" operator' do
|
|
@@ -66,9 +70,9 @@ describe PgSearch::Features::Trigram do
|
|
|
66
70
|
end
|
|
67
71
|
end
|
|
68
72
|
|
|
69
|
-
context
|
|
73
|
+
context "when searching by word_similarity" do
|
|
70
74
|
let(:options) do
|
|
71
|
-
{
|
|
75
|
+
{threshold: 0.5, word_similarity: true}
|
|
72
76
|
end
|
|
73
77
|
|
|
74
78
|
it 'uses a minimum similarity expression instead of the "<%" operator' do
|
|
@@ -79,30 +83,30 @@ describe PgSearch::Features::Trigram do
|
|
|
79
83
|
end
|
|
80
84
|
end
|
|
81
85
|
|
|
82
|
-
context
|
|
83
|
-
context
|
|
84
|
-
let(:options) { {
|
|
86
|
+
context "when only certain columns are selected" do
|
|
87
|
+
context "with one column" do
|
|
88
|
+
let(:options) { {only: :name} }
|
|
85
89
|
|
|
86
|
-
it
|
|
87
|
-
|
|
88
|
-
coalesced_column = "coalesce(#{Model.quoted_table_name}.\"name\"::text, '')"
|
|
90
|
+
it "only searches against the select column" do
|
|
91
|
+
coalesced_column = "coalesce((#{Model.quoted_table_name}.\"name\")::text, '')"
|
|
89
92
|
expect(feature.conditions.to_sql).to eq("('#{query}' % (#{coalesced_column}))")
|
|
90
93
|
end
|
|
91
94
|
end
|
|
92
|
-
context 'multiple columns' do
|
|
93
|
-
let(:options) { { only: %i[name content] } }
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
context "with multiple columns" do
|
|
97
|
+
let(:options) { {only: %i[name content]} }
|
|
98
|
+
|
|
99
|
+
it "concatenates when multiples columns are selected" do
|
|
97
100
|
expect(feature.conditions.to_sql).to eq("('#{query}' % (#{coalesced_columns}))")
|
|
98
101
|
end
|
|
99
102
|
end
|
|
100
103
|
end
|
|
101
104
|
end
|
|
102
105
|
|
|
103
|
-
describe
|
|
104
|
-
it
|
|
106
|
+
describe "#rank" do
|
|
107
|
+
it "returns an expression using the similarity() function" do
|
|
105
108
|
expect(feature.rank.to_sql).to eq("(similarity('#{query}', (#{coalesced_columns})))")
|
|
106
109
|
end
|
|
107
110
|
end
|
|
108
111
|
end
|
|
112
|
+
# standard:enable RSpec/MultipleMemoizedHelpers, RSpec/NestedGroups
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "spec_helper"
|
|
4
|
-
require "active_support/
|
|
4
|
+
require "active_support/core_ext/kernel/reporting"
|
|
5
5
|
|
|
6
6
|
describe PgSearch::Features::TSearch do
|
|
7
7
|
describe "#rank" do
|
|
@@ -19,14 +19,32 @@ describe PgSearch::Features::TSearch do
|
|
|
19
19
|
PgSearch::Configuration::Column.new(:content, nil, Model)
|
|
20
20
|
]
|
|
21
21
|
options = {}
|
|
22
|
-
config =
|
|
22
|
+
config = instance_double(PgSearch::Configuration, :config, ignore: [])
|
|
23
23
|
normalizer = PgSearch::Normalizer.new(config)
|
|
24
24
|
|
|
25
25
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
|
26
26
|
expect(feature.rank.to_sql).to eq(
|
|
27
|
-
%{(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))}
|
|
27
|
+
%{(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))}
|
|
28
28
|
)
|
|
29
29
|
end
|
|
30
|
+
|
|
31
|
+
context "with a tsvector column and a custom normalization" do
|
|
32
|
+
it "works?" do
|
|
33
|
+
query = "query"
|
|
34
|
+
columns = [
|
|
35
|
+
PgSearch::Configuration::Column.new(:name, nil, Model),
|
|
36
|
+
PgSearch::Configuration::Column.new(:content, nil, Model)
|
|
37
|
+
]
|
|
38
|
+
options = {tsvector_column: :my_tsvector, normalization: 2}
|
|
39
|
+
config = instance_double(PgSearch::Configuration, :config, ignore: [])
|
|
40
|
+
normalizer = PgSearch::Normalizer.new(config)
|
|
41
|
+
|
|
42
|
+
feature = described_class.new(query, options, columns, Model, normalizer)
|
|
43
|
+
expect(feature.rank.to_sql).to eq(
|
|
44
|
+
%{(ts_rank((#{Model.quoted_table_name}."my_tsvector"), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 2))}
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
30
48
|
end
|
|
31
49
|
|
|
32
50
|
describe "#conditions" do
|
|
@@ -44,12 +62,12 @@ describe PgSearch::Features::TSearch do
|
|
|
44
62
|
PgSearch::Configuration::Column.new(:content, nil, Model)
|
|
45
63
|
]
|
|
46
64
|
options = {}
|
|
47
|
-
config =
|
|
65
|
+
config = instance_double(PgSearch::Configuration, :config, ignore: [])
|
|
48
66
|
normalizer = PgSearch::Normalizer.new(config)
|
|
49
67
|
|
|
50
68
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
|
51
69
|
expect(feature.conditions.to_sql).to eq(
|
|
52
|
-
%{((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))) @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))}
|
|
70
|
+
%{((to_tsvector('simple', coalesce((#{Model.quoted_table_name}."name")::text, '')) || to_tsvector('simple', coalesce((#{Model.quoted_table_name}."content")::text, ''))) @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))}
|
|
53
71
|
)
|
|
54
72
|
end
|
|
55
73
|
|
|
@@ -60,13 +78,13 @@ describe PgSearch::Features::TSearch do
|
|
|
60
78
|
PgSearch::Configuration::Column.new(:name, nil, Model),
|
|
61
79
|
PgSearch::Configuration::Column.new(:content, nil, Model)
|
|
62
80
|
]
|
|
63
|
-
options = {
|
|
64
|
-
config =
|
|
81
|
+
options = {negation: true}
|
|
82
|
+
config = instance_double(PgSearch::Configuration, :config, ignore: [])
|
|
65
83
|
normalizer = PgSearch::Normalizer.new(config)
|
|
66
84
|
|
|
67
85
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
|
68
86
|
expect(feature.conditions.to_sql).to eq(
|
|
69
|
-
%{((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))) @@ (to_tsquery('simple', '!' || ''' ' || 'query' || ' ''')))}
|
|
87
|
+
%{((to_tsvector('simple', coalesce((#{Model.quoted_table_name}."name")::text, '')) || to_tsvector('simple', coalesce((#{Model.quoted_table_name}."content")::text, ''))) @@ (to_tsquery('simple', '!' || ''' ' || 'query' || ' ''')))}
|
|
70
88
|
)
|
|
71
89
|
end
|
|
72
90
|
end
|
|
@@ -78,49 +96,49 @@ describe PgSearch::Features::TSearch do
|
|
|
78
96
|
PgSearch::Configuration::Column.new(:name, nil, Model),
|
|
79
97
|
PgSearch::Configuration::Column.new(:content, nil, Model)
|
|
80
98
|
]
|
|
81
|
-
options = {
|
|
82
|
-
config =
|
|
99
|
+
options = {negation: false}
|
|
100
|
+
config = instance_double(PgSearch::Configuration, :config, ignore: [])
|
|
83
101
|
normalizer = PgSearch::Normalizer.new(config)
|
|
84
102
|
|
|
85
103
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
|
86
104
|
expect(feature.conditions.to_sql).to eq(
|
|
87
|
-
%{((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))) @@ (to_tsquery('simple', ''' ' || '!query' || ' ''')))}
|
|
105
|
+
%{((to_tsvector('simple', coalesce((#{Model.quoted_table_name}."name")::text, '')) || to_tsvector('simple', coalesce((#{Model.quoted_table_name}."content")::text, ''))) @@ (to_tsquery('simple', ''' ' || '!query' || ' ''')))}
|
|
88
106
|
)
|
|
89
107
|
end
|
|
90
108
|
end
|
|
91
109
|
|
|
92
110
|
context "when options[:tsvector_column] is a string" do
|
|
93
|
-
it
|
|
111
|
+
it "uses the tsvector column" do
|
|
94
112
|
query = "query"
|
|
95
113
|
columns = [
|
|
96
114
|
PgSearch::Configuration::Column.new(:name, nil, Model),
|
|
97
115
|
PgSearch::Configuration::Column.new(:content, nil, Model)
|
|
98
116
|
]
|
|
99
|
-
options = {
|
|
100
|
-
config =
|
|
117
|
+
options = {tsvector_column: "my_tsvector"}
|
|
118
|
+
config = instance_double(PgSearch::Configuration, :config, ignore: [])
|
|
101
119
|
normalizer = PgSearch::Normalizer.new(config)
|
|
102
120
|
|
|
103
121
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
|
104
122
|
expect(feature.conditions.to_sql).to eq(
|
|
105
|
-
%{((#{Model.quoted_table_name}
|
|
123
|
+
%{((#{Model.quoted_table_name}."my_tsvector") @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))}
|
|
106
124
|
)
|
|
107
125
|
end
|
|
108
126
|
end
|
|
109
127
|
|
|
110
128
|
context "when options[:tsvector_column] is an array of strings" do
|
|
111
|
-
it
|
|
129
|
+
it "uses the tsvector column" do
|
|
112
130
|
query = "query"
|
|
113
131
|
columns = [
|
|
114
132
|
PgSearch::Configuration::Column.new(:name, nil, Model),
|
|
115
133
|
PgSearch::Configuration::Column.new(:content, nil, Model)
|
|
116
134
|
]
|
|
117
|
-
options = {
|
|
118
|
-
config =
|
|
135
|
+
options = {tsvector_column: ["tsvector1", "tsvector2"]}
|
|
136
|
+
config = instance_double(PgSearch::Configuration, :config, ignore: [])
|
|
119
137
|
normalizer = PgSearch::Normalizer.new(config)
|
|
120
138
|
|
|
121
139
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
|
122
140
|
expect(feature.conditions.to_sql).to eq(
|
|
123
|
-
%{((#{Model.quoted_table_name}
|
|
141
|
+
%{((#{Model.quoted_table_name}."tsvector1" || #{Model.quoted_table_name}."tsvector2") @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))}
|
|
124
142
|
)
|
|
125
143
|
end
|
|
126
144
|
end
|
|
@@ -141,17 +159,18 @@ describe PgSearch::Features::TSearch do
|
|
|
141
159
|
]
|
|
142
160
|
options = {}
|
|
143
161
|
|
|
144
|
-
config =
|
|
162
|
+
config = instance_double(PgSearch::Configuration, :config, ignore: [])
|
|
145
163
|
normalizer = PgSearch::Normalizer.new(config)
|
|
146
164
|
|
|
147
165
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
|
148
166
|
expect(feature.highlight.to_sql).to eq(
|
|
149
|
-
"(ts_headline('simple', (coalesce(#{Model.quoted_table_name}.\"name\"::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), ''))"
|
|
167
|
+
"(ts_headline('simple', (coalesce((#{Model.quoted_table_name}.\"name\")::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), ''))"
|
|
150
168
|
)
|
|
151
169
|
end
|
|
152
170
|
|
|
153
171
|
context "when options[:dictionary] is passed" do
|
|
154
|
-
|
|
172
|
+
# standard:disable RSpec/ExampleLength
|
|
173
|
+
it "uses the provided dictionary" do
|
|
155
174
|
query = "query"
|
|
156
175
|
columns = [
|
|
157
176
|
PgSearch::Configuration::Column.new(:name, nil, Model),
|
|
@@ -165,18 +184,20 @@ describe PgSearch::Features::TSearch do
|
|
|
165
184
|
}
|
|
166
185
|
}
|
|
167
186
|
|
|
168
|
-
config =
|
|
187
|
+
config = instance_double(PgSearch::Configuration, :config, ignore: [])
|
|
169
188
|
normalizer = PgSearch::Normalizer.new(config)
|
|
170
189
|
|
|
171
190
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
|
172
191
|
|
|
173
|
-
expected_sql = %{(ts_headline('spanish', (coalesce(#{Model.quoted_table_name}."name"::text, '') || ' ' || coalesce(#{Model.quoted_table_name}."content"::text, '')), (to_tsquery('spanish', ''' ' || 'query' || ' ''')), 'StartSel = "<b>", StopSel = "</b>"'))}
|
|
192
|
+
expected_sql = %{(ts_headline('spanish', (coalesce((#{Model.quoted_table_name}."name")::text, '') || ' ' || coalesce((#{Model.quoted_table_name}."content")::text, '')), (to_tsquery('spanish', ''' ' || 'query' || ' ''')), 'StartSel = "<b>", StopSel = "</b>"'))}
|
|
174
193
|
|
|
175
194
|
expect(feature.highlight.to_sql).to eq(expected_sql)
|
|
176
195
|
end
|
|
196
|
+
# standard:enable RSpec/ExampleLength
|
|
177
197
|
end
|
|
178
198
|
|
|
179
199
|
context "when options[:highlight] has options set" do
|
|
200
|
+
# standard:disable RSpec/ExampleLength
|
|
180
201
|
it "passes the options to ts_headline" do
|
|
181
202
|
query = "query"
|
|
182
203
|
columns = [
|
|
@@ -185,26 +206,28 @@ describe PgSearch::Features::TSearch do
|
|
|
185
206
|
options = {
|
|
186
207
|
highlight: {
|
|
187
208
|
StartSel: '<start class="search">',
|
|
188
|
-
StopSel:
|
|
209
|
+
StopSel: "<stop>",
|
|
189
210
|
MaxWords: 123,
|
|
190
211
|
MinWords: 456,
|
|
191
212
|
ShortWord: 4,
|
|
192
213
|
HighlightAll: true,
|
|
193
214
|
MaxFragments: 3,
|
|
194
|
-
FragmentDelimiter:
|
|
215
|
+
FragmentDelimiter: "…"
|
|
195
216
|
}
|
|
196
217
|
}
|
|
197
218
|
|
|
198
|
-
config =
|
|
219
|
+
config = instance_double(PgSearch::Configuration, :config, ignore: [])
|
|
199
220
|
normalizer = PgSearch::Normalizer.new(config)
|
|
200
221
|
|
|
201
222
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
|
202
223
|
|
|
203
|
-
expected_sql = %{(ts_headline('simple', (coalesce(#{Model.quoted_table_name}."name"::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 'StartSel = "<start class=""search"">", StopSel = "<stop>", MaxFragments = 3, MaxWords = 123, MinWords = 456, ShortWord = 4, FragmentDelimiter = "…", HighlightAll = TRUE'))}
|
|
224
|
+
expected_sql = %{(ts_headline('simple', (coalesce((#{Model.quoted_table_name}."name")::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 'StartSel = "<start class=""search"">", StopSel = "<stop>", MaxFragments = 3, MaxWords = 123, MinWords = 456, ShortWord = 4, FragmentDelimiter = "…", HighlightAll = TRUE'))}
|
|
204
225
|
|
|
205
226
|
expect(feature.highlight.to_sql).to eq(expected_sql)
|
|
206
227
|
end
|
|
228
|
+
# standard:enable RSpec/ExampleLength
|
|
207
229
|
|
|
230
|
+
# standard:disable RSpec/ExampleLength
|
|
208
231
|
it "passes deprecated options to ts_headline" do
|
|
209
232
|
query = "query"
|
|
210
233
|
columns = [
|
|
@@ -213,26 +236,27 @@ describe PgSearch::Features::TSearch do
|
|
|
213
236
|
options = {
|
|
214
237
|
highlight: {
|
|
215
238
|
start_sel: '<start class="search">',
|
|
216
|
-
stop_sel:
|
|
239
|
+
stop_sel: "<stop>",
|
|
217
240
|
max_words: 123,
|
|
218
241
|
min_words: 456,
|
|
219
242
|
short_word: 4,
|
|
220
243
|
highlight_all: false,
|
|
221
244
|
max_fragments: 3,
|
|
222
|
-
fragment_delimiter:
|
|
245
|
+
fragment_delimiter: "…"
|
|
223
246
|
}
|
|
224
247
|
}
|
|
225
248
|
|
|
226
|
-
config =
|
|
249
|
+
config = instance_double(PgSearch::Configuration, :config, ignore: [])
|
|
227
250
|
normalizer = PgSearch::Normalizer.new(config)
|
|
228
251
|
|
|
229
252
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
|
230
253
|
|
|
231
|
-
highlight_sql =
|
|
232
|
-
expected_sql = %{(ts_headline('simple', (coalesce(#{Model.quoted_table_name}."name"::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 'StartSel = "<start class=""search"">", StopSel = "<stop>", MaxFragments = 3, MaxWords = 123, MinWords = 456, ShortWord = 4, FragmentDelimiter = "…", HighlightAll = FALSE'))}
|
|
254
|
+
highlight_sql = silence_warnings { feature.highlight.to_sql }
|
|
255
|
+
expected_sql = %{(ts_headline('simple', (coalesce((#{Model.quoted_table_name}."name")::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 'StartSel = "<start class=""search"">", StopSel = "<stop>", MaxFragments = 3, MaxWords = 123, MinWords = 456, ShortWord = 4, FragmentDelimiter = "…", HighlightAll = FALSE'))}
|
|
233
256
|
|
|
234
257
|
expect(highlight_sql).to eq(expected_sql)
|
|
235
258
|
end
|
|
259
|
+
# standard:enable RSpec/ExampleLength
|
|
236
260
|
end
|
|
237
261
|
end
|
|
238
262
|
end
|