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.
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, normalize(query))
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
- normalize(query)
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(normalize(Arel.sql(document)))
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
@@ -1,3 +1,3 @@
1
1
  module PgSearch
2
- VERSION = "0.7.0"
2
+ VERSION = "0.7.1"
3
3
  end
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 "with will_paginate" do
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 = stub(:to_s => "foo")
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 = stub(
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!(:some_content => text)
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!(:attr1 => "1", :attr2 => "2")
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 = stub(:config, :ignore => [])
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 = stub(:config, :ignore => [])
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 "spec_helper"
1
+ require 'spec_helper'
2
+ require 'ostruct'
2
3
 
3
4
  describe PgSearch::Features::Trigram do
4
- describe "#rank" do
5
- with_model :Model do
6
- table do |t|
7
- t.string :name
8
- t.text :content
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
- it "returns an expression using the similarity() function" do
13
- query = "query"
14
- columns = [
15
- PgSearch::Configuration::Column.new(:name, nil, Model),
16
- PgSearch::Configuration::Column.new(:content, nil, Model),
17
- ]
18
- options = stub(:options)
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
- feature = described_class.new(query, options, columns, Model, normalizer)
23
- feature.rank.to_sql.should == %Q{(similarity((coalesce(#{Model.quoted_table_name}."name"::text, '') || ' ' || coalesce(#{Model.quoted_table_name}."content"::text, '')), 'query'))}
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 = stub(:config, :ignore => [])
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 = stub(:config, :ignore => [])
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
- '2001-01-01 00:00:00' AS created_at,
95
- '2001-01-01 00:00:00' AS updated_at
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 = stub("config", :ignore => [:accents], :postgresql_version => 90000)
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 = stub("config", :ignore => [:accents], :postgresql_version => 90000)
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 = stub("config", :ignore => [:accents], :postgresql_version => 90000)
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 = stub("config", :ignore => [:accents], :postgresql_version => 90000)
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 = stub("config", :ignore => [], :postgresql_version => 90000)
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 = stub("config", :ignore => [:accents], :postgresql_version => 89999)
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 = stub("config", :ignore => [], :postgresql_version => 90000)
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
- require "pg"
14
- error_class = PGError
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
- rescue LoadError, StandardError
20
- raise "I don't know what database adapter you're using, sorry."
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 => e
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 e
44
+ raise $!
47
45
  end
48
46
 
49
47
  if ENV["LOGGER"]