pg_search 0.4.2 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,12 +1,21 @@
1
1
  = PgSearch
2
2
 
3
+ == 0.5
4
+
5
+ * Convert migration rake tasks into generators.
6
+
7
+ * Use rake task arguments for multisearch rebuild instead of environment
8
+ variable.
9
+
10
+ * Always cast columns to text.
11
+
3
12
  == 0.4.2
4
13
 
5
14
  * Fill in timestamps correctly when rebuilding multisearch documents. (Barton McGuire)
6
15
 
7
16
  * Fix various issues with rebuilding multisearch documents. (Eugen Neagoe)
8
17
 
9
- * Fix syntax error in pg_search_dmetaphone() migration (Casey Foster)
18
+ * Fix syntax error in pg_search_dmetaphone() migration. (Casey Foster)
10
19
 
11
20
  * Rename PgSearch#rank to PgSearch#pg_search_rank and always return a Float.
12
21
 
data/README.rdoc CHANGED
@@ -26,7 +26,7 @@ The 0.2 branch lives at https://github.com/Casecommons/pg_search/tree/0.2-stable
26
26
 
27
27
  === Other ActiveRecord-based projects
28
28
 
29
- In addition to installing and requiring the gem, you may want to include the PgSearch rake tasks in your Rakefile:
29
+ In addition to installing and requiring the gem, you may want to include the PgSearch rake tasks in your Rakefile. This isn't necessary for Rails projects, which gain the Rake tasks via a Railtie.
30
30
 
31
31
  load "pg_search/tasks.rb"
32
32
 
@@ -52,7 +52,7 @@ The other technique is search scopes, which allow you to do more advanced search
52
52
 
53
53
  Before using multi-search, you must generate and run a migration to create the pg_search_documents database table.
54
54
 
55
- $ rake pg_search:migration:multisearch
55
+ $ rails g pg_search:migration:multisearch
56
56
  $ rake db:migrate
57
57
 
58
58
  ==== multisearchable
@@ -120,7 +120,7 @@ To remove all of the documents for a given class, you can simply delete all of t
120
120
 
121
121
  Run this Rake task to regenerate all of the documents for a given class.
122
122
 
123
- $ rake pg_search:multisearch:rebuild MODEL=BlogPost
123
+ $ rake pg_search:multisearch:rebuild[BlogPost]
124
124
 
125
125
  Currently this is only supported for :against methods that directly map to Active Record attributes. Until that is fixed, you could also manually rebuild all of the documents.
126
126
 
@@ -198,9 +198,10 @@ Important: The returned hash must include a :query key. Its value does not neces
198
198
 
199
199
  It is possible to search columns on associated models. Note that if you do this, it will be impossible to speed up searches with database indexes. However, it is supported as a quick way to try out cross-model searching.
200
200
 
201
- In PostgreSQL 8.3 and earlier, you must install a utility function into your database. To generate a migration for this, run:
201
+ In PostgreSQL 8.3 and earlier, you must install a utility function into your database. To generate and run a migration for this, run:
202
202
 
203
- $ rake pg_search:migration:associated_against
203
+ $ rails g pg_search:migration:associated_against
204
+ $ rake db:migrate
204
205
 
205
206
  This migration is safe to run against newer versions of PostgreSQL as well. It will essentially do nothing.
206
207
 
@@ -392,9 +393,10 @@ Setting this attribute to true will perform a search which will return all model
392
393
 
393
394
  {Double Metaphone}[http://en.wikipedia.org/wiki/Double_Metaphone] is an algorithm for matching words that sound alike even if they are spelled very differently. For example, "Geoff" and "Jeff" sound identical and thus match. Currently, this is not a true double-metaphone, as only the first metaphone is used for searching.
394
395
 
395
- Double Metaphone support is currently available as part of the {fuzzystrmatch contrib package}[http://www.postgresql.org/docs/current/static/fuzzystrmatch.html] that must be installed before this feature can be used. In addition to the contrib package, you must install a utility function into your database. To generate a migration for this, run:
396
+ Double Metaphone support is currently available as part of the {fuzzystrmatch contrib package}[http://www.postgresql.org/docs/current/static/fuzzystrmatch.html] that must be installed before this feature can be used. In addition to the contrib package, you must install a utility function into your database. To generate and run a migration for this, run:
396
397
 
397
- $ rake pg_search:migration:dmetaphone
398
+ $ rails g pg_search:migration:dmetaphone
399
+ $ rake db:migrate
398
400
 
399
401
  The following example shows how to use :dmetaphone.
400
402
 
@@ -26,7 +26,7 @@ module PgSearch
26
26
  else
27
27
  full_name
28
28
  end
29
- "coalesce(#{name}, '')"
29
+ "coalesce(#{name}::text, '')"
30
30
  end
31
31
 
32
32
  def foreign?
@@ -0,0 +1,23 @@
1
+ require "rails/generators/base"
2
+
3
+ module PgSearch
4
+ module Migration
5
+ class AssociatedAgainstGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("../templates", __FILE__)
7
+
8
+ def create_migration
9
+ now = Time.now.utc
10
+ filename = "#{now.strftime('%Y%m%d%H%M%S')}_add_pg_search_associated_against_support_functions.rb"
11
+ template "add_pg_search_associated_against_support_functions.rb.erb", "db/migrate/#{filename}"
12
+ end
13
+
14
+ private
15
+
16
+ def read_sql_file(filename)
17
+ sql_directory = File.expand_path("../../../../sql", __FILE__)
18
+ source_path = File.join(sql_directory, "#{filename}.sql")
19
+ File.read(source_path)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ require "rails/generators/base"
2
+
3
+ module PgSearch
4
+ module Migration
5
+ class DmetaphoneGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("../templates", __FILE__)
7
+
8
+ def create_migration
9
+ now = Time.now.utc
10
+ filename = "#{now.strftime('%Y%m%d%H%M%S')}_add_pg_search_dmetaphone_support_functions.rb"
11
+ template "add_pg_search_dmetaphone_support_functions.rb.erb", "db/migrate/#{filename}"
12
+ end
13
+
14
+ private
15
+
16
+ def read_sql_file(filename)
17
+ sql_directory = File.expand_path("../../../../sql", __FILE__)
18
+ source_path = File.join(sql_directory, "#{filename}.sql")
19
+ File.read(source_path)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ require "rails/generators/base"
2
+
3
+ module PgSearch
4
+ module Migration
5
+ class MultisearchGenerator < Rails::Generators::Base
6
+ source_root Pathname.new(File.dirname(__FILE__)).join("templates")
7
+
8
+ def create_migration
9
+ now = Time.now.utc
10
+ filename = "#{now.strftime('%Y%m%d%H%M%S')}_create_pg_search_documents.rb"
11
+ copy_file "create_pg_search_documents.rb", "db/migrate/#{filename}"
12
+ end
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,21 @@
1
+ class AddPgSearchAssociatedAgainstSupportFunctions < ActiveRecord::Migration
2
+ def self.up
3
+ say_with_time("Adding support functions for pg_search :associated_against") do
4
+ if ActiveRecord::Base.connection.send(:postgresql_version) < 80400
5
+ execute <<-SQL
6
+ <%= read_sql_file "array_agg" %>
7
+ SQL
8
+ end
9
+ end
10
+ end
11
+
12
+ def self.down
13
+ say_with_time("Dropping support functions for pg_search :associated_against") do
14
+ if ActiveRecord::Base.connection.send(:postgresql_version) < 80400
15
+ execute <<-SQL
16
+ <%= read_sql_file "uninstall_array_agg" %>
17
+ SQL
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ class AddPgSearchDmetaphoneSupportFunctions < ActiveRecord::Migration
2
+ def self.up
3
+ say_with_time("Adding support functions for pg_search :dmetaphone") do
4
+ if ActiveRecord::Base.connection.send(:postgresql_version) < 80400
5
+ execute <<-SQL
6
+ <%= read_sql_file "unnest" %>
7
+ SQL
8
+ end
9
+
10
+ execute <<-SQL
11
+ <%= read_sql_file "dmetaphone" %>
12
+ SQL
13
+ end
14
+ end
15
+
16
+ def self.down
17
+ say_with_time("Dropping support functions for pg_search :dmetaphone") do
18
+ execute <<-SQL
19
+ <%= read_sql_file "uninstall_dmetaphone" %>
20
+ SQL
21
+
22
+ if ActiveRecord::Base.connection.send(:postgresql_version) < 80400
23
+ execute <<-SQL
24
+ <%= read_sql_file "uninstall_unnest" %>
25
+ SQL
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ class CreatePgSearchDocuments < ActiveRecord::Migration
2
+ def self.up
3
+ say_with_time("Creating table for pg_search multisearch") do
4
+ create_table :pg_search_documents do |t|
5
+ t.text :content
6
+ t.belongs_to :searchable, :polymorphic => true
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
11
+
12
+ def self.down
13
+ say_with_time("Dropping table for pg_search multisearch") do
14
+ drop_table :pg_search_documents
15
+ end
16
+ end
17
+ end
@@ -32,7 +32,7 @@ SQL
32
32
  )
33
33
 
34
34
  content_expressions = columns.map { |column|
35
- %Q{coalesce(:model_table.#{column}, '')}
35
+ %Q{coalesce(:model_table.#{column}::text, '')}
36
36
  }.join(" || ' ' || ")
37
37
 
38
38
  REBUILD_SQL_TEMPLATE.gsub(
@@ -3,5 +3,11 @@ module PgSearch
3
3
  rake_tasks do
4
4
  load "pg_search/tasks.rb"
5
5
  end
6
+
7
+ generators do
8
+ require "pg_search/migration/multisearch_generator"
9
+ require "pg_search/migration/dmetaphone_generator"
10
+ require "pg_search/migration/associated_against_generator"
11
+ end
6
12
  end
7
13
  end
@@ -3,120 +3,14 @@ require 'pg_search'
3
3
 
4
4
  namespace :pg_search do
5
5
  namespace :multisearch do
6
- desc "Rebuild PgSearch multisearch records for MODEL"
7
- task :rebuild => :environment do
8
- raise "must set MODEL=<model name>" unless ENV["MODEL"]
9
- model_class = ENV["MODEL"].classify.constantize
6
+ desc "Rebuild PgSearch multisearch records for a given model"
7
+ task :rebuild, [:model] => :environment do |task, args|
8
+ raise ArgumentError, <<-MESSAGE unless args.model
9
+ You must pass a model as an argument.
10
+ Example: rake pg_search:multisearch:rebuild[BlogPost]
11
+ MESSAGE
12
+ model_class = args.model.classify.constantize
10
13
  PgSearch::Multisearch.rebuild(model_class)
11
14
  end
12
15
  end
13
-
14
- namespace :migration do
15
- desc "Generate migration to add table for multisearch"
16
- task :multisearch do
17
- now = Time.now.utc
18
- filename = "#{now.strftime('%Y%m%d%H%M%S')}_create_pg_search_documents.rb"
19
-
20
- File.open(Rails.root + 'db' + 'migrate' + filename, 'wb') do |migration_file|
21
- migration_file.puts <<-RUBY
22
- class CreatePgSearchDocuments < ActiveRecord::Migration
23
- def self.up
24
- say_with_time("Creating table for pg_search multisearch") do
25
- create_table :pg_search_documents do |t|
26
- t.text :content
27
- t.belongs_to :searchable, :polymorphic => true
28
- t.timestamps
29
- end
30
- end
31
- end
32
-
33
- def self.down
34
- say_with_time("Dropping table for pg_search multisearch") do
35
- drop_table :pg_search_documents
36
- end
37
- end
38
- end
39
- RUBY
40
- end
41
- end
42
-
43
- desc "Generate migration to add support functions for :dmetaphone"
44
- task :dmetaphone do
45
- now = Time.now.utc
46
- filename = "#{now.strftime('%Y%m%d%H%M%S')}_add_pg_search_dmetaphone_support_functions.rb"
47
-
48
- dmetaphone_sql = File.read(File.join(File.dirname(__FILE__), '..', '..', 'sql', 'dmetaphone.sql')).chomp
49
- uninstall_dmetaphone_sql = File.read(File.join(File.dirname(__FILE__), '..', '..', 'sql', 'uninstall_dmetaphone.sql')).chomp
50
-
51
- unnest_sql = File.read(File.join(File.dirname(__FILE__), '..', '..', 'sql', 'unnest.sql')).chomp
52
- uninstall_unnest_sql = File.read(File.join(File.dirname(__FILE__), '..', '..', 'sql', 'uninstall_unnest.sql')).chomp
53
-
54
- File.open(Rails.root + 'db' + 'migrate' + filename, 'wb') do |migration_file|
55
- migration_file.puts <<-RUBY
56
- class AddPgSearchDmetaphoneSupportFunctions < ActiveRecord::Migration
57
- def self.up
58
- say_with_time("Adding support functions for pg_search :dmetaphone") do
59
- if ActiveRecord::Base.connection.send(:postgresql_version) < 80400
60
- ActiveRecord::Base.connection.execute(<<-SQL)
61
- #{unnest_sql}
62
- SQL
63
- end
64
- ActiveRecord::Base.connection.execute(<<-SQL)
65
- #{dmetaphone_sql}
66
- SQL
67
- end
68
- end
69
-
70
- def self.down
71
- say_with_time("Dropping support functions for pg_search :dmetaphone") do
72
- ActiveRecord::Base.connection.execute(<<-SQL)
73
- #{uninstall_dmetaphone_sql}
74
- SQL
75
- if ActiveRecord::Base.connection.send(:postgresql_version) < 80400
76
- ActiveRecord::Base.connection.execute(<<-SQL)
77
- #{uninstall_unnest_sql}
78
- SQL
79
- end
80
- end
81
- end
82
- end
83
- RUBY
84
- end
85
- end
86
-
87
- desc "Generate migration to add support functions for :associated_against in PostgreSQL 8.3 and earlier"
88
- task :associated_against do
89
- now = Time.now.utc
90
- filename = "#{now.strftime('%Y%m%d%H%M%S')}_add_pg_search_associated_against_support_functions.rb"
91
-
92
- array_agg_sql = File.read(File.join(File.dirname(__FILE__), '..', '..', 'sql', 'array_agg.sql')).chomp
93
- uninstall_array_agg_sql = File.read(File.join(File.dirname(__FILE__), '..', '..', 'sql', 'uninstall_array_agg.sql')).chomp
94
-
95
- File.open(Rails.root + 'db' + 'migrate' + filename, 'wb') do |migration_file|
96
- migration_file.puts <<-RUBY
97
- class AddPgSearchAssociatedAgainstSupportFunctions < ActiveRecord::Migration
98
- def self.up
99
- say_with_time("Adding support functions for pg_search :associated_against") do
100
- if ActiveRecord::Base.connection.send(:postgresql_version) < 80400
101
- ActiveRecord::Base.connection.execute(<<-SQL)
102
- #{array_agg_sql}
103
- SQL
104
- end
105
- end
106
- end
107
-
108
- def self.down
109
- say_with_time("Dropping support functions for pg_search :associated_against") do
110
- if ActiveRecord::Base.connection.send(:postgresql_version) < 80400
111
- ActiveRecord::Base.connection.execute(<<-SQL)
112
- #{uninstall_array_agg_sql}
113
- SQL
114
- end
115
- end
116
- end
117
- end
118
- RUBY
119
- end
120
- end
121
- end
122
16
  end
@@ -1,3 +1,3 @@
1
1
  module PgSearch
2
- VERSION = "0.4.2"
2
+ VERSION = "0.5"
3
3
  end
@@ -38,7 +38,7 @@ INSERT INTO #{PgSearch::Document.quoted_table_name} (searchable_type, searchable
38
38
  SELECT #{connection.quote(model.name)} AS searchable_type,
39
39
  #{model.quoted_table_name}.id AS searchable_id,
40
40
  (
41
- coalesce(#{model.quoted_table_name}.title, '')
41
+ coalesce(#{model.quoted_table_name}.title::text, '')
42
42
  ) AS content,
43
43
  #{connection.quote(connection.quoted_date(now))} AS created_at,
44
44
  #{connection.quote(connection.quoted_date(now))} AS updated_at
@@ -61,7 +61,7 @@ INSERT INTO #{PgSearch::Document.quoted_table_name} (searchable_type, searchable
61
61
  SELECT #{connection.quote(model.name)} AS searchable_type,
62
62
  #{model.quoted_table_name}.id AS searchable_id,
63
63
  (
64
- coalesce(#{model.quoted_table_name}.title, '') || ' ' || coalesce(#{model.quoted_table_name}.content, '')
64
+ coalesce(#{model.quoted_table_name}.title::text, '') || ' ' || coalesce(#{model.quoted_table_name}.content::text, '')
65
65
  ) AS content,
66
66
  #{connection.quote(connection.quoted_date(now))} AS created_at,
67
67
  #{connection.quote(connection.quoted_date(now))} AS updated_at
@@ -97,10 +97,13 @@ describe PgSearch::Multisearchable do
97
97
  record = ModelThatIsMultisearchable.create!
98
98
  document = record.pg_search_document
99
99
 
100
- lambda { record.destroy }.should change(PgSearch::Document, :count).by(-1)
101
- lambda {
100
+ expect {
101
+ record.destroy
102
+ }.to change(PgSearch::Document, :count).by(-1)
103
+
104
+ expect {
102
105
  PgSearch::Document.find(document.id)
103
- }.should raise_error(ActiveRecord::RecordNotFound)
106
+ }.to raise_error(ActiveRecord::RecordNotFound)
104
107
  end
105
108
  end
106
109
  end
@@ -15,26 +15,21 @@ describe "an ActiveRecord model which includes PgSearch" do
15
15
  end
16
16
 
17
17
  describe ".pg_search_scope" do
18
- it "builds a scope" do
19
- ModelWithPgSearch.class_eval do
20
- pg_search_scope "matching_query", :against => []
21
- end
22
-
23
- lambda {
24
- ModelWithPgSearch.scoped({}).matching_query("foo").scoped({})
25
- }.should_not raise_error
18
+ it "builds a chainable scope" do
19
+ ModelWithPgSearch.pg_search_scope "matching_query", :against => []
20
+ scope = ModelWithPgSearch.scoped({}).matching_query("foo").scoped({})
21
+ scope.should be_an ActiveRecord::Relation
26
22
  end
27
23
 
28
24
  context "when passed a lambda" do
29
25
  it "builds a dynamic scope" do
30
- ModelWithPgSearch.class_eval do
31
- pg_search_scope :search_title_or_content, lambda { |query, pick_content|
26
+ ModelWithPgSearch.pg_search_scope :search_title_or_content,
27
+ lambda { |query, pick_content|
32
28
  {
33
29
  :query => query.gsub("-remove-", ""),
34
30
  :against => pick_content ? :content : :title
35
31
  }
36
32
  }
37
- end
38
33
 
39
34
  included = ModelWithPgSearch.create!(:title => 'foo', :content => 'bar')
40
35
  excluded = ModelWithPgSearch.create!(:title => 'bar', :content => 'foo')
@@ -46,86 +41,89 @@ describe "an ActiveRecord model which includes PgSearch" do
46
41
 
47
42
  context "when an unknown option is passed in" do
48
43
  it "raises an exception when invoked" do
49
- lambda {
50
- ModelWithPgSearch.class_eval do
51
- pg_search_scope :with_unknown_option, :against => :content, :foo => :bar
52
- end
44
+ ModelWithPgSearch.pg_search_scope :with_unknown_option,
45
+ :against => :content,
46
+ :foo => :bar
47
+
48
+ expect {
53
49
  ModelWithPgSearch.with_unknown_option("foo")
54
- }.should raise_error(ArgumentError, /foo/)
50
+ }.to raise_error(ArgumentError, /foo/)
55
51
  end
56
52
 
57
53
  context "dynamically" do
58
54
  it "raises an exception when invoked" do
59
- lambda {
60
- ModelWithPgSearch.class_eval do
61
- pg_search_scope :with_unknown_option, lambda { |*| {:against => :content, :foo => :bar} }
62
- end
55
+ ModelWithPgSearch.pg_search_scope :with_unknown_option,
56
+ lambda { |*| {:against => :content, :foo => :bar} }
57
+
58
+ expect {
63
59
  ModelWithPgSearch.with_unknown_option("foo")
64
- }.should raise_error(ArgumentError, /foo/)
60
+ }.to raise_error(ArgumentError, /foo/)
65
61
  end
66
62
  end
67
63
  end
68
64
 
69
65
  context "when an unknown :using is passed" do
70
66
  it "raises an exception when invoked" do
71
- lambda {
72
- ModelWithPgSearch.class_eval do
73
- pg_search_scope :with_unknown_using, :against => :content, :using => :foo
74
- end
67
+ ModelWithPgSearch.pg_search_scope :with_unknown_using,
68
+ :against => :content,
69
+ :using => :foo
70
+
71
+ expect {
75
72
  ModelWithPgSearch.with_unknown_using("foo")
76
- }.should raise_error(ArgumentError, /foo/)
73
+ }.to raise_error(ArgumentError, /foo/)
77
74
  end
78
75
 
79
76
  context "dynamically" do
80
77
  it "raises an exception when invoked" do
81
- lambda {
82
- ModelWithPgSearch.class_eval do
83
- pg_search_scope :with_unknown_using, lambda { |*| {:against => :content, :using => :foo} }
84
- end
78
+ ModelWithPgSearch.pg_search_scope :with_unknown_using,
79
+ lambda { |*| {:against => :content, :using => :foo} }
80
+
81
+ expect {
85
82
  ModelWithPgSearch.with_unknown_using("foo")
86
- }.should raise_error(ArgumentError, /foo/)
83
+ }.to raise_error(ArgumentError, /foo/)
87
84
  end
88
85
  end
89
86
  end
90
87
 
91
88
  context "when an unknown :ignoring is passed" do
92
89
  it "raises an exception when invoked" do
93
- lambda {
94
- ModelWithPgSearch.class_eval do
95
- pg_search_scope :with_unknown_ignoring, :against => :content, :ignoring => :foo
96
- end
90
+ ModelWithPgSearch.pg_search_scope :with_unknown_ignoring,
91
+ :against => :content,
92
+ :ignoring => :foo
93
+
94
+ expect {
97
95
  ModelWithPgSearch.with_unknown_ignoring("foo")
98
- }.should raise_error(ArgumentError, /ignoring.*foo/)
96
+ }.to raise_error(ArgumentError, /ignoring.*foo/)
99
97
  end
100
98
 
101
99
  context "dynamically" do
102
100
  it "raises an exception when invoked" do
103
- lambda {
104
- ModelWithPgSearch.class_eval do
105
- pg_search_scope :with_unknown_ignoring, lambda { |*| {:against => :content, :ignoring => :foo} }
106
- end
101
+ ModelWithPgSearch.pg_search_scope :with_unknown_ignoring,
102
+ lambda { |*| {:against => :content, :ignoring => :foo} }
103
+
104
+ expect {
107
105
  ModelWithPgSearch.with_unknown_ignoring("foo")
108
- }.should raise_error(ArgumentError, /ignoring.*foo/)
106
+ }.to raise_error(ArgumentError, /ignoring.*foo/)
109
107
  end
110
108
  end
111
109
 
112
110
  context "when :against is not passed in" do
113
111
  it "raises an exception when invoked" do
114
- lambda {
115
- ModelWithPgSearch.class_eval do
116
- pg_search_scope :with_unknown_ignoring, {}
117
- end
112
+ ModelWithPgSearch.pg_search_scope :with_unknown_ignoring, {}
113
+
114
+ expect {
118
115
  ModelWithPgSearch.with_unknown_ignoring("foo")
119
- }.should raise_error(ArgumentError, /against/)
116
+ }.to raise_error(ArgumentError, /against/)
120
117
  end
118
+
121
119
  context "dynamically" do
122
120
  it "raises an exception when invoked" do
123
- lambda {
124
- ModelWithPgSearch.class_eval do
125
- pg_search_scope :with_unknown_ignoring, lambda { |*| {} }
126
- end
121
+ ModelWithPgSearch.pg_search_scope :with_unknown_ignoring,
122
+ lambda { |*| {} }
123
+
124
+ expect {
127
125
  ModelWithPgSearch.with_unknown_ignoring("foo")
128
- }.should raise_error(ArgumentError, /against/)
126
+ }.to raise_error(ArgumentError, /against/)
129
127
  end
130
128
  end
131
129
  end
@@ -135,9 +133,7 @@ describe "an ActiveRecord model which includes PgSearch" do
135
133
  describe "a search scope" do
136
134
  context "against a single column" do
137
135
  before do
138
- ModelWithPgSearch.class_eval do
139
- pg_search_scope :search_content, :against => :content
140
- end
136
+ ModelWithPgSearch.pg_search_scope :search_content, :against => :content
141
137
  end
142
138
 
143
139
  it "returns an empty array when a blank query is passed in" do
@@ -252,13 +248,36 @@ describe "an ActiveRecord model which includes PgSearch" do
252
248
  not_a_string = stub(:to_s => "foo")
253
249
  ModelWithPgSearch.search_content(not_a_string).should == [foo]
254
250
  end
251
+
252
+ context "when the column is not text" do
253
+ with_model :ModelWithTimestamps do
254
+ table do |t|
255
+ t.timestamps
256
+ end
257
+
258
+ model do
259
+ include PgSearch
260
+
261
+ # WARNING: searching timestamps is not something PostgreSQL
262
+ # full-text search is good at. Use at your own risk.
263
+ pg_search_scope :search_timestamps,
264
+ :against => [:created_at, :updated_at]
265
+ end
266
+ end
267
+
268
+ it "casts the column to text" do
269
+ record = ModelWithTimestamps.create!
270
+
271
+ query = record.created_at.strftime("%Y-%m-%d")
272
+ results = ModelWithTimestamps.search_timestamps(query)
273
+ results.should == [record]
274
+ end
275
+ end
255
276
  end
256
277
 
257
278
  context "against multiple columns" do
258
279
  before do
259
- ModelWithPgSearch.class_eval do
260
- pg_search_scope :search_title_and_content, :against => [:title, :content]
261
- end
280
+ ModelWithPgSearch.pg_search_scope :search_title_and_content, :against => [:title, :content]
262
281
  end
263
282
 
264
283
  it "returns rows whose columns contain all of the terms in the query across columns" do
@@ -297,9 +316,7 @@ describe "an ActiveRecord model which includes PgSearch" do
297
316
 
298
317
  context "using trigram" do
299
318
  before do
300
- ModelWithPgSearch.class_eval do
301
- pg_search_scope :with_trigrams, :against => [:title, :content], :using => :trigram
302
- end
319
+ ModelWithPgSearch.pg_search_scope :with_trigrams, :against => [:title, :content], :using => :trigram
303
320
  end
304
321
 
305
322
  it "returns rows where one searchable column and the query share enough trigrams" do
@@ -317,20 +334,18 @@ describe "an ActiveRecord model which includes PgSearch" do
317
334
 
318
335
  context "using tsearch" do
319
336
  before do
320
- ModelWithPgSearch.class_eval do
321
- pg_search_scope :search_title_with_prefixes,
322
- :against => :title,
323
- :using => {
324
- :tsearch => {:prefix => true}
325
- }
326
- end
337
+ ModelWithPgSearch.pg_search_scope :search_title_with_prefixes,
338
+ :against => :title,
339
+ :using => {
340
+ :tsearch => {:prefix => true}
341
+ }
327
342
  end
328
343
 
329
344
  if ActiveRecord::Base.connection.send(:postgresql_version) < 80400
330
345
  it "is unsupported in PostgreSQL 8.3 and earlier" do
331
- lambda do
346
+ expect {
332
347
  ModelWithPgSearch.search_title_with_prefixes("abcd\303\251f")
333
- end.should raise_exception(PgSearch::NotSupportedForPostgresqlVersion)
348
+ }.to raise_exception(PgSearch::NotSupportedForPostgresqlVersion)
334
349
  end
335
350
  else
336
351
  context "with :prefix => true" do
@@ -356,13 +371,11 @@ describe "an ActiveRecord model which includes PgSearch" do
356
371
 
357
372
  context "with the english dictionary" do
358
373
  before do
359
- ModelWithPgSearch.class_eval do
360
- pg_search_scope :search_content_with_english,
361
- :against => :content,
362
- :using => {
363
- :tsearch => {:dictionary => :english}
364
- }
365
- end
374
+ ModelWithPgSearch.pg_search_scope :search_content_with_english,
375
+ :against => :content,
376
+ :using => {
377
+ :tsearch => {:dictionary => :english}
378
+ }
366
379
  end
367
380
 
368
381
  it "returns rows that match the query when stemmed by the english dictionary" do
@@ -383,9 +396,7 @@ describe "an ActiveRecord model which includes PgSearch" do
383
396
  end
384
397
 
385
398
  it "adds a #pg_search_rank method to each returned model record" do
386
- ModelWithPgSearch.class_eval do
387
- pg_search_scope :search_content, :against => :content
388
- end
399
+ ModelWithPgSearch.pg_search_scope :search_content, :against => :content
389
400
 
390
401
  result = ModelWithPgSearch.search_content("Strip Down").first
391
402
 
@@ -394,13 +405,11 @@ describe "an ActiveRecord model which includes PgSearch" do
394
405
 
395
406
  context "with a normalization specified" do
396
407
  before do
397
- ModelWithPgSearch.class_eval do
398
- pg_search_scope :search_content_with_normalization,
399
- :against => :content,
400
- :using => {
401
- :tsearch => {:normalization => 2}
402
- }
403
- end
408
+ ModelWithPgSearch.pg_search_scope :search_content_with_normalization,
409
+ :against => :content,
410
+ :using => {
411
+ :tsearch => {:normalization => 2}
412
+ }
404
413
  end
405
414
 
406
415
  it "ranks the results for documents with less text higher" do
@@ -413,11 +422,9 @@ describe "an ActiveRecord model which includes PgSearch" do
413
422
 
414
423
  context "with no normalization" do
415
424
  before do
416
- ModelWithPgSearch.class_eval do
417
- pg_search_scope :search_content_without_normalization,
418
- :against => :content,
419
- :using => :tsearch
420
- end
425
+ ModelWithPgSearch.pg_search_scope :search_content_without_normalization,
426
+ :against => :content,
427
+ :using => :tsearch
421
428
  end
422
429
 
423
430
  it "ranks the results equally" do
@@ -431,9 +438,8 @@ describe "an ActiveRecord model which includes PgSearch" do
431
438
 
432
439
  context "against columns ranked with arrays" do
433
440
  before do
434
- ModelWithPgSearch.class_eval do
435
- pg_search_scope :search_weighted_by_array_of_arrays, :against => [[:content, 'B'], [:title, 'A']]
436
- end
441
+ ModelWithPgSearch.pg_search_scope :search_weighted_by_array_of_arrays,
442
+ :against => [[:content, 'B'], [:title, 'A']]
437
443
  end
438
444
 
439
445
  it "returns results sorted by weighted rank" do
@@ -448,9 +454,8 @@ describe "an ActiveRecord model which includes PgSearch" do
448
454
 
449
455
  context "against columns ranked with a hash" do
450
456
  before do
451
- ModelWithPgSearch.class_eval do
452
- pg_search_scope :search_weighted_by_hash, :against => {:content => 'B', :title => 'A'}
453
- end
457
+ ModelWithPgSearch.pg_search_scope :search_weighted_by_hash,
458
+ :against => {:content => 'B', :title => 'A'}
454
459
  end
455
460
 
456
461
  it "returns results sorted by weighted rank" do
@@ -465,9 +470,8 @@ describe "an ActiveRecord model which includes PgSearch" do
465
470
 
466
471
  context "against columns of which only some are ranked" do
467
472
  before do
468
- ModelWithPgSearch.class_eval do
469
- pg_search_scope :search_weighted, :against => [:content, [:title, 'A']]
470
- end
473
+ ModelWithPgSearch.pg_search_scope :search_weighted,
474
+ :against => [:content, [:title, 'A']]
471
475
  end
472
476
 
473
477
  it "returns results sorted by weighted rank using an implied low rank for unranked columns" do
@@ -482,16 +486,14 @@ describe "an ActiveRecord model which includes PgSearch" do
482
486
 
483
487
  context "searching any_word option" do
484
488
  before do
485
- ModelWithPgSearch.class_eval do
486
- pg_search_scope :search_title_with_any_word,
487
- :against => :title,
488
- :using => {
489
- :tsearch => {:any_word => true}
490
- }
491
-
492
- pg_search_scope :search_title_with_all_words,
493
- :against => :title
494
- end
489
+ ModelWithPgSearch.pg_search_scope :search_title_with_any_word,
490
+ :against => :title,
491
+ :using => {
492
+ :tsearch => {:any_word => true}
493
+ }
494
+
495
+ ModelWithPgSearch.pg_search_scope :search_title_with_all_words,
496
+ :against => :title
495
497
  end
496
498
 
497
499
  it "returns all results containing any word in their title" do
@@ -510,9 +512,9 @@ describe "an ActiveRecord model which includes PgSearch" do
510
512
 
511
513
  context "using dmetaphone" do
512
514
  before do
513
- ModelWithPgSearch.class_eval do
514
- pg_search_scope :with_dmetaphones, :against => [:title, :content], :using => :dmetaphone
515
- end
515
+ ModelWithPgSearch.pg_search_scope :with_dmetaphones,
516
+ :against => [:title, :content],
517
+ :using => :dmetaphone
516
518
  end
517
519
 
518
520
  it "returns rows where one searchable column and the query share enough dmetaphones" do
@@ -549,23 +551,23 @@ describe "an ActiveRecord model which includes PgSearch" do
549
551
 
550
552
  context "using multiple features" do
551
553
  before do
552
- ModelWithPgSearch.class_eval do
553
- pg_search_scope :with_tsearch,
554
- :against => :title,
555
- :using => [
556
- [:tsearch, {:dictionary => 'english'}]
557
- ]
554
+ ModelWithPgSearch.pg_search_scope :with_tsearch,
555
+ :against => :title,
556
+ :using => [
557
+ [:tsearch, {:dictionary => 'english'}]
558
+ ]
558
559
 
559
- pg_search_scope :with_trigram, :against => :title, :using => :trigram
560
+ ModelWithPgSearch.pg_search_scope :with_trigram,
561
+ :against => :title,
562
+ :using => :trigram
560
563
 
561
- pg_search_scope :with_tsearch_and_trigram_using_array,
562
- :against => :title,
563
- :using => [
564
- [:tsearch, {:dictionary => 'english'}],
565
- :trigram
566
- ]
564
+ ModelWithPgSearch.pg_search_scope :with_tsearch_and_trigram,
565
+ :against => :title,
566
+ :using => [
567
+ [:tsearch, {:dictionary => 'english'}],
568
+ :trigram
569
+ ]
567
570
 
568
- end
569
571
  end
570
572
 
571
573
  it "returns rows that match using any of the features" do
@@ -575,13 +577,13 @@ describe "an ActiveRecord model which includes PgSearch" do
575
577
  trigram_query = "ling is grouty"
576
578
  ModelWithPgSearch.with_trigram(trigram_query).should include(record)
577
579
  ModelWithPgSearch.with_tsearch(trigram_query).should_not include(record)
578
- ModelWithPgSearch.with_tsearch_and_trigram_using_array(trigram_query).should == [record]
580
+ ModelWithPgSearch.with_tsearch_and_trigram(trigram_query).should == [record]
579
581
 
580
582
  # matches tsearch only
581
583
  tsearch_query = "tiles"
582
584
  ModelWithPgSearch.with_tsearch(tsearch_query).should include(record)
583
585
  ModelWithPgSearch.with_trigram(tsearch_query).should_not include(record)
584
- ModelWithPgSearch.with_tsearch_and_trigram_using_array(tsearch_query).should == [record]
586
+ ModelWithPgSearch.with_tsearch_and_trigram(tsearch_query).should == [record]
585
587
  end
586
588
 
587
589
  context "with feature-specific configuration" do
@@ -589,14 +591,12 @@ describe "an ActiveRecord model which includes PgSearch" do
589
591
  @tsearch_config = tsearch_config = {:dictionary => 'english'}
590
592
  @trigram_config = trigram_config = {:foo => 'bar'}
591
593
 
592
- ModelWithPgSearch.class_eval do
593
- pg_search_scope :with_tsearch_and_trigram_using_hash,
594
- :against => :title,
595
- :using => {
596
- :tsearch => tsearch_config,
597
- :trigram => trigram_config
598
- }
599
- end
594
+ ModelWithPgSearch.pg_search_scope :with_tsearch_and_trigram_using_hash,
595
+ :against => :title,
596
+ :using => {
597
+ :tsearch => tsearch_config,
598
+ :trigram => trigram_config
599
+ }
600
600
  end
601
601
 
602
602
  it "should pass the custom configuration down to the specified feature" do
@@ -628,16 +628,14 @@ describe "an ActiveRecord model which includes PgSearch" do
628
628
  SET content_tsvector = to_tsvector('english'::regconfig, "#{ModelWithPgSearchUsingTsVectorColumn.table_name}"."content")
629
629
  SQL
630
630
 
631
- ModelWithPgSearchUsingTsVectorColumn.class_eval do
632
- pg_search_scope :search_by_content_with_tsvector,
633
- :against => :content,
634
- :using => {
635
- :tsearch => {
636
- :tsvector_column => 'content_tsvector',
637
- :dictionary => 'english'
638
- }
631
+ ModelWithPgSearchUsingTsVectorColumn.pg_search_scope :search_by_content_with_tsvector,
632
+ :against => :content,
633
+ :using => {
634
+ :tsearch => {
635
+ :tsvector_column => 'content_tsvector',
636
+ :dictionary => 'english'
639
637
  }
640
- end
638
+ }
641
639
  end
642
640
 
643
641
  it "should not use to_tsvector in the query" do
@@ -651,16 +649,16 @@ describe "an ActiveRecord model which includes PgSearch" do
651
649
 
652
650
  context "ignoring accents" do
653
651
  before do
654
- ModelWithPgSearch.class_eval do
655
- pg_search_scope :search_title_without_accents, :against => :title, :ignoring => :accents
656
- end
652
+ ModelWithPgSearch.pg_search_scope :search_title_without_accents,
653
+ :against => :title,
654
+ :ignoring => :accents
657
655
  end
658
656
 
659
657
  if ActiveRecord::Base.connection.send(:postgresql_version) < 90000
660
658
  it "is unsupported in PostgreSQL 8.x" do
661
- lambda do
659
+ expect {
662
660
  ModelWithPgSearch.search_title_without_accents("abcd\303\251f")
663
- end.should raise_exception(PgSearch::NotSupportedForPostgresqlVersion)
661
+ }.to raise_exception(PgSearch::NotSupportedForPostgresqlVersion)
664
662
  end
665
663
  else
666
664
  it "returns rows that match the query but not its accents" do
@@ -677,16 +675,16 @@ describe "an ActiveRecord model which includes PgSearch" do
677
675
 
678
676
  context "when passed a :ranked_by expression" do
679
677
  before do
680
- ModelWithPgSearch.class_eval do
681
- pg_search_scope :search_content_with_default_rank,
682
- :against => :content
683
- pg_search_scope :search_content_with_importance_as_rank,
684
- :against => :content,
685
- :ranked_by => "importance"
686
- pg_search_scope :search_content_with_importance_as_rank_multiplier,
687
- :against => :content,
688
- :ranked_by => ":tsearch * importance"
689
- end
678
+ ModelWithPgSearch.pg_search_scope :search_content_with_default_rank,
679
+ :against => :content
680
+
681
+ ModelWithPgSearch.pg_search_scope :search_content_with_importance_as_rank,
682
+ :against => :content,
683
+ :ranked_by => "importance"
684
+
685
+ ModelWithPgSearch.pg_search_scope :search_content_with_importance_as_rank_multiplier,
686
+ :against => :content,
687
+ :ranked_by => ":tsearch * importance"
690
688
  end
691
689
 
692
690
  it "should return records with a rank attribute equal to the :ranked_by expression" do
@@ -716,22 +714,20 @@ describe "an ActiveRecord model which includes PgSearch" do
716
714
  end
717
715
 
718
716
  %w[tsearch trigram dmetaphone].each do |feature|
719
-
720
717
  context "using the #{feature} ranking algorithm" do
721
718
  before do
722
719
  @scope_name = scope_name = :"search_content_ranked_by_#{feature}"
723
- ModelWithPgSearch.class_eval do
724
- pg_search_scope scope_name,
725
- :against => :content,
726
- :ranked_by => ":#{feature}"
727
- end
720
+
721
+ ModelWithPgSearch.pg_search_scope scope_name,
722
+ :against => :content,
723
+ :ranked_by => ":#{feature}"
728
724
  end
729
725
 
730
726
  it "should return results with a rank" do
731
727
  ModelWithPgSearch.create!(:content => 'foo')
732
728
 
733
729
  results = ModelWithPgSearch.send(@scope_name, 'foo')
734
- results.first.pg_search_rank.should_not be_nil
730
+ results.first.pg_search_rank.should be_a Float
735
731
  end
736
732
  end
737
733
  end
data/sql/dmetaphone.sql CHANGED
@@ -1,4 +1,3 @@
1
1
  CREATE OR REPLACE FUNCTION pg_search_dmetaphone(text) RETURNS text LANGUAGE SQL IMMUTABLE STRICT AS $function$
2
2
  SELECT array_to_string(ARRAY(SELECT dmetaphone(unnest(regexp_split_to_array($1, E'\\\\s+')))), ' ')
3
3
  $function$;
4
-
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_search
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: '0.5'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-09 00:00:00.000000000 Z
12
+ date: 2012-05-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -70,6 +70,12 @@ files:
70
70
  - lib/pg_search/features/dmetaphone.rb
71
71
  - lib/pg_search/features/trigram.rb
72
72
  - lib/pg_search/features/tsearch.rb
73
+ - lib/pg_search/migration/associated_against_generator.rb
74
+ - lib/pg_search/migration/dmetaphone_generator.rb
75
+ - lib/pg_search/migration/multisearch_generator.rb
76
+ - lib/pg_search/migration/templates/add_pg_search_associated_against_support_functions.rb.erb
77
+ - lib/pg_search/migration/templates/add_pg_search_dmetaphone_support_functions.rb.erb
78
+ - lib/pg_search/migration/templates/create_pg_search_documents.rb
73
79
  - lib/pg_search/multisearch.rb
74
80
  - lib/pg_search/multisearchable.rb
75
81
  - lib/pg_search/normalizer.rb