pg_search 0.2.2 → 0.3

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.
@@ -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'