search_steroids 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +3 -0
  3. data/Rakefile +39 -0
  4. data/lib/generators/enable_tsearch/USAGE +14 -0
  5. data/lib/generators/enable_tsearch/enable_tsearch_generator.rb +45 -0
  6. data/lib/generators/enable_tsearch/templates/add_column_migration.rb +5 -0
  7. data/lib/generators/enable_tsearch/templates/add_trigger_migration.rb +27 -0
  8. data/lib/generators/enable_tsearch/templates/add_tsearch_index_migration.rb +8 -0
  9. data/lib/generators/enable_tsearch/templates/populate_search_terms_migration.rb +21 -0
  10. data/lib/generators/enable_tsearch/templates/populate_vector_migration.rb +8 -0
  11. data/lib/generators/generator_helpers.rb +10 -0
  12. data/lib/generators/search_context/USAGE +8 -0
  13. data/lib/generators/search_context/search_context_generator.rb +79 -0
  14. data/lib/generators/search_context/templates/add_aliases_trigger_migration.rb +23 -0
  15. data/lib/generators/search_context/templates/add_context_migration.rb +8 -0
  16. data/lib/generators/search_context/templates/alias.rb +4 -0
  17. data/lib/generators/search_context/templates/create_aliases_migration.rb +10 -0
  18. data/lib/generators/search_context/templates/create_model_migration.rb +11 -0
  19. data/lib/generators/search_context/templates/create_search_config_migration.rb +41 -0
  20. data/lib/generators/search_context/templates/install_migration.rb +12 -0
  21. data/lib/generators/search_context/templates/migration_helper.rb +42 -0
  22. data/lib/generators/search_context/templates/model.rb +29 -0
  23. data/lib/search_context.rb +5 -0
  24. data/lib/search_context/callbacks.rb +83 -0
  25. data/lib/search_context/methods.rb +114 -0
  26. data/lib/search_context/version.rb +3 -0
  27. data/lib/tasks/search_terms_tasks.rake +4 -0
  28. data/spec/dummy/README.rdoc +261 -0
  29. data/spec/dummy/Rakefile +7 -0
  30. data/spec/dummy/app/models/author.rb +4 -0
  31. data/spec/dummy/app/models/bottle.rb +3 -0
  32. data/spec/dummy/app/models/foo.rb +3 -0
  33. data/spec/dummy/app/models/name.rb +32 -0
  34. data/spec/dummy/app/models/name_alias.rb +4 -0
  35. data/spec/dummy/app/models/varietal.rb +27 -0
  36. data/spec/dummy/app/models/varietal_alias.rb +4 -0
  37. data/spec/dummy/config.ru +4 -0
  38. data/spec/dummy/config/application.rb +65 -0
  39. data/spec/dummy/config/boot.rb +10 -0
  40. data/spec/dummy/config/database.yml +19 -0
  41. data/spec/dummy/config/environment.rb +5 -0
  42. data/spec/dummy/config/environments/development.rb +37 -0
  43. data/spec/dummy/config/environments/production.rb +67 -0
  44. data/spec/dummy/config/environments/test.rb +37 -0
  45. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  46. data/spec/dummy/config/initializers/inflections.rb +15 -0
  47. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  48. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  49. data/spec/dummy/config/initializers/session_store.rb +8 -0
  50. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  51. data/spec/dummy/config/locales/en.yml +5 -0
  52. data/spec/dummy/config/routes.rb +58 -0
  53. data/spec/dummy/db/migrate/20130619161647_create_authors.rb +10 -0
  54. data/spec/dummy/db/migrate/20130620184542_install_trigram_extension.rb +12 -0
  55. data/spec/dummy/db/migrate/20130620184543_create_names.rb +9 -0
  56. data/spec/dummy/db/migrate/20130620184544_add_trigram_index_to_names.rb +8 -0
  57. data/spec/dummy/db/migrate/20130621211356_add_names_vector_to_authors.rb +5 -0
  58. data/spec/dummy/db/migrate/20130621211357_add_names_trigger_to_authors.rb +26 -0
  59. data/spec/dummy/db/migrate/20130621211358_add_names_tsearch_index_to_authors.rb +8 -0
  60. data/spec/dummy/db/migrate/20130622154705_add_names_search_config.rb +31 -0
  61. data/spec/dummy/db/migrate/20130623112439_create_name_aliases.rb +10 -0
  62. data/spec/dummy/db/migrate/20130623152928_populate_names_vector_on_authors.rb +8 -0
  63. data/spec/dummy/db/migrate/20130623152929_populate_names_from_authors.rb +20 -0
  64. data/spec/dummy/db/migrate/20130625023142_create_foos.rb +10 -0
  65. data/spec/dummy/db/migrate/20130625031604_create_varietals.rb +9 -0
  66. data/spec/dummy/db/migrate/20130625031605_add_varietals_search_config.rb +41 -0
  67. data/spec/dummy/db/migrate/20130625031606_add_trigram_index_to_varietals.rb +8 -0
  68. data/spec/dummy/db/migrate/20130625031607_create_varietal_aliases.rb +10 -0
  69. data/spec/dummy/db/migrate/20130625031608_add_varietal_aliases_trigger.rb +23 -0
  70. data/spec/dummy/db/migrate/20130625191624_add_name_aliases_trigger.rb +23 -0
  71. data/spec/dummy/db/migrate/20130629161005_create_bottles.rb +10 -0
  72. data/spec/dummy/db/migration_helper.rb +42 -0
  73. data/spec/dummy/db/schema.rb +63 -0
  74. data/spec/dummy/db/seeds.rb +17 -0
  75. data/spec/dummy/db/static/bottles.csv +201 -0
  76. data/spec/dummy/db/static/varietal_aliases.csv +433 -0
  77. data/spec/dummy/db/static/varietals.csv +295 -0
  78. data/spec/dummy/log/test.log +9362 -0
  79. data/spec/dummy/public/404.html +26 -0
  80. data/spec/dummy/public/422.html +26 -0
  81. data/spec/dummy/public/500.html +25 -0
  82. data/spec/dummy/public/favicon.ico +0 -0
  83. data/spec/dummy/script/rails +6 -0
  84. data/spec/dummy/spec/factories/authors.rb +8 -0
  85. data/spec/dummy/spec/factories/bottles.rb +8 -0
  86. data/spec/dummy/spec/factories/foos.rb +8 -0
  87. data/spec/dummy/spec/factories/names.rb +8 -0
  88. data/spec/dummy/spec/fixtures/varietals.yml +2135 -0
  89. data/spec/dummy/spec/generators/enable_tsearch_generator_spec.rb +35 -0
  90. data/spec/dummy/spec/generators/search_context_generator_spec.rb +32 -0
  91. data/spec/dummy/spec/lib/string_helpers_spec.rb +19 -0
  92. data/spec/dummy/spec/models/author_spec.rb +62 -0
  93. data/spec/dummy/spec/models/bottle_spec.rb +28 -0
  94. data/spec/dummy/spec/models/name_spec.rb +41 -0
  95. data/spec/dummy/spec/models/varietal_spec.rb +174 -0
  96. data/spec/dummy/spec/tmp/app/models/foo.rb +28 -0
  97. data/spec/dummy/spec/tmp/app/models/foo_alias.rb +4 -0
  98. data/spec/dummy/spec/tmp/db/migrate/20131010200231_install_trigram_extension.rb +12 -0
  99. data/spec/dummy/spec/tmp/db/migrate/20131010200232_create_foos.rb +9 -0
  100. data/spec/dummy/spec/tmp/db/migrate/20131010200233_add_foos_search_config.rb +41 -0
  101. data/spec/dummy/spec/tmp/db/migrate/20131010200234_add_trigram_index_to_foos.rb +8 -0
  102. data/spec/dummy/spec/tmp/db/migrate/20131010200235_create_foo_aliases.rb +10 -0
  103. data/spec/dummy/spec/tmp/db/migrate/20131010200236_add_foo_aliases_trigger.rb +23 -0
  104. data/spec/dummy/spec/tmp/db/migration_helper.rb +42 -0
  105. data/spec/spec_helper.rb +23 -0
  106. metadata +358 -0
@@ -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.
@@ -0,0 +1,3 @@
1
+ = SearchTerms
2
+
3
+ This project rocks and uses MIT-LICENSE.
@@ -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,5 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def change
3
+ add_column :<%=table_name %>, :<%=column_name %>, :tsvector
4
+ end
5
+ 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,8 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def up
3
+ execute "CREATE INDEX <%=index_name %> on <%=table_name %> USING gin (<%=column_name%>)"
4
+ end
5
+ def down
6
+ execute "DROP INDEX <%=index_name %>"
7
+ end
8
+ 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,8 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def up
3
+ # nop update that hopefully triggers the TRIGGER to update the vector
4
+ execute %Q{UPDATE <%=table_name%> set updated_at = updated_at}
5
+ end
6
+ def down
7
+ end
8
+ end
@@ -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,8 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def up
3
+ execute "CREATE INDEX <%=index_name %> on <%=table_name %> USING gin (name gin_trgm_ops)"
4
+ end
5
+ def down
6
+ execute "DROP INDEX <%=index_name %>"
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ class <%=aliases_class_name %> < ActiveRecord::Base
2
+ # database trigger maintains *_tsquery versions
3
+ attr_accessible :original, :substitution
4
+ 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,11 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def change
3
+ create_table :<%=table_name %> do |t|
4
+ t.string :name
5
+ <%if options.dynamic? -%>
6
+ t.integer :count
7
+ <%end%>
8
+ t.timestamps
9
+ end
10
+ end
11
+ 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