pg_search 0.7.0 → 0.7.1
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/.gitignore +1 -0
- data/CHANGELOG.md +187 -0
- data/CONTRIBUTING.md +17 -0
- data/Gemfile +1 -1
- data/README.md +844 -0
- data/Rakefile +0 -11
- data/lib/pg_search/features/trigram.rb +8 -3
- data/lib/pg_search/version.rb +1 -1
- data/pg_search.gemspec +1 -3
- data/spec/integration/associations_spec.rb +42 -0
- data/spec/integration/pagination_spec.rb +6 -3
- data/spec/integration/pg_search_spec.rb +21 -2
- data/spec/lib/pg_search/document_spec.rb +2 -2
- data/spec/lib/pg_search/features/dmetaphone_spec.rb +2 -2
- data/spec/lib/pg_search/features/trigram_spec.rb +36 -17
- data/spec/lib/pg_search/features/tsearch_spec.rb +2 -2
- data/spec/lib/pg_search/multisearch/rebuilder_spec.rb +18 -2
- data/spec/lib/pg_search/normalizer_spec.rb +7 -7
- data/spec/spec_helper.rb +7 -9
- metadata +7 -34
- data/CHANGELOG.rdoc +0 -178
- data/README.rdoc +0 -669
data/Rakefile
CHANGED
@@ -11,14 +11,3 @@ desc "Run all specs"
|
|
11
11
|
task "spec" do
|
12
12
|
bundle_exec("rspec spec")
|
13
13
|
end
|
14
|
-
|
15
|
-
task "doc" do
|
16
|
-
bundle_exec("rspec --format d spec")
|
17
|
-
end
|
18
|
-
|
19
|
-
namespace "doc" do
|
20
|
-
desc "Generate README and preview in browser"
|
21
|
-
task "readme" do
|
22
|
-
sh "rdoc -c utf8 README.rdoc && open doc/README_rdoc.html"
|
23
|
-
end
|
24
|
-
end
|
@@ -3,7 +3,7 @@ module PgSearch
|
|
3
3
|
class Trigram < Feature
|
4
4
|
def conditions
|
5
5
|
Arel::Nodes::Grouping.new(
|
6
|
-
Arel::Nodes::InfixOperation.new("%", normalized_document,
|
6
|
+
Arel::Nodes::InfixOperation.new("%", normalized_document, normalized_query)
|
7
7
|
)
|
8
8
|
end
|
9
9
|
|
@@ -13,7 +13,7 @@ module PgSearch
|
|
13
13
|
"similarity",
|
14
14
|
[
|
15
15
|
normalized_document,
|
16
|
-
|
16
|
+
normalized_query
|
17
17
|
]
|
18
18
|
)
|
19
19
|
)
|
@@ -22,7 +22,12 @@ module PgSearch
|
|
22
22
|
private
|
23
23
|
|
24
24
|
def normalized_document
|
25
|
-
Arel::Nodes::Grouping.new(
|
25
|
+
Arel::Nodes::Grouping.new(Arel.sql(normalize(document)))
|
26
|
+
end
|
27
|
+
|
28
|
+
def normalized_query
|
29
|
+
sanitized_query = connection.quote(query)
|
30
|
+
Arel.sql(normalize(sanitized_query))
|
26
31
|
end
|
27
32
|
end
|
28
33
|
end
|
data/lib/pg_search/version.rb
CHANGED
data/pg_search.gemspec
CHANGED
@@ -23,11 +23,9 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.add_dependency 'arel'
|
24
24
|
|
25
25
|
s.add_development_dependency 'rake'
|
26
|
-
s.add_development_dependency 'rdoc'
|
27
26
|
s.add_development_dependency 'pry'
|
28
|
-
s.add_development_dependency 'rspec'
|
27
|
+
s.add_development_dependency 'rspec', '>= 2.14'
|
29
28
|
s.add_development_dependency 'with_model'
|
30
|
-
s.add_development_dependency 'will_paginate'
|
31
29
|
|
32
30
|
s.required_ruby_version = ">= 1.9.2"
|
33
31
|
end
|
@@ -467,4 +467,46 @@ describe PgSearch do
|
|
467
467
|
results.should_not include(*excluded)
|
468
468
|
end
|
469
469
|
end
|
470
|
+
|
471
|
+
context "chained onto a has_many association" do
|
472
|
+
with_model :Company do
|
473
|
+
model do
|
474
|
+
has_many :positions
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
with_model :Position do
|
479
|
+
table do |t|
|
480
|
+
t.string :title
|
481
|
+
t.belongs_to :company
|
482
|
+
end
|
483
|
+
|
484
|
+
model do
|
485
|
+
include PgSearch
|
486
|
+
pg_search_scope :search, :against => :title, :using => [:tsearch, :trigram]
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
# https://github.com/Casecommons/pg_search/issues/106
|
491
|
+
it "should handle numbers in a trigram query properly" do
|
492
|
+
company = Company.create!
|
493
|
+
another_company = Company.create!
|
494
|
+
|
495
|
+
included = [
|
496
|
+
Position.create!(company_id: company.id, title: "teller 1"),
|
497
|
+
Position.create!(company_id: company.id, title: "teller 2") # close enough
|
498
|
+
]
|
499
|
+
|
500
|
+
excluded = [
|
501
|
+
Position.create!(company_id: nil, title: "teller 1"),
|
502
|
+
Position.create!(company_id: another_company.id, title: "teller 1"),
|
503
|
+
Position.create!(company_id: company.id, title: "penn 1")
|
504
|
+
]
|
505
|
+
|
506
|
+
results = company.positions.search('teller 1')
|
507
|
+
|
508
|
+
results.should include(*included)
|
509
|
+
results.should_not include(*excluded)
|
510
|
+
end
|
511
|
+
end
|
470
512
|
end
|
@@ -1,8 +1,7 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe "pagination" do
|
4
|
-
describe "
|
5
|
-
require 'will_paginate/active_record'
|
4
|
+
describe "using LIMIT and OFFSET" do
|
6
5
|
with_model :PaginatedModel do
|
7
6
|
table do |t|
|
8
7
|
t.string :name
|
@@ -10,8 +9,12 @@ describe "pagination" do
|
|
10
9
|
|
11
10
|
model do
|
12
11
|
include PgSearch
|
13
|
-
self.per_page = 2
|
14
12
|
pg_search_scope :search_name, :against => :name
|
13
|
+
|
14
|
+
def self.page(page_number)
|
15
|
+
offset = (page_number - 1) * 2
|
16
|
+
limit(2).offset(offset)
|
17
|
+
end
|
15
18
|
end
|
16
19
|
end
|
17
20
|
|
@@ -241,7 +241,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
241
241
|
|
242
242
|
it "accepts non-string queries and calls #to_s on them" do
|
243
243
|
foo = ModelWithPgSearch.create!(:content => "foo")
|
244
|
-
not_a_string =
|
244
|
+
not_a_string = double(:to_s => "foo")
|
245
245
|
ModelWithPgSearch.search_content(not_a_string).should == [foo]
|
246
246
|
end
|
247
247
|
|
@@ -557,6 +557,11 @@ describe "an Active Record model which includes PgSearch" do
|
|
557
557
|
:against => :title,
|
558
558
|
:using => :trigram
|
559
559
|
|
560
|
+
ModelWithPgSearch.pg_search_scope :with_trigram_and_ignoring_accents,
|
561
|
+
:against => :title,
|
562
|
+
:ignoring => :accents,
|
563
|
+
:using => :trigram
|
564
|
+
|
560
565
|
ModelWithPgSearch.pg_search_scope :with_tsearch_and_trigram,
|
561
566
|
:against => :title,
|
562
567
|
:using => [
|
@@ -566,6 +571,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
566
571
|
|
567
572
|
ModelWithPgSearch.pg_search_scope :complex_search,
|
568
573
|
:against => [:content, :title],
|
574
|
+
:ignoring => :accents,
|
569
575
|
:using => {
|
570
576
|
:tsearch => {:dictionary => 'english'},
|
571
577
|
:dmetaphone => {},
|
@@ -579,14 +585,26 @@ describe "an Active Record model which includes PgSearch" do
|
|
579
585
|
# matches trigram only
|
580
586
|
trigram_query = "ling is grouty"
|
581
587
|
ModelWithPgSearch.with_trigram(trigram_query).should include(record)
|
588
|
+
ModelWithPgSearch.with_trigram_and_ignoring_accents(trigram_query).should include(record)
|
582
589
|
ModelWithPgSearch.with_tsearch(trigram_query).should_not include(record)
|
583
590
|
ModelWithPgSearch.with_tsearch_and_trigram(trigram_query).should == [record]
|
584
591
|
ModelWithPgSearch.complex_search(trigram_query).should include(record)
|
585
592
|
|
593
|
+
# matches accent
|
594
|
+
# \303\266 is o with diaeresis
|
595
|
+
# \303\272 is u with acute accent
|
596
|
+
accent_query = "gr\303\266\303\272ty"
|
597
|
+
ModelWithPgSearch.with_trigram(accent_query).should_not include(record)
|
598
|
+
ModelWithPgSearch.with_trigram_and_ignoring_accents(accent_query).should include(record)
|
599
|
+
ModelWithPgSearch.with_tsearch(accent_query).should_not include(record)
|
600
|
+
ModelWithPgSearch.with_tsearch_and_trigram(accent_query).should be_empty
|
601
|
+
ModelWithPgSearch.complex_search(accent_query).should include(record)
|
602
|
+
|
586
603
|
# matches tsearch only
|
587
604
|
tsearch_query = "tiles"
|
588
605
|
ModelWithPgSearch.with_tsearch(tsearch_query).should include(record)
|
589
606
|
ModelWithPgSearch.with_trigram(tsearch_query).should_not include(record)
|
607
|
+
ModelWithPgSearch.with_trigram_and_ignoring_accents(tsearch_query).should_not include(record)
|
590
608
|
ModelWithPgSearch.with_tsearch_and_trigram(tsearch_query).should == [record]
|
591
609
|
ModelWithPgSearch.complex_search(tsearch_query).should include(record)
|
592
610
|
|
@@ -594,6 +612,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
594
612
|
dmetaphone_query = "tyling"
|
595
613
|
ModelWithPgSearch.with_tsearch(dmetaphone_query).should_not include(record)
|
596
614
|
ModelWithPgSearch.with_trigram(dmetaphone_query).should_not include(record)
|
615
|
+
ModelWithPgSearch.with_trigram_and_ignoring_accents(dmetaphone_query).should_not include(record)
|
597
616
|
ModelWithPgSearch.with_tsearch_and_trigram(dmetaphone_query).should_not include(record)
|
598
617
|
ModelWithPgSearch.complex_search(dmetaphone_query).should include(record)
|
599
618
|
end
|
@@ -612,7 +631,7 @@ describe "an Active Record model which includes PgSearch" do
|
|
612
631
|
end
|
613
632
|
|
614
633
|
it "should pass the custom configuration down to the specified feature" do
|
615
|
-
stub_feature =
|
634
|
+
stub_feature = double(
|
616
635
|
:conditions => Arel::Nodes::Grouping.new(Arel.sql("1 = 1")),
|
617
636
|
:rank => Arel::Nodes::Grouping.new(Arel.sql("1.0"))
|
618
637
|
)
|
@@ -28,7 +28,7 @@ describe PgSearch::Document do
|
|
28
28
|
let(:multisearchable_options) { {:against => :some_content} }
|
29
29
|
let(:text) { "foo bar" }
|
30
30
|
before do
|
31
|
-
searchable.stub
|
31
|
+
searchable.stub(:some_content => text)
|
32
32
|
document.valid?
|
33
33
|
end
|
34
34
|
|
@@ -38,7 +38,7 @@ describe PgSearch::Document do
|
|
38
38
|
context "when searching against multiple columns" do
|
39
39
|
let(:multisearchable_options) { {:against => [:attr1, :attr2]} }
|
40
40
|
before do
|
41
|
-
searchable.stub
|
41
|
+
searchable.stub(:attr1 => "1", :attr2 => "2")
|
42
42
|
document.valid?
|
43
43
|
end
|
44
44
|
|
@@ -16,7 +16,7 @@ describe PgSearch::Features::DMetaphone do
|
|
16
16
|
PgSearch::Configuration::Column.new(:content, nil, Model),
|
17
17
|
]
|
18
18
|
options = {}
|
19
|
-
config =
|
19
|
+
config = double(:config, :ignore => [])
|
20
20
|
normalizer = PgSearch::Normalizer.new(config)
|
21
21
|
|
22
22
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
@@ -40,7 +40,7 @@ describe PgSearch::Features::DMetaphone do
|
|
40
40
|
PgSearch::Configuration::Column.new(:content, nil, Model),
|
41
41
|
]
|
42
42
|
options = {}
|
43
|
-
config =
|
43
|
+
config = double(:config, :ignore => [])
|
44
44
|
normalizer = PgSearch::Normalizer.new(config)
|
45
45
|
|
46
46
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
@@ -1,26 +1,45 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
2
3
|
|
3
4
|
describe PgSearch::Features::Trigram do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
subject(:feature) { described_class.new(query, options, columns, Model, normalizer) }
|
6
|
+
let(:query) { 'lolwut' }
|
7
|
+
let(:options) { {} }
|
8
|
+
let(:columns) {[
|
9
|
+
PgSearch::Configuration::Column.new(:name, nil, Model),
|
10
|
+
PgSearch::Configuration::Column.new(:content, nil, Model)
|
11
|
+
]}
|
12
|
+
let(:normalizer) { PgSearch::Normalizer.new(config) }
|
13
|
+
let(:config) { OpenStruct.new(:ignore => [], :postgresql_version => 90000) }
|
14
|
+
let(:coalesced_colums) { %Q{coalesce(#{Model.quoted_table_name}."name"::text, '') || ' ' || \
|
15
|
+
coalesce(#{Model.quoted_table_name}."content"::text, '')}.squeeze(' ') }
|
16
|
+
|
17
|
+
with_model :Model do
|
18
|
+
table do |t|
|
19
|
+
t.string :name
|
20
|
+
t.string :content
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'conditions' do
|
25
|
+
context 'paying attention to accents' do
|
26
|
+
it 'escapes the search document and query' do
|
27
|
+
config.ignore = []
|
28
|
+
expect(feature.conditions.to_sql).to eq("((#{coalesced_colums}) % '#{query}')")
|
9
29
|
end
|
10
30
|
end
|
11
31
|
|
12
|
-
|
13
|
-
query
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
config = stub(:config, :ignore => [])
|
20
|
-
normalizer = PgSearch::Normalizer.new(config)
|
32
|
+
context 'ignoring to accents' do
|
33
|
+
it 'escapes the search document and query, but not the accent function' do
|
34
|
+
config.ignore = [:accents]
|
35
|
+
expect(feature.conditions.to_sql).to eq("((unaccent(#{coalesced_colums})) % unaccent('#{query}'))")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
21
39
|
|
22
|
-
|
23
|
-
|
40
|
+
describe '#rank' do
|
41
|
+
it 'returns an expression using the similarity() function' do
|
42
|
+
expect(feature.rank.to_sql).to eq("(similarity((#{coalesced_colums}), '#{query}'))")
|
24
43
|
end
|
25
44
|
end
|
26
45
|
end
|
@@ -16,7 +16,7 @@ describe PgSearch::Features::TSearch do
|
|
16
16
|
PgSearch::Configuration::Column.new(:content, nil, Model),
|
17
17
|
]
|
18
18
|
options = {}
|
19
|
-
config =
|
19
|
+
config = double(:config, :ignore => [])
|
20
20
|
normalizer = PgSearch::Normalizer.new(config)
|
21
21
|
|
22
22
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
@@ -40,7 +40,7 @@ describe PgSearch::Features::TSearch do
|
|
40
40
|
PgSearch::Configuration::Column.new(:content, nil, Model),
|
41
41
|
]
|
42
42
|
options = {}
|
43
|
-
config =
|
43
|
+
config = double(:config, :ignore => [])
|
44
44
|
normalizer = PgSearch::Normalizer.new(config)
|
45
45
|
|
46
46
|
feature = described_class.new(query, options, columns, Model, normalizer)
|
@@ -84,6 +84,22 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
84
84
|
time = DateTime.parse("2001-01-01")
|
85
85
|
rebuilder = PgSearch::Multisearch::Rebuilder.new(Model, lambda { time } )
|
86
86
|
|
87
|
+
# Handle change in precision of DateTime objects in SQL in Active Record 4.0.1
|
88
|
+
# https://github.com/rails/rails/commit/17f5d8e062909f1fcae25351834d8e89967b645e
|
89
|
+
version_4_0_1_or_newer = (
|
90
|
+
(ActiveRecord::VERSION::MAJOR > 4) ||
|
91
|
+
(ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR >= 1) ||
|
92
|
+
(ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 0 && ActiveRecord::VERSION::TINY >= 1)
|
93
|
+
)
|
94
|
+
|
95
|
+
expected_timestamp =
|
96
|
+
if version_4_0_1_or_newer
|
97
|
+
"2001-01-01 00:00:00.000000"
|
98
|
+
else
|
99
|
+
"2001-01-01 00:00:00"
|
100
|
+
end
|
101
|
+
|
102
|
+
|
87
103
|
expected_sql = <<-SQL.strip_heredoc
|
88
104
|
INSERT INTO "pg_search_documents" (searchable_type, searchable_id, content, created_at, updated_at)
|
89
105
|
SELECT 'Model' AS searchable_type,
|
@@ -91,8 +107,8 @@ describe PgSearch::Multisearch::Rebuilder do
|
|
91
107
|
(
|
92
108
|
coalesce(#{Model.quoted_table_name}.name::text, '')
|
93
109
|
) AS content,
|
94
|
-
'
|
95
|
-
'
|
110
|
+
'#{expected_timestamp}' AS created_at,
|
111
|
+
'#{expected_timestamp}' AS updated_at
|
96
112
|
FROM #{Model.quoted_table_name}
|
97
113
|
SQL
|
98
114
|
|
@@ -6,7 +6,7 @@ describe PgSearch::Normalizer do
|
|
6
6
|
context "when config[:ignore] includes :accents" do
|
7
7
|
context "when passed an Arel node" do
|
8
8
|
it "wraps the expression in unaccent()" do
|
9
|
-
config =
|
9
|
+
config = double("config", :ignore => [:accents], :postgresql_version => 90000)
|
10
10
|
node = Arel::Nodes::NamedFunction.new("foo", ["bar"])
|
11
11
|
|
12
12
|
normalizer = PgSearch::Normalizer.new(config)
|
@@ -18,7 +18,7 @@ describe PgSearch::Normalizer do
|
|
18
18
|
PgSearch.stub(:unaccent_function).and_return("my_unaccent")
|
19
19
|
node = Arel::Nodes::NamedFunction.new("foo", ["bar"])
|
20
20
|
|
21
|
-
config =
|
21
|
+
config = double("config", :ignore => [:accents], :postgresql_version => 90000)
|
22
22
|
|
23
23
|
normalizer = PgSearch::Normalizer.new(config)
|
24
24
|
normalizer.add_normalization(node).should == "my_unaccent(foo('bar'))"
|
@@ -28,7 +28,7 @@ describe PgSearch::Normalizer do
|
|
28
28
|
|
29
29
|
context "when passed a String" do
|
30
30
|
it "wraps the expression in unaccent()" do
|
31
|
-
config =
|
31
|
+
config = double("config", :ignore => [:accents], :postgresql_version => 90000)
|
32
32
|
|
33
33
|
normalizer = PgSearch::Normalizer.new(config)
|
34
34
|
normalizer.add_normalization("foo").should == "unaccent(foo)"
|
@@ -38,7 +38,7 @@ describe PgSearch::Normalizer do
|
|
38
38
|
it "wraps the expression in that function" do
|
39
39
|
PgSearch.stub(:unaccent_function).and_return("my_unaccent")
|
40
40
|
|
41
|
-
config =
|
41
|
+
config = double("config", :ignore => [:accents], :postgresql_version => 90000)
|
42
42
|
|
43
43
|
normalizer = PgSearch::Normalizer.new(config)
|
44
44
|
normalizer.add_normalization("foo").should == "my_unaccent(foo)"
|
@@ -49,7 +49,7 @@ describe PgSearch::Normalizer do
|
|
49
49
|
|
50
50
|
context "when config[:ignore] does not include :accents" do
|
51
51
|
it "passes the expression through" do
|
52
|
-
config =
|
52
|
+
config = double("config", :ignore => [], :postgresql_version => 90000)
|
53
53
|
|
54
54
|
normalizer = PgSearch::Normalizer.new(config)
|
55
55
|
normalizer.add_normalization("foo").should == "foo"
|
@@ -60,7 +60,7 @@ describe PgSearch::Normalizer do
|
|
60
60
|
context "for PostgreSQL versions before 9.0" do
|
61
61
|
context "when config[:ignore] includes :accents" do
|
62
62
|
it "raises a NotSupportedForPostgresqlVersion exception" do
|
63
|
-
config =
|
63
|
+
config = double("config", :ignore => [:accents], :postgresql_version => 89999)
|
64
64
|
|
65
65
|
normalizer = PgSearch::Normalizer.new(config)
|
66
66
|
expect {
|
@@ -71,7 +71,7 @@ describe PgSearch::Normalizer do
|
|
71
71
|
|
72
72
|
context "when config[:ignore] does not include :accents" do
|
73
73
|
it "passes the expression through" do
|
74
|
-
config =
|
74
|
+
config = double("config", :ignore => [], :postgresql_version => 90000)
|
75
75
|
|
76
76
|
normalizer = PgSearch::Normalizer.new(config)
|
77
77
|
normalizer.add_normalization("foo").should == "foo"
|
data/spec/spec_helper.rb
CHANGED
@@ -10,14 +10,12 @@ if ENV["TRAVIS"]
|
|
10
10
|
end
|
11
11
|
|
12
12
|
begin
|
13
|
-
|
14
|
-
|
15
|
-
rescue
|
16
|
-
begin
|
17
|
-
require "arjdbc/jdbc/core_ext"
|
13
|
+
if defined? JRUBY_VERSION
|
14
|
+
require 'activerecord-jdbcpostgresql-adapter'
|
18
15
|
error_class = ActiveRecord::JDBCError
|
19
|
-
|
20
|
-
|
16
|
+
else
|
17
|
+
require "pg"
|
18
|
+
error_class = PGError
|
21
19
|
end
|
22
20
|
end
|
23
21
|
|
@@ -35,7 +33,7 @@ begin
|
|
35
33
|
connection = ActiveRecord::Base.connection
|
36
34
|
postgresql_version = connection.send(:postgresql_version)
|
37
35
|
connection.execute("SELECT 1")
|
38
|
-
rescue error_class
|
36
|
+
rescue error_class
|
39
37
|
at_exit do
|
40
38
|
puts "-" * 80
|
41
39
|
puts "Unable to connect to database. Please run:"
|
@@ -43,7 +41,7 @@ rescue error_class => e
|
|
43
41
|
puts " createdb pg_search_test"
|
44
42
|
puts "-" * 80
|
45
43
|
end
|
46
|
-
raise
|
44
|
+
raise $!
|
47
45
|
end
|
48
46
|
|
49
47
|
if ENV["LOGGER"]
|