pg_search 0.2.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,49 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe PgSearch::Document do
4
+ with_table "pg_search_documents", {}, &DOCUMENTS_SCHEMA
5
+
6
+ with_model :Searchable do
7
+ table
8
+ model do
9
+ include PgSearch
10
+ multisearchable({})
11
+ end
12
+ end
13
+
14
+ it { should be_an(ActiveRecord::Base) }
15
+
16
+ describe "callbacks" do
17
+ describe "before_validation" do
18
+ subject { document }
19
+ let(:document) { PgSearch::Document.new(:searchable => searchable) }
20
+ let(:searchable) { Searchable.new }
21
+
22
+ before do
23
+ # Redefine the options for multisearchable
24
+ Searchable.multisearchable(multisearchable_options)
25
+ end
26
+
27
+ context "when searching against a single column" do
28
+ let(:multisearchable_options) { {:against => :some_content} }
29
+ let(:text) { "foo bar" }
30
+ before do
31
+ searchable.stub!(:some_content => text)
32
+ document.valid?
33
+ end
34
+
35
+ its(:content) { should == text }
36
+ end
37
+
38
+ context "when searching against multiple columns" do
39
+ let(:multisearchable_options) { {:against => [:attr1, :attr2]} }
40
+ before do
41
+ searchable.stub!(:attr1 => "1", :attr2 => "2")
42
+ document.valid?
43
+ end
44
+
45
+ its(:content) { should == "1 2" }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,66 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe PgSearch::Multisearch do
4
+ with_table "pg_search_documents", {}, &DOCUMENTS_SCHEMA
5
+
6
+ with_model :MultisearchableModel do
7
+ table do |t|
8
+ t.string :title
9
+ t.text :content
10
+ t.timestamps
11
+ end
12
+ model do
13
+ include PgSearch
14
+ end
15
+ end
16
+
17
+ describe ".rebuild" do
18
+ it "should fetch the proper columns from the model" do
19
+ end
20
+ end
21
+
22
+ describe ".rebuild_sql" do
23
+ context "with one attribute" do
24
+ it "should generate the proper SQL code" do
25
+ model = MultisearchableModel
26
+ connection = model.connection
27
+
28
+ model.multisearchable :against => :title
29
+
30
+ expected_sql = <<-SQL
31
+ INSERT INTO #{PgSearch::Document.quoted_table_name} (searchable_type, searchable_id, content)
32
+ SELECT #{connection.quote(model.name)} AS searchable_type,
33
+ #{model.quoted_table_name}.id AS searchable_id,
34
+ (
35
+ coalesce(#{model.quoted_table_name}.title, '')
36
+ ) AS content
37
+ FROM #{model.quoted_table_name}
38
+ SQL
39
+
40
+ PgSearch::Multisearch.rebuild_sql(MultisearchableModel).should == expected_sql
41
+ end
42
+ end
43
+
44
+ context "with multiple attributes" do
45
+ it "should generate the proper SQL code" do
46
+ model = MultisearchableModel
47
+ connection = model.connection
48
+
49
+ model.multisearchable :against => [:title, :content]
50
+
51
+ expected_sql = <<-SQL
52
+ INSERT INTO #{PgSearch::Document.quoted_table_name} (searchable_type, searchable_id, content)
53
+ SELECT #{connection.quote(model.name)} AS searchable_type,
54
+ #{model.quoted_table_name}.id AS searchable_id,
55
+ (
56
+ coalesce(#{model.quoted_table_name}.title, '') || ' ' || coalesce(#{model.quoted_table_name}.content, '')
57
+ ) AS content
58
+ FROM #{model.quoted_table_name}
59
+ SQL
60
+
61
+ PgSearch::Multisearch.rebuild_sql(MultisearchableModel).should == expected_sql
62
+ end
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,108 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe PgSearch::Multisearchable do
4
+ with_table "pg_search_documents", {}, &DOCUMENTS_SCHEMA
5
+
6
+ before { PgSearch.stub(:multisearch_enabled?) { true } }
7
+
8
+ describe "a model that is multisearchable" do
9
+ subject { ModelThatIsMultisearchable }
10
+
11
+ with_model :ModelThatIsMultisearchable do
12
+ table do |t|
13
+ end
14
+ model do
15
+ include PgSearch
16
+ multisearchable
17
+ end
18
+ end
19
+
20
+ describe "callbacks" do
21
+ describe "after_create" do
22
+ let(:record) { ModelThatIsMultisearchable.new }
23
+
24
+ describe "saving the record" do
25
+ subject do
26
+ lambda { record.save! }
27
+ end
28
+
29
+ context "with multisearch enabled" do
30
+ before { PgSearch.stub(:multisearch_enabled?) { true } }
31
+ it { should change(PgSearch::Document, :count).by(1) }
32
+ end
33
+
34
+ context "with multisearch disabled" do
35
+ before { PgSearch.stub(:multisearch_enabled?) { false } }
36
+ it { should_not change(PgSearch::Document, :count) }
37
+ end
38
+ end
39
+
40
+ describe "the document" do
41
+ subject { document }
42
+ before { record.save! }
43
+ let(:document) { PgSearch::Document.last }
44
+
45
+ its(:searchable) { should == record }
46
+ end
47
+ end
48
+
49
+ describe "after_update" do
50
+ let!(:record) { ModelThatIsMultisearchable.create! }
51
+
52
+ context "when the document is present" do
53
+ describe "saving the record" do
54
+ subject do
55
+ lambda { record.save! }
56
+ end
57
+
58
+ context "with multisearch enabled" do
59
+ before { PgSearch.stub(:multisearch_enabled?) { true } }
60
+
61
+ before { record.pg_search_document.should_receive(:save) }
62
+ it { should_not change(PgSearch::Document, :count) }
63
+ end
64
+
65
+ context "with multisearch disabled" do
66
+ before { PgSearch.stub(:multisearch_enabled?) { false } }
67
+
68
+ before { record.pg_search_document.should_not_receive(:save) }
69
+ it { should_not change(PgSearch::Document, :count) }
70
+ end
71
+ end
72
+ end
73
+
74
+ context "when the document is missing" do
75
+ before { record.pg_search_document = nil }
76
+
77
+ describe "saving the record" do
78
+ subject do
79
+ lambda { record.save! }
80
+ end
81
+
82
+ context "with multisearch enabled" do
83
+ before { PgSearch.stub(:multisearch_enabled?) { true } }
84
+ it { should change(PgSearch::Document, :count).by(1) }
85
+ end
86
+
87
+ context "with multisearch disabled" do
88
+ before { PgSearch.stub(:multisearch_enabled?) { false } }
89
+ it { should_not change(PgSearch::Document, :count) }
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ describe "after_destroy" do
96
+ it "should remove its document" do
97
+ record = ModelThatIsMultisearchable.create!
98
+ document = record.pg_search_document
99
+
100
+ lambda { record.destroy }.should change(PgSearch::Document, :count).by(-1)
101
+ lambda {
102
+ PgSearch::Document.find(document.id)
103
+ }.should raise_error(ActiveRecord::RecordNotFound)
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -369,6 +369,48 @@ describe "an ActiveRecord model which includes PgSearch" do
369
369
  results.should =~ included
370
370
  end
371
371
  end
372
+
373
+ describe "ranking" do
374
+ before do
375
+ ["Strip Down", "Down", "Down and Out", "Won't Let You Down"].each do |name|
376
+ ModelWithPgSearch.create! :content => name
377
+ end
378
+ end
379
+
380
+ context "with a normalization specified" do
381
+ before do
382
+ ModelWithPgSearch.class_eval do
383
+ pg_search_scope :search_content_with_normalization,
384
+ :against => :content,
385
+ :using => {
386
+ :tsearch => {:normalization => 2}
387
+ }
388
+ end
389
+ end
390
+ it "ranks the results for documents with less text higher" do
391
+ results = ModelWithPgSearch.search_content_with_normalization("down")
392
+
393
+ results.map(&:content).should == ["Down", "Strip Down", "Down and Out", "Won't Let You Down"]
394
+ results.first.rank.should be > results.last.rank
395
+ end
396
+ end
397
+
398
+ context "with no normalization" do
399
+ before do
400
+ ModelWithPgSearch.class_eval do
401
+ pg_search_scope :search_content_without_normalization,
402
+ :against => :content,
403
+ :using => :tsearch
404
+ end
405
+ end
406
+ it "ranks the results equally" do
407
+ results = ModelWithPgSearch.search_content_without_normalization("down")
408
+
409
+ results.map(&:content).should == ["Strip Down", "Down", "Down and Out", "Won't Let You Down"]
410
+ results.first.rank.should == results.last.rank
411
+ end
412
+ end
413
+ end
372
414
 
373
415
  context "against columns ranked with arrays" do
374
416
  before do
@@ -420,6 +462,33 @@ describe "an ActiveRecord model which includes PgSearch" do
420
462
  results.should == [winner, loser]
421
463
  end
422
464
  end
465
+
466
+ context "searching any_word option" do
467
+ before do
468
+ ModelWithPgSearch.class_eval do
469
+ pg_search_scope :search_title_with_any_word,
470
+ :against => :title,
471
+ :using => {
472
+ :tsearch => {:any_word => true}
473
+ }
474
+
475
+ pg_search_scope :search_title_with_all_words,
476
+ :against => :title
477
+ end
478
+ end
479
+
480
+ it "returns all results containing any word in their title" do
481
+ numbers = %w(one two three four).map{|number| ModelWithPgSearch.create!(:title => number)}
482
+
483
+ results = ModelWithPgSearch.search_title_with_any_word("one two three four")
484
+
485
+ results.map(&:title).should == %w(one two three four)
486
+
487
+ results = ModelWithPgSearch.search_title_with_all_words("one two three four")
488
+
489
+ results.map(&:title).should == []
490
+ end
491
+ end
423
492
  end
424
493
 
425
494
  context "using dmetaphone" do
@@ -523,48 +592,6 @@ describe "an ActiveRecord model which includes PgSearch" do
523
592
  end
524
593
  end
525
594
 
526
- context "using a tsvector column" do
527
- with_model :ModelWithPgSearchUsingTsVectorColumn do
528
- table do |t|
529
- t.text 'content'
530
- t.column 'content_tsvector', :tsvector
531
- end
532
-
533
- model { include PgSearch }
534
- end
535
-
536
- let!(:expected) { ModelWithPgSearchUsingTsVectorColumn.create!(:content => 'tiling is grouty') }
537
- let!(:unexpected) { ModelWithPgSearchUsingTsVectorColumn.create!(:content => 'longcat is looooooooong') }
538
-
539
- before do
540
- ActiveRecord::Base.connection.execute <<-SQL
541
- UPDATE #{ModelWithPgSearchUsingTsVectorColumn.table_name}
542
- SET content_tsvector = to_tsvector('english'::regconfig, "#{ModelWithPgSearchUsingTsVectorColumn.table_name}"."content")
543
- SQL
544
-
545
- ModelWithPgSearchUsingTsVectorColumn.class_eval do
546
- pg_search_scope :search_by_content_with_tsvector,
547
- :against => :content,
548
- :using => {
549
- :tsearch => {
550
- :tsvector_column => 'content_tsvector',
551
- :dictionary => 'english'
552
- }
553
- }
554
- end
555
- end
556
-
557
- if defined?(ActiveRecord::Relation)
558
- it "should not use to_tsvector in the query" do
559
- ModelWithPgSearchUsingTsVectorColumn.search_by_content_with_tsvector("tiles").to_sql.should_not =~ /to_tsvector/
560
- end
561
- end
562
-
563
- it "should find the expected result" do
564
- ModelWithPgSearchUsingTsVectorColumn.search_by_content_with_tsvector("tiles").map(&:id).should == [expected.id]
565
- end
566
- end
567
-
568
595
  context "ignoring accents" do
569
596
  before do
570
597
  ModelWithPgSearch.class_eval do
@@ -645,4 +672,83 @@ describe "an ActiveRecord model which includes PgSearch" do
645
672
  end
646
673
  end
647
674
  end
675
+
676
+ describe ".multisearchable" do
677
+ it "should include the Multisearchable module" do
678
+ ModelWithPgSearch.should_receive(:include).with(PgSearch::Multisearchable)
679
+ ModelWithPgSearch.multisearchable
680
+ end
681
+
682
+ it "should set pg_search_multisearchable_options on the class" do
683
+ options = double(:options)
684
+ ModelWithPgSearch.multisearchable(options)
685
+ ModelWithPgSearch.pg_search_multisearchable_options.should == options
686
+ end
687
+ end
688
+
689
+ describe ".multisearch" do
690
+ subject { PgSearch.multisearch(query) }
691
+ let(:query) { double(:query) }
692
+ let(:relation) { double(:relation) }
693
+ before do
694
+ PgSearch::Document.should_receive(:search).with(query).and_return(relation)
695
+ end
696
+
697
+ it { should == relation }
698
+ end
699
+
700
+ describe ".disable_multisearch" do
701
+ it "should temporarily disable multisearch" do
702
+ @multisearch_enabled_before = PgSearch.multisearch_enabled?
703
+ PgSearch.disable_multisearch do
704
+ @multisearch_enabled_inside = PgSearch.multisearch_enabled?
705
+ end
706
+ @multisearch_enabled_after = PgSearch.multisearch_enabled?
707
+
708
+ @multisearch_enabled_before.should be(true)
709
+ @multisearch_enabled_inside.should be(false)
710
+ @multisearch_enabled_after.should be(true)
711
+ end
712
+
713
+ it "should reenable multisearch after an error" do
714
+ @multisearch_enabled_before = PgSearch.multisearch_enabled?
715
+ begin
716
+ PgSearch.disable_multisearch do
717
+ @multisearch_enabled_inside = PgSearch.multisearch_enabled?
718
+ raise
719
+ end
720
+ rescue
721
+ end
722
+
723
+ @multisearch_enabled_after = PgSearch.multisearch_enabled?
724
+
725
+ @multisearch_enabled_before.should be(true)
726
+ @multisearch_enabled_inside.should be(false)
727
+ @multisearch_enabled_after.should be(true)
728
+ end
729
+
730
+ it "should not disable multisearch on other threads" do
731
+ values = Queue.new
732
+ sync = Queue.new
733
+ Thread.new do
734
+ values.push PgSearch.multisearch_enabled?
735
+ sync.pop # wait
736
+ values.push PgSearch.multisearch_enabled?
737
+ sync.pop # wait
738
+ values.push PgSearch.multisearch_enabled?
739
+ end
740
+
741
+ @multisearch_enabled_before = values.pop
742
+ PgSearch.disable_multisearch do
743
+ sync.push :go
744
+ @multisearch_enabled_inside = values.pop
745
+ end
746
+ sync.push :go
747
+ @multisearch_enabled_after = values.pop
748
+
749
+ @multisearch_enabled_before.should be(true)
750
+ @multisearch_enabled_inside.should be(true)
751
+ @multisearch_enabled_after.should be(true)
752
+ end
753
+ end
648
754
  end
data/spec/spec_helper.rb CHANGED
@@ -4,6 +4,7 @@ require "pg_search"
4
4
  begin
5
5
  ActiveRecord::Base.establish_connection(:adapter => 'postgresql',
6
6
  :database => 'pg_search_test',
7
+ :username => ('postgres' if ENV["TRAVIS"]),
7
8
  :min_messages => 'warning')
8
9
  connection = ActiveRecord::Base.connection
9
10
  connection.execute("SELECT 1")
@@ -45,8 +46,11 @@ RSpec.configure do |config|
45
46
  config.extend WithModel
46
47
  end
47
48
 
48
- if defined?(ActiveRecord::Relation)
49
- RSpec::Matchers::OperatorMatcher.register(ActiveRecord::Relation, '=~', RSpec::Matchers::MatchArray)
49
+ RSpec::Matchers::OperatorMatcher.register(ActiveRecord::Relation, '=~', RSpec::Matchers::MatchArray)
50
+
51
+ DOCUMENTS_SCHEMA = lambda do |t|
52
+ t.belongs_to :searchable, :polymorphic => true
53
+ t.text :content
50
54
  end
51
55
 
52
56
  require 'irb'