search_steroids 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +39 -0
- data/lib/generators/enable_tsearch/USAGE +14 -0
- data/lib/generators/enable_tsearch/enable_tsearch_generator.rb +45 -0
- data/lib/generators/enable_tsearch/templates/add_column_migration.rb +5 -0
- data/lib/generators/enable_tsearch/templates/add_trigger_migration.rb +27 -0
- data/lib/generators/enable_tsearch/templates/add_tsearch_index_migration.rb +8 -0
- data/lib/generators/enable_tsearch/templates/populate_search_terms_migration.rb +21 -0
- data/lib/generators/enable_tsearch/templates/populate_vector_migration.rb +8 -0
- data/lib/generators/generator_helpers.rb +10 -0
- data/lib/generators/search_context/USAGE +8 -0
- data/lib/generators/search_context/search_context_generator.rb +79 -0
- data/lib/generators/search_context/templates/add_aliases_trigger_migration.rb +23 -0
- data/lib/generators/search_context/templates/add_context_migration.rb +8 -0
- data/lib/generators/search_context/templates/alias.rb +4 -0
- data/lib/generators/search_context/templates/create_aliases_migration.rb +10 -0
- data/lib/generators/search_context/templates/create_model_migration.rb +11 -0
- data/lib/generators/search_context/templates/create_search_config_migration.rb +41 -0
- data/lib/generators/search_context/templates/install_migration.rb +12 -0
- data/lib/generators/search_context/templates/migration_helper.rb +42 -0
- data/lib/generators/search_context/templates/model.rb +29 -0
- data/lib/search_context.rb +5 -0
- data/lib/search_context/callbacks.rb +83 -0
- data/lib/search_context/methods.rb +114 -0
- data/lib/search_context/version.rb +3 -0
- data/lib/tasks/search_terms_tasks.rake +4 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/models/author.rb +4 -0
- data/spec/dummy/app/models/bottle.rb +3 -0
- data/spec/dummy/app/models/foo.rb +3 -0
- data/spec/dummy/app/models/name.rb +32 -0
- data/spec/dummy/app/models/name_alias.rb +4 -0
- data/spec/dummy/app/models/varietal.rb +27 -0
- data/spec/dummy/app/models/varietal_alias.rb +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +65 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +19 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/db/migrate/20130619161647_create_authors.rb +10 -0
- data/spec/dummy/db/migrate/20130620184542_install_trigram_extension.rb +12 -0
- data/spec/dummy/db/migrate/20130620184543_create_names.rb +9 -0
- data/spec/dummy/db/migrate/20130620184544_add_trigram_index_to_names.rb +8 -0
- data/spec/dummy/db/migrate/20130621211356_add_names_vector_to_authors.rb +5 -0
- data/spec/dummy/db/migrate/20130621211357_add_names_trigger_to_authors.rb +26 -0
- data/spec/dummy/db/migrate/20130621211358_add_names_tsearch_index_to_authors.rb +8 -0
- data/spec/dummy/db/migrate/20130622154705_add_names_search_config.rb +31 -0
- data/spec/dummy/db/migrate/20130623112439_create_name_aliases.rb +10 -0
- data/spec/dummy/db/migrate/20130623152928_populate_names_vector_on_authors.rb +8 -0
- data/spec/dummy/db/migrate/20130623152929_populate_names_from_authors.rb +20 -0
- data/spec/dummy/db/migrate/20130625023142_create_foos.rb +10 -0
- data/spec/dummy/db/migrate/20130625031604_create_varietals.rb +9 -0
- data/spec/dummy/db/migrate/20130625031605_add_varietals_search_config.rb +41 -0
- data/spec/dummy/db/migrate/20130625031606_add_trigram_index_to_varietals.rb +8 -0
- data/spec/dummy/db/migrate/20130625031607_create_varietal_aliases.rb +10 -0
- data/spec/dummy/db/migrate/20130625031608_add_varietal_aliases_trigger.rb +23 -0
- data/spec/dummy/db/migrate/20130625191624_add_name_aliases_trigger.rb +23 -0
- data/spec/dummy/db/migrate/20130629161005_create_bottles.rb +10 -0
- data/spec/dummy/db/migration_helper.rb +42 -0
- data/spec/dummy/db/schema.rb +63 -0
- data/spec/dummy/db/seeds.rb +17 -0
- data/spec/dummy/db/static/bottles.csv +201 -0
- data/spec/dummy/db/static/varietal_aliases.csv +433 -0
- data/spec/dummy/db/static/varietals.csv +295 -0
- data/spec/dummy/log/test.log +9362 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/spec/factories/authors.rb +8 -0
- data/spec/dummy/spec/factories/bottles.rb +8 -0
- data/spec/dummy/spec/factories/foos.rb +8 -0
- data/spec/dummy/spec/factories/names.rb +8 -0
- data/spec/dummy/spec/fixtures/varietals.yml +2135 -0
- data/spec/dummy/spec/generators/enable_tsearch_generator_spec.rb +35 -0
- data/spec/dummy/spec/generators/search_context_generator_spec.rb +32 -0
- data/spec/dummy/spec/lib/string_helpers_spec.rb +19 -0
- data/spec/dummy/spec/models/author_spec.rb +62 -0
- data/spec/dummy/spec/models/bottle_spec.rb +28 -0
- data/spec/dummy/spec/models/name_spec.rb +41 -0
- data/spec/dummy/spec/models/varietal_spec.rb +174 -0
- data/spec/dummy/spec/tmp/app/models/foo.rb +28 -0
- data/spec/dummy/spec/tmp/app/models/foo_alias.rb +4 -0
- data/spec/dummy/spec/tmp/db/migrate/20131010200231_install_trigram_extension.rb +12 -0
- data/spec/dummy/spec/tmp/db/migrate/20131010200232_create_foos.rb +9 -0
- data/spec/dummy/spec/tmp/db/migrate/20131010200233_add_foos_search_config.rb +41 -0
- data/spec/dummy/spec/tmp/db/migrate/20131010200234_add_trigram_index_to_foos.rb +8 -0
- data/spec/dummy/spec/tmp/db/migrate/20131010200235_create_foo_aliases.rb +10 -0
- data/spec/dummy/spec/tmp/db/migrate/20131010200236_add_foo_aliases_trigger.rb +23 -0
- data/spec/dummy/spec/tmp/db/migration_helper.rb +42 -0
- data/spec/spec_helper.rb +23 -0
- metadata +358 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'SearchTerms'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
Bundler::GemHelper.install_tasks
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
|
30
|
+
Rake::TestTask.new(:test) do |t|
|
31
|
+
t.libs << 'lib'
|
32
|
+
t.libs << 'test'
|
33
|
+
t.pattern = 'test/**/*_test.rb'
|
34
|
+
t.verbose = false
|
35
|
+
end
|
36
|
+
|
37
|
+
# include the dummy app rake tasks, so that you can create a test db etc from the root.
|
38
|
+
import 'spec/dummy/Rakefile'
|
39
|
+
task :default => :test
|
@@ -0,0 +1,14 @@
|
|
1
|
+
rails generate enable_tsearch <table> <context> <col1> <col2> ...
|
2
|
+
|
3
|
+
This assumes that you have already created a search_context using 'rails g search_context ...' and want to enable using that search_context from this related model.
|
4
|
+
|
5
|
+
This would generate the following
|
6
|
+
|
7
|
+
1. migration to add a context_vector column to table
|
8
|
+
2. migration to create a trigger that updates the context_vector column from <col1> <col2> ... whenever <table> is inserted or updated.
|
9
|
+
|
10
|
+
The migration to create the trigger is only an example, it'll work for simple cases but you'd need to edit it to create more complex and useful triggers, for example
|
11
|
+
if you needed to join to a remote table to get some columns from there.
|
12
|
+
|
13
|
+
|
14
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/active_model'
|
3
|
+
require 'rails/generators/migration'
|
4
|
+
require 'generators/generator_helpers.rb'
|
5
|
+
class EnableTsearchGenerator < Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
8
|
+
argument :table_name, :type => :string
|
9
|
+
argument :search_context, :type=>:string
|
10
|
+
argument :columns, :type => :array, :banner => "col1 col2 col3 ..."
|
11
|
+
|
12
|
+
|
13
|
+
def self.next_migration_number(path)
|
14
|
+
sleep(1) # force a new timestamp
|
15
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_context
|
19
|
+
tmp = " search_context [:#{columns.join(',:')}], :search_context=>:#{search_context}\n" unless columns.empty?
|
20
|
+
tmp = " search_context :a_method_that_returns_the_search_terms, :search_context=>:#{search_context}\n" if columns.empty?
|
21
|
+
inject_into_file "app/models/#{table_name.singularize}.rb", tmp , :after => /class #{table_name.singularize.camelize} < .*\n/
|
22
|
+
migrate_if_needed("add_column_migration.rb","db/migrate/add_#{column_name}_to_#{table_name}.rb")
|
23
|
+
migrate_if_needed("add_trigger_migration.rb", "db/migrate/add_#{search_context}_trigger_to_#{table_name}.rb")
|
24
|
+
migrate_if_needed("add_tsearch_index_migration.rb", "db/migrate/add_#{search_context}_tsearch_index_to_#{table_name}.rb")
|
25
|
+
migrate_if_needed("populate_vector_migration.rb", "db/migrate/populate_#{column_name}_on_#{table_name}.rb")
|
26
|
+
migrate_if_needed("populate_search_terms_migration.rb", "db/migrate/populate_#{search_context}_from_#{table_name}.rb")
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
def column_name
|
31
|
+
"#{search_context}_vector"
|
32
|
+
end
|
33
|
+
def search_config_name
|
34
|
+
"#{search_context}_search_config"
|
35
|
+
end
|
36
|
+
def index_name
|
37
|
+
"idx_#{column_name}_on_#{table_name}"
|
38
|
+
end
|
39
|
+
def trigger_name
|
40
|
+
"update_#{column_name}"
|
41
|
+
end
|
42
|
+
def trigger_sp_name
|
43
|
+
"sp_update_#{column_name}"
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
execute <<-EOS
|
4
|
+
-- feel free to customize this function if you want to join to columns from another table, for example
|
5
|
+
CREATE OR REPLACE FUNCTION <%=trigger_sp_name %>() RETURNS trigger AS $$
|
6
|
+
begin
|
7
|
+
new.<%=column_name %> :=
|
8
|
+
<% sep='';columns.each do |column| -%>
|
9
|
+
<%=sep%>setweight(to_tsvector('<%=search_config_name %>', coalesce(new.<%=column %>,'')), 'A')
|
10
|
+
<%sep='|| ' -%>
|
11
|
+
<% end -%>;
|
12
|
+
return new;
|
13
|
+
end
|
14
|
+
$$ LANGUAGE plpgsql;
|
15
|
+
EOS
|
16
|
+
execute <<-EOS
|
17
|
+
DROP TRIGGER IF EXISTS <%=trigger_name %> ON <%=table_name %>;
|
18
|
+
CREATE TRIGGER <%=trigger_name %> BEFORE INSERT OR UPDATE
|
19
|
+
ON <%=table_name %> FOR EACH ROW EXECUTE PROCEDURE <%=trigger_sp_name %>();
|
20
|
+
EOS
|
21
|
+
end
|
22
|
+
def down
|
23
|
+
execute "DROP TRIGGER <%=trigger_name %>"
|
24
|
+
execute "DROP FUNCTION <%=trigger_sp_name %>"
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
execute <<-EOS
|
4
|
+
-- DELETE from <%=search_context %>;
|
5
|
+
INSERT into <%=search_context %>(name, count,created_at, updated_at)
|
6
|
+
SELECT word, ndoc as count, now() as created_at, now() as updated_at
|
7
|
+
FROM ts_stat(
|
8
|
+
'SELECT to_tsvector(''simple'',
|
9
|
+
<% sep='';columns.each do |column| -%>
|
10
|
+
<%=sep%>coalesce(<%=column %>,'''')
|
11
|
+
<%sep=" || '' '' ||" -%>
|
12
|
+
<% end -%>
|
13
|
+
) FROM <%=table_name %>
|
14
|
+
')
|
15
|
+
EOS
|
16
|
+
end
|
17
|
+
def down
|
18
|
+
execute %Q{DELETE from <%=search_context %>}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module SearchContext
|
2
|
+
module GeneratorHelpers
|
3
|
+
def migrate_if_needed(template,migration)
|
4
|
+
unless self.class.migration_exists?(File.dirname(migration),File.basename(migration).gsub('.rb',''))
|
5
|
+
migration_template template, migration
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
Rails::Generators::Base.send :include, SearchContext::GeneratorHelpers
|
@@ -0,0 +1,8 @@
|
|
1
|
+
rails generate search_context <context>
|
2
|
+
|
3
|
+
The <context> is the both the context of the words (ie, names) and the table that is used to store them in the database. Typically you only need one context, and the default name is 'search_terms', so you would type:
|
4
|
+
|
5
|
+
rails g search_context
|
6
|
+
|
7
|
+
That will generate the search_terms migration + model.
|
8
|
+
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/active_model'
|
3
|
+
require 'rails/generators/migration'
|
4
|
+
require 'generators/generator_helpers.rb'
|
5
|
+
|
6
|
+
class SearchContextGenerator < Rails::Generators::Base
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
source_root File.expand_path('../templates', __FILE__)
|
9
|
+
argument :context, :type => :string, :banner => "<context-name>"
|
10
|
+
class_option :dynamic, :type => :boolean, :default => false, :description => "include count so that the search context can be maintained dynamically"
|
11
|
+
|
12
|
+
|
13
|
+
def self.next_migration_number(path)
|
14
|
+
sleep(1) # force a new timestamp
|
15
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_context
|
19
|
+
migrate_if_needed "install_migration.rb", "db/migrate/install_trigram_extension.rb"
|
20
|
+
template 'model.rb', "app/models/#{model_file_name}.rb"
|
21
|
+
template 'alias.rb', "app/models/#{aliases_file_name}.rb"
|
22
|
+
template 'migration_helper.rb', "db/migration_helper.rb"
|
23
|
+
migrate_if_needed "create_model_migration.rb", "db/migrate/create_#{table_name}.rb"
|
24
|
+
migrate_if_needed "create_search_config_migration.rb", "db/migrate/add_#{search_config_name}.rb"
|
25
|
+
migrate_if_needed "add_context_migration.rb", "db/migrate/add_trigram_index_to_#{context}.rb"
|
26
|
+
migrate_if_needed "create_aliases_migration.rb", "db/migrate/create_#{aliases_name}.rb"
|
27
|
+
migrate_if_needed "add_aliases_trigger_migration.rb", "db/migrate/add_#{aliases_name}_trigger.rb"
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def column_name
|
33
|
+
"#{context}_vector"
|
34
|
+
end
|
35
|
+
|
36
|
+
def class_name
|
37
|
+
model_file_name.camelize
|
38
|
+
end
|
39
|
+
|
40
|
+
def model_file_name
|
41
|
+
context.underscore.singularize
|
42
|
+
end
|
43
|
+
|
44
|
+
def search_config_name
|
45
|
+
"#{table_name}_search_config"
|
46
|
+
end
|
47
|
+
|
48
|
+
def alias_search_config_name
|
49
|
+
"#{table_name}_alias_search_config"
|
50
|
+
end
|
51
|
+
|
52
|
+
def aliases_file_name
|
53
|
+
"#{table_name.singularize}_alias"
|
54
|
+
end
|
55
|
+
|
56
|
+
def aliases_name
|
57
|
+
"#{table_name.singularize}_aliases"
|
58
|
+
end
|
59
|
+
|
60
|
+
def aliases_class_name
|
61
|
+
"#{aliases_name.singularize.camelize}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def table_name
|
65
|
+
context.underscore.pluralize
|
66
|
+
end
|
67
|
+
def index_name
|
68
|
+
"idx_trgm_on_#{table_name}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def trigger_name
|
72
|
+
"trigger_update_#{aliases_name}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def trigger_sp_name
|
76
|
+
"sp_update_#{aliases_name}"
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
execute <<-EOS
|
4
|
+
CREATE OR REPLACE FUNCTION <%=trigger_sp_name %>() RETURNS trigger AS $$
|
5
|
+
begin
|
6
|
+
new.original_tsquery = plainto_tsquery('<%=alias_search_config_name%>', coalesce(new.original,''));
|
7
|
+
new.substitution_tsquery = plainto_tsquery('<%=alias_search_config_name%>', coalesce(new.substitution,''));
|
8
|
+
return new;
|
9
|
+
end
|
10
|
+
$$ LANGUAGE plpgsql;
|
11
|
+
EOS
|
12
|
+
execute <<-EOS
|
13
|
+
DROP TRIGGER IF EXISTS <%=trigger_name %> ON <%=aliases_name %>;
|
14
|
+
CREATE TRIGGER <%=trigger_name %> BEFORE INSERT OR UPDATE
|
15
|
+
ON <%=aliases_name %> FOR EACH ROW EXECUTE PROCEDURE <%=trigger_sp_name %>();
|
16
|
+
EOS
|
17
|
+
end
|
18
|
+
|
19
|
+
def down
|
20
|
+
execute "DROP TRIGGER <%=trigger_name %> on <%=aliases_name %>;"
|
21
|
+
execute "DROP FUNCTION <%=trigger_sp_name(); %>"
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
# populate this table with your query rewrite rules
|
4
|
+
create_table :<%=aliases_name %> do |t| t.timestamps end
|
5
|
+
add_column :<%=aliases_name %>, :original_tsquery, :tsquery
|
6
|
+
add_column :<%=aliases_name %>, :substitution_tsquery, :tsquery
|
7
|
+
add_column :<%=aliases_name %>, :original, :string
|
8
|
+
add_column :<%=aliases_name %>, :substitution, :string
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.expand_path('../../migration_helper', __FILE__)
|
2
|
+
include MigrationHelper
|
3
|
+
|
4
|
+
class <%= migration_class_name %> < ActiveRecord::Migration
|
5
|
+
def up
|
6
|
+
# install unaccent if it isn't installed'
|
7
|
+
install_extension('unaccent')
|
8
|
+
|
9
|
+
# create a new search configuration by copying an existing one. For my purposes, I wanted to use the french dictionary and
|
10
|
+
# remove all the accent characters. You could equally use the english dictionary as your base, or create your own.
|
11
|
+
# this is only intended to show you the basic command.
|
12
|
+
|
13
|
+
execute %Q{CREATE TEXT SEARCH CONFIGURATION <%=search_config_name%> ( COPY = pg_catalog.french );}
|
14
|
+
# From the docs, you typically don't want these mappings for simple searches of names and names of things, so I show you how to
|
15
|
+
# drop them here
|
16
|
+
# Parsing documents into tokens. It is useful to identify various classes of tokens, e.g., numbers, words, complex words,
|
17
|
+
# email addresses, so that they can be processed differently.
|
18
|
+
# In principle token classes depend on the specific application, but for most purposes it
|
19
|
+
# is adequate to use a predefined set of classes. PostgreSQL uses a parser to perform this step.
|
20
|
+
# A standard parser is provided, and custom parsers can be created for specific needs.
|
21
|
+
execute %{ALTER TEXT SEARCH CONFIGURATION <%=search_config_name%>
|
22
|
+
DROP MAPPING FOR email, file, float, host, int, sfloat, uint, url, url_path, version}
|
23
|
+
# remove all the accent characters.
|
24
|
+
execute %Q{ALTER TEXT SEARCH CONFIGURATION <%=search_config_name%>
|
25
|
+
ALTER MAPPING FOR hword, hword_part, word
|
26
|
+
WITH unaccent, french_stem;}
|
27
|
+
|
28
|
+
# create a variant of the simple dictionary that ignores accents
|
29
|
+
execute %Q{CREATE TEXT SEARCH CONFIGURATION <%=alias_search_config_name%> ( COPY = pg_catalog.simple );}
|
30
|
+
execute %{ALTER TEXT SEARCH CONFIGURATION <%=alias_search_config_name%>
|
31
|
+
DROP MAPPING FOR email, file, float, host, int, sfloat, uint, url, url_path, version}
|
32
|
+
execute %Q{ALTER TEXT SEARCH CONFIGURATION <%=alias_search_config_name%>
|
33
|
+
ALTER MAPPING FOR hword, hword_part, word
|
34
|
+
WITH unaccent, simple;}
|
35
|
+
end
|
36
|
+
|
37
|
+
def down
|
38
|
+
execute "DROP TEXT SEARCH CONFIGURATION <%=search_config_name%>"
|
39
|
+
execute "DROP TEXT SEARCH CONFIGURATION <%=alias_search_config_name%>"
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path('../../migration_helper', __FILE__)
|
2
|
+
include MigrationHelper
|
3
|
+
|
4
|
+
class <%= migration_class_name %> < ActiveRecord::Migration
|
5
|
+
def up
|
6
|
+
install_extension('pg_trgm')
|
7
|
+
end
|
8
|
+
|
9
|
+
def down
|
10
|
+
uninstall_extension('pg_trgm')
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module MigrationHelper
|
2
|
+
def support_create_extension?
|
3
|
+
# Assume that 9.1 or later supports CREATE EXTENSION
|
4
|
+
version_parts = select_value("SELECT version()").match(/PostgreSQL ([\d\.]*)/)[1].split('.')
|
5
|
+
version_parts[0].to_i >= 9 && version_parts[1].to_i >= 1
|
6
|
+
end
|
7
|
+
|
8
|
+
# Install the extension named extension_name
|
9
|
+
def install_extension(extension_name, function_name = nil)
|
10
|
+
if support_create_extension?
|
11
|
+
if select_value(%Q{select extname from pg_extension where extname = '#{extension_name}'}).nil?
|
12
|
+
execute "CREATE EXTENSION #{extension_name}"
|
13
|
+
end
|
14
|
+
else
|
15
|
+
# Speculating that the extension installs a function of the same name
|
16
|
+
function_name ||= extension_name
|
17
|
+
unless select_value("SELECT proname FROM pg_proc WHERE proname = '#{function_name}'")
|
18
|
+
puts "*" * 60
|
19
|
+
puts ""
|
20
|
+
puts "You need to install the #{extension_name} extension"
|
21
|
+
puts ""
|
22
|
+
sharedir = `pg_config --sharedir 2>&1`.strip
|
23
|
+
if $?.to_i > 0
|
24
|
+
puts "First, you need to know SHAREDIR - maybe something like `pg_config --sharedir`?"
|
25
|
+
puts ""
|
26
|
+
sharedir = 'SHAREDIR'
|
27
|
+
end
|
28
|
+
puts "Try `psql -d #{connection.current_database} -f #{sharedir}/contrib/#{extension_name}.sql`"
|
29
|
+
puts ""
|
30
|
+
puts "*" * 60
|
31
|
+
raise "Extension #{name} not installed."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
def uninstall_extension(extension_name)
|
36
|
+
if support_create_extension?
|
37
|
+
execute "DROP EXTENSION #{extension_name}"
|
38
|
+
else
|
39
|
+
puts "Skipping uninstall of extension #{extension_name}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|