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 +10 -1
- data/README.rdoc +9 -7
- data/lib/pg_search/configuration/column.rb +1 -1
- data/lib/pg_search/migration/associated_against_generator.rb +23 -0
- data/lib/pg_search/migration/dmetaphone_generator.rb +23 -0
- data/lib/pg_search/migration/multisearch_generator.rb +16 -0
- data/lib/pg_search/migration/templates/add_pg_search_associated_against_support_functions.rb.erb +21 -0
- data/lib/pg_search/migration/templates/add_pg_search_dmetaphone_support_functions.rb.erb +29 -0
- data/lib/pg_search/migration/templates/create_pg_search_documents.rb +17 -0
- data/lib/pg_search/multisearch.rb +1 -1
- data/lib/pg_search/railtie.rb +6 -0
- data/lib/pg_search/tasks.rb +7 -113
- data/lib/pg_search/version.rb +1 -1
- data/spec/pg_search/multisearch_spec.rb +2 -2
- data/spec/pg_search/multisearchable_spec.rb +6 -3
- data/spec/pg_search_spec.rb +164 -168
- data/sql/dmetaphone.sql +0 -1
- metadata +8 -2
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
|
-
$
|
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
|
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
|
-
$
|
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
|
-
$
|
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
|
|
@@ -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
|
+
|
data/lib/pg_search/migration/templates/add_pg_search_associated_against_support_functions.rb.erb
ADDED
@@ -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
|
data/lib/pg_search/railtie.rb
CHANGED
@@ -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
|
data/lib/pg_search/tasks.rb
CHANGED
@@ -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
|
7
|
-
task :rebuild => :environment do
|
8
|
-
raise
|
9
|
-
|
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
|
data/lib/pg_search/version.rb
CHANGED
@@ -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
|
-
|
101
|
-
|
100
|
+
expect {
|
101
|
+
record.destroy
|
102
|
+
}.to change(PgSearch::Document, :count).by(-1)
|
103
|
+
|
104
|
+
expect {
|
102
105
|
PgSearch::Document.find(document.id)
|
103
|
-
}.
|
106
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
104
107
|
end
|
105
108
|
end
|
106
109
|
end
|
data/spec/pg_search_spec.rb
CHANGED
@@ -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.
|
20
|
-
|
21
|
-
|
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.
|
31
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
}.
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
}.
|
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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
-
}.
|
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
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
-
}.
|
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
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
}.
|
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
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
-
}.
|
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
|
-
|
115
|
-
|
116
|
-
|
117
|
-
end
|
112
|
+
ModelWithPgSearch.pg_search_scope :with_unknown_ignoring, {}
|
113
|
+
|
114
|
+
expect {
|
118
115
|
ModelWithPgSearch.with_unknown_ignoring("foo")
|
119
|
-
}.
|
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
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
121
|
+
ModelWithPgSearch.pg_search_scope :with_unknown_ignoring,
|
122
|
+
lambda { |*| {} }
|
123
|
+
|
124
|
+
expect {
|
127
125
|
ModelWithPgSearch.with_unknown_ignoring("foo")
|
128
|
-
}.
|
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.
|
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.
|
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.
|
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.
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
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
|
-
|
346
|
+
expect {
|
332
347
|
ModelWithPgSearch.search_title_with_prefixes("abcd\303\251f")
|
333
|
-
|
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.
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
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.
|
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.
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
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.
|
417
|
-
|
418
|
-
|
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.
|
435
|
-
|
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.
|
452
|
-
|
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.
|
469
|
-
|
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.
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
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.
|
514
|
-
|
515
|
-
|
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.
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
]
|
554
|
+
ModelWithPgSearch.pg_search_scope :with_tsearch,
|
555
|
+
:against => :title,
|
556
|
+
:using => [
|
557
|
+
[:tsearch, {:dictionary => 'english'}]
|
558
|
+
]
|
558
559
|
|
559
|
-
|
560
|
+
ModelWithPgSearch.pg_search_scope :with_trigram,
|
561
|
+
:against => :title,
|
562
|
+
:using => :trigram
|
560
563
|
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
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.
|
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.
|
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.
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
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.
|
632
|
-
|
633
|
-
|
634
|
-
:
|
635
|
-
:
|
636
|
-
|
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
|
-
|
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.
|
655
|
-
|
656
|
-
|
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
|
-
|
659
|
+
expect {
|
662
660
|
ModelWithPgSearch.search_title_without_accents("abcd\303\251f")
|
663
|
-
|
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.
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
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
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
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.
|
730
|
+
results.first.pg_search_rank.should be_a Float
|
735
731
|
end
|
736
732
|
end
|
737
733
|
end
|
data/sql/dmetaphone.sql
CHANGED
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
|
+
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-
|
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
|