pg_search 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
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"]