acts_as_fulltextable 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +1 -0
- data/acts_as_fulltextable.gemspec +19 -0
- data/lib/acts_as_fulltextable.rb +114 -0
- data/lib/acts_as_fulltextable/version.rb +3 -0
- data/lib/fulltext_row.rb +162 -0
- data/lib/generators/fulltext_rows_generator.rb +33 -0
- data/lib/generators/templates/migrate.rb +22 -0
- metadata +61 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in acts_as_fulltextable.gemspec
|
4
|
+
rails_version = '~> 3.1'
|
5
|
+
|
6
|
+
#gem 'actionpack', rails_version
|
7
|
+
gem 'activerecord', rails_version
|
8
|
+
|
9
|
+
gem 'rake', '~> 0.8.7'
|
10
|
+
#gem 'mocha', '0.9.7'
|
11
|
+
#gem 'sqlite3-ruby', '1.3.1'
|
12
|
+
gem 'mysql', :group => :mysql
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Anthony Figueroa
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# ActsAsFulltextable
|
2
|
+
|
3
|
+
This gem is based on the old Rails 2 plugin made by boone (https://github.com/boone/acts_as_fulltextable).
|
4
|
+
|
5
|
+
It allows you to create an auxiliary table to be used for full-text searches.
|
6
|
+
It behaves like a polymorphic association, so it can be used with any
|
7
|
+
ActiveRecord model.
|
8
|
+
|
9
|
+
|
10
|
+
It has been tested on Rails 3.1+. Ruby 1.9.1+.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
gem 'acts_as_fulltextable'
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle install
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem 'acts_as_fulltextable', :git => 'git://github.com/toptierlabs/acts_as_fulltextable.git'
|
25
|
+
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
Create a migration for the models that you want to make searches.
|
30
|
+
|
31
|
+
$ rails generate fulltext_rows model1 model2 model3 ....
|
32
|
+
$ rake db:migrate
|
33
|
+
|
34
|
+
|
35
|
+
Add acts_as_fulltextable in any model, followed by the list of searchable fields.
|
36
|
+
|
37
|
+
i.e.
|
38
|
+
```ruby
|
39
|
+
|
40
|
+
class Person < ActiveRecord::Base
|
41
|
+
attr_accessible :age, :description, :name
|
42
|
+
|
43
|
+
acts_as_fulltextable :description, :name
|
44
|
+
end
|
45
|
+
|
46
|
+
```
|
47
|
+
|
48
|
+
You can either run a search on a single model:
|
49
|
+
Model.find_fulltext('query to run', :limit => 10, :offset => 0)
|
50
|
+
|
51
|
+
Or you can run it on more models at once:
|
52
|
+
FulltextRow.search('query to run', :only => [:only, :this, :models], :limit => 10, :offset => 0)
|
53
|
+
|
54
|
+
## Warning
|
55
|
+
|
56
|
+
Should you add acts_as_fulltextable to a new model after the initial migration was run,
|
57
|
+
you should execute the following piece of code (a migration or script/console are both fine):
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
|
61
|
+
NewModel.find(:all).each {|i| i.create_fulltext_record}
|
62
|
+
|
63
|
+
```
|
64
|
+
|
65
|
+
## Contributing
|
66
|
+
|
67
|
+
1. Fork it
|
68
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
69
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
70
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
71
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'acts_as_fulltextable/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "acts_as_fulltextable"
|
8
|
+
gem.version = ActsAsFulltextable::VERSION
|
9
|
+
gem.authors = ["Anthony Figueroa"]
|
10
|
+
gem.email = ["afigueroa@toptierlabs.com"]
|
11
|
+
gem.description = %q{Creates an auxiliary table in order to be used with full-text searches}
|
12
|
+
gem.summary = %q{It allows you to create an auxiliary to be used for full-text searches. It behaves like a polymorphic association, so it can be used with any ActiveRecord model.}
|
13
|
+
gem.homepage = "https://github.com/toptierlabs/acts_as_fulltextable"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require "acts_as_fulltextable/version"
|
2
|
+
require "fulltext_row"
|
3
|
+
|
4
|
+
module ActsAsFulltextable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# Makes a model searchable.
|
9
|
+
# Takes a list of fields to use to create the index. It also take an option (:check_for_changes,
|
10
|
+
# which defaults to true) to tell the engine wether it should check if the value of a given
|
11
|
+
# instance has changed before it actually updates the associated fulltext row.
|
12
|
+
# If option :parent_id is not nulled, it is used as the field to be used as the parent of the record,
|
13
|
+
# which is useful if you want to limit your queries to a scope.
|
14
|
+
# If option :conditions is given, it should be a string containing a ruby expression that
|
15
|
+
# equates to true or nil/false. Records are tested with this condition and only those that return true
|
16
|
+
# add/update the FullTextRow. A record returning false that is already in FullTextRow is removed.
|
17
|
+
#
|
18
|
+
def acts_as_fulltextable(*attr_names)
|
19
|
+
puts '///////////////////////////'
|
20
|
+
puts attr_names
|
21
|
+
configuration = { :check_for_changes => true, :parent_id => nil, :conditions => "true" }
|
22
|
+
configuration.update(attr_names.pop) while attr_names.last.is_a?(Hash)
|
23
|
+
configuration[:fields] = attr_names.flatten.uniq.compact
|
24
|
+
puts 'Going to add act as fields'
|
25
|
+
class_attribute :fulltext_options
|
26
|
+
self.fulltext_options = configuration
|
27
|
+
|
28
|
+
extend FulltextableClassMethods
|
29
|
+
include FulltextableInstanceMethods
|
30
|
+
self.send('after_create', :create_fulltext_record)
|
31
|
+
self.send('after_update', :update_fulltext_record)
|
32
|
+
self.send('has_one', :fulltext_row, :as => :fulltextable, :dependent => :delete)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module FulltextableClassMethods
|
37
|
+
|
38
|
+
def fulltext_fields
|
39
|
+
self.fulltext_options[:fields]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Performs full-text search for objects of this class.
|
43
|
+
# It takes three options:
|
44
|
+
# * limit: maximum number of rows to return. Defaults to 10.
|
45
|
+
# * offset: offset to apply to query. Defaults to 0.
|
46
|
+
# * page: only available with will_paginate plugin installed.
|
47
|
+
# * active_record: wether a ActiveRecord objects should be returned or an Array of [class_name, id]
|
48
|
+
#
|
49
|
+
def find_fulltext(query, options = {})
|
50
|
+
default_options = {:active_record => true}
|
51
|
+
options = default_options.merge(options)
|
52
|
+
unless options[:page]
|
53
|
+
options = {:limit => 10, :offset => 0}.merge(options)
|
54
|
+
end
|
55
|
+
options[:only] = self.to_s.underscore.to_sym # Only look for object belonging to this class
|
56
|
+
# Pass from what class search is invoked.
|
57
|
+
options[:search_class] = Kernel.const_get(self.to_s)
|
58
|
+
|
59
|
+
FulltextRow.search(query, options)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.included(receiver)
|
64
|
+
receiver.extend(ClassMethods)
|
65
|
+
end
|
66
|
+
|
67
|
+
module FulltextableInstanceMethods
|
68
|
+
# Creates the fulltext_row record for self
|
69
|
+
#
|
70
|
+
def create_fulltext_record
|
71
|
+
puts '=================='
|
72
|
+
puts self.class.to_s
|
73
|
+
puts self.id
|
74
|
+
puts self.fulltext_value
|
75
|
+
puts self.parent_id_value
|
76
|
+
FulltextRow.create(:fulltextable_type => self.class.to_s, :fulltextable_id => self.id, :value => self.fulltext_value, :parent_id => self.parent_id_value) if eval self.class.fulltext_options[:conditions]
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns the parent_id value or nil if it wasn't set.
|
80
|
+
#
|
81
|
+
def parent_id_value
|
82
|
+
self.class.fulltext_options[:parent_id].nil? ? nil : self.send(self.class.fulltext_options[:parent_id])
|
83
|
+
end
|
84
|
+
|
85
|
+
# Updates self's fulltext_row record
|
86
|
+
#
|
87
|
+
def update_fulltext_record
|
88
|
+
if eval self.class.fulltext_options[:conditions]
|
89
|
+
if self.class.fulltext_options[:check_for_changes]
|
90
|
+
row = FulltextRow.find_by_fulltextable_type_and_fulltextable_id(self.class.to_s, self.id)
|
91
|
+
# If we haven't got a row for the record, yet, create it instead of updating it.
|
92
|
+
if row.nil?
|
93
|
+
self.create_fulltext_record
|
94
|
+
return
|
95
|
+
end
|
96
|
+
end
|
97
|
+
FulltextRow.update_all(["value = ?, parent_id = ?", self.fulltext_value, self.parent_id_value], ["fulltextable_type = ? AND fulltextable_id = ?", self.class.to_s, self.id]) if !(self.class.fulltext_options[:check_for_changes]) || (row.value != self.fulltext_value) || (self.parent_id_value != row.parent_id)
|
98
|
+
else
|
99
|
+
|
100
|
+
row = FulltextRow.find_by_fulltextable_type_and_fulltextable_id(self.class.to_s, self.id)
|
101
|
+
row.destroy unless row.nil?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns self's value created by concatenating fulltext fields for its class
|
106
|
+
#
|
107
|
+
def fulltext_value
|
108
|
+
full_value = self.class.fulltext_fields.map {|f| self.send(f)}.join("\n")
|
109
|
+
full_value
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
ActiveRecord::Base.send :include, ActsAsFulltextable
|
data/lib/fulltext_row.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# FulltextRow
|
2
|
+
#
|
3
|
+
# 2008-03-07
|
4
|
+
# Patched by Artūras Šlajus <x11@arturaz.net> for will_paginate support
|
5
|
+
# 2008-06-19
|
6
|
+
# Fixed a bug, see acts_as_fulltextable.rb
|
7
|
+
class FulltextRow < ActiveRecord::Base
|
8
|
+
attr_accessible :fulltextable_type, :fulltextable_id, :value, :parent_id
|
9
|
+
|
10
|
+
# If FULLTEXT_ROW_TABLE is set, use it as the table name
|
11
|
+
begin
|
12
|
+
set_table_name FULLTEXT_ROW_TABLE if Object.const_get('FULLTEXT_ROW_TABLE')
|
13
|
+
rescue
|
14
|
+
end
|
15
|
+
@@use_advanced_search = false
|
16
|
+
@@use_and_search = false
|
17
|
+
@@use_phrase_search = false
|
18
|
+
|
19
|
+
belongs_to :fulltextable,
|
20
|
+
:polymorphic => true
|
21
|
+
validates_presence_of :fulltextable_type, :fulltextable_id
|
22
|
+
validates_uniqueness_of :fulltextable_id,
|
23
|
+
:scope => :fulltextable_type
|
24
|
+
# Performs full-text search.
|
25
|
+
# It takes four options:
|
26
|
+
# * limit: maximum number of rows to return (use 0 for all). Defaults to 10.
|
27
|
+
# * offset: offset to apply to query. Defaults to 0.
|
28
|
+
# * page: only available with will_paginate.
|
29
|
+
# * active_record: wether a ActiveRecord objects should be returned or an Array of [class_name, id]
|
30
|
+
# * only: limit search to these classes. Defaults to all classes. (should be a symbol or an Array of symbols)
|
31
|
+
#
|
32
|
+
def self.search(query, options = {})
|
33
|
+
default_options = {:active_record => true, :parent_id => nil}
|
34
|
+
options = default_options.merge(options)
|
35
|
+
unless options[:page]
|
36
|
+
options = {:limit => 10, :offset => 0}.merge(options)
|
37
|
+
options[:offset] = 0 if options[:offset] < 0
|
38
|
+
unless options[:limit].nil?
|
39
|
+
options[:limit] = 10 if options[:limit] < 0
|
40
|
+
options[:limit] = nil if options[:limit] == 0
|
41
|
+
end
|
42
|
+
end
|
43
|
+
options[:only] = [options[:only]] unless options[:only].nil? || options[:only].is_a?(Array)
|
44
|
+
options[:only] = options[:only].map {|o| o.to_s.camelize}.uniq.compact unless options[:only].nil?
|
45
|
+
|
46
|
+
rows = raw_search(query, options[:only], options[:limit],
|
47
|
+
options[:offset], options[:parent_id], options[:page],
|
48
|
+
options[:search_class])
|
49
|
+
if options[:active_record]
|
50
|
+
types = {}
|
51
|
+
rows.each {|r| types.include?(r.fulltextable_type) ? (types[r.fulltextable_type] << r.fulltextable_id) : (types[r.fulltextable_type] = [r.fulltextable_id])}
|
52
|
+
objects = {}
|
53
|
+
types.each {|k, v| objects[k] = Object.const_get(k).find_all_by_id(v)}
|
54
|
+
objects.each {|k, v| v.sort! {|x, y| types[k].index(x.id) <=> types[k].index(y.id)}}
|
55
|
+
|
56
|
+
if defined?(WillPaginate) && options[:page]
|
57
|
+
result = WillPaginate::Collection.new(
|
58
|
+
rows.current_page,
|
59
|
+
rows.per_page,
|
60
|
+
rows.total_entries
|
61
|
+
)
|
62
|
+
else
|
63
|
+
result = []
|
64
|
+
end
|
65
|
+
|
66
|
+
rows.each {|r| result << objects[r.fulltextable_type].shift}
|
67
|
+
return result
|
68
|
+
else
|
69
|
+
return rows.map {|r| [r.fulltextable_type, r.fulltextable_id]}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Use advanced search mechanism, instead of pure fulltext search.
|
74
|
+
#
|
75
|
+
def self.use_advanced_search!
|
76
|
+
@@use_advanced_search = true
|
77
|
+
end
|
78
|
+
|
79
|
+
# Force usage of AND search instead of OR. Works only when advanced search
|
80
|
+
# is enabled.
|
81
|
+
#
|
82
|
+
def self.use_and_search!
|
83
|
+
@@use_and_search = true
|
84
|
+
end
|
85
|
+
|
86
|
+
# Force usage of phrase search instead of OR search. Doesn't work when
|
87
|
+
# advanced search is enabled.
|
88
|
+
#
|
89
|
+
def self.use_phrase_search!
|
90
|
+
@@use_phrase_search = true
|
91
|
+
end
|
92
|
+
private
|
93
|
+
# Performs a raw full-text search.
|
94
|
+
# * query: string to be searched
|
95
|
+
# * only: limit search to these classes. Defaults to all classes.
|
96
|
+
# * limit: maximum number of rows to return (use 0 for all). Defaults to 10.
|
97
|
+
# * offset: offset to apply to query. Defaults to 0.
|
98
|
+
# * parent_id: limit query to record with passed parent_id. An Array of ids is fine.
|
99
|
+
# * page: overrides limit and offset, only available with will_paginate.
|
100
|
+
# * search_class: from what class should we take .per_page? Only with will_paginate
|
101
|
+
#
|
102
|
+
def self.raw_search(query, only, limit, offset, parent_id = nil, page = nil, search_class = nil)
|
103
|
+
unless only.nil? || only.empty?
|
104
|
+
only_condition = " AND fulltextable_type IN (#{only.map {|c| (/\A\w+\Z/ === c.to_s) ? "'#{c.to_s}'" : nil}.uniq.compact.join(',')})"
|
105
|
+
else
|
106
|
+
only_condition = ''
|
107
|
+
end
|
108
|
+
unless parent_id.nil?
|
109
|
+
if parent_id.is_a?(Array)
|
110
|
+
only_condition += " AND parent_id IN (#{parent_id.join(',')})"
|
111
|
+
else
|
112
|
+
only_condition += " AND parent_id = #{parent_id.to_i}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
if @@use_advanced_search
|
117
|
+
query_parts = query.gsub(/[\*\+\-]/, '').split(' ')
|
118
|
+
if @@use_and_search
|
119
|
+
search_query = query_parts.map {|w| "+#{w}*"}.join(' ')
|
120
|
+
else
|
121
|
+
search_query = query_parts.map {|w| "#{w}"}.join(' ')
|
122
|
+
end
|
123
|
+
matches = []
|
124
|
+
matches << [query_parts.map {|w| "+#{w}"}.join(' '), 5] # match_all_exact
|
125
|
+
if @@use_and_search
|
126
|
+
matches << [query_parts.map {|w| "+#{w}*"}.join(' '), query_parts.size > 3 ? 2 : 1] # match_all_wildcard
|
127
|
+
else
|
128
|
+
matches << [query_parts.map {|w| "#{w}"}.join(' '), query_parts.size <= 3 ? 2.5 : 1] # match_some_exact
|
129
|
+
end
|
130
|
+
#matches << [search_query, 0.5] # match_some_wildcard
|
131
|
+
|
132
|
+
relevancy = matches.map {|m| sanitize_sql(["(match(`value`) against(? in boolean mode) * #{m[1]})", m[0]])}.join(' + ')
|
133
|
+
|
134
|
+
search_options = {
|
135
|
+
:conditions => [("match(value) against(? in boolean mode)" + only_condition), search_query],
|
136
|
+
:select => "fulltext_rows.fulltextable_type, fulltext_rows.fulltextable_id, #{relevancy} AS relevancy",
|
137
|
+
:order => "relevancy DESC, value ASC"
|
138
|
+
}
|
139
|
+
else
|
140
|
+
if @@use_phrase_search
|
141
|
+
query = "\"#{query}\""
|
142
|
+
else
|
143
|
+
query = query.gsub(/(\S+)/, '\1*')
|
144
|
+
end
|
145
|
+
search_options = {
|
146
|
+
:conditions => [("match(value) against(? in boolean mode)" + only_condition), query],
|
147
|
+
:select => "fulltext_rows.fulltextable_type, fulltext_rows.fulltextable_id, #{sanitize_sql(["match(`value`) against(? in boolean mode) AS relevancy", query])}",
|
148
|
+
:order => "relevancy DESC, value ASC"
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
if defined?(WillPaginate) && page
|
153
|
+
search_options = search_options.merge(:page => page)
|
154
|
+
unless search_class.nil?
|
155
|
+
search_options = search_options.merge(:per_page => search_class.per_page)
|
156
|
+
end
|
157
|
+
self.paginate(:all, search_options)
|
158
|
+
else
|
159
|
+
self.find(:all, search_options.merge(:limit => limit, :offset => offset))
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
|
5
|
+
class FulltextRowsGenerator < Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
argument :models, :type => :array
|
8
|
+
source_root File.expand_path("../templates", __FILE__)
|
9
|
+
|
10
|
+
attr_accessor :models
|
11
|
+
|
12
|
+
def initialize(*runtime_args)
|
13
|
+
super(*runtime_args)
|
14
|
+
#puts @models.to_json
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_model_migrations
|
18
|
+
migration_template("migrate.rb", 'db/migrate/create_fulltext_rows.rb')
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.next_migration_number(path)
|
23
|
+
@migration_number = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
def banner
|
28
|
+
"Usage: #{$0} [model1 model2 model3 ...]"
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class CreateFulltextRows < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :fulltext_rows, :options => 'ENGINE=MyISAM' do |t|
|
4
|
+
t.column :fulltextable_type, :string, :null => false, :limit => 50
|
5
|
+
t.column :fulltextable_id, :integer, :null => false
|
6
|
+
t.column :value, :text, :null => false, :default => ''
|
7
|
+
t.column :parent_id, :integer
|
8
|
+
end
|
9
|
+
|
10
|
+
[<%= @models.join(', ') %>].each do |m|
|
11
|
+
m.find(:all).each {|i| i.create_fulltext_record}
|
12
|
+
end
|
13
|
+
|
14
|
+
execute "CREATE FULLTEXT INDEX fulltext_index ON fulltext_rows (value)"
|
15
|
+
add_index :fulltext_rows, :parent_id
|
16
|
+
add_index :fulltext_rows, [:fulltextable_type, :fulltextable_id], :unique => true
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.down
|
20
|
+
drop_table :fulltext_rows
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: acts_as_fulltextable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Anthony Figueroa
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-26 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Creates an auxiliary table in order to be used with full-text searches
|
15
|
+
email:
|
16
|
+
- afigueroa@toptierlabs.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .DS_Store
|
22
|
+
- .gitignore
|
23
|
+
- Gemfile
|
24
|
+
- LICENSE.txt
|
25
|
+
- README.md
|
26
|
+
- Rakefile
|
27
|
+
- acts_as_fulltextable.gemspec
|
28
|
+
- lib/.DS_Store
|
29
|
+
- lib/acts_as_fulltextable.rb
|
30
|
+
- lib/acts_as_fulltextable/version.rb
|
31
|
+
- lib/fulltext_row.rb
|
32
|
+
- lib/generators/.DS_Store
|
33
|
+
- lib/generators/fulltext_rows_generator.rb
|
34
|
+
- lib/generators/templates/migrate.rb
|
35
|
+
homepage: https://github.com/toptierlabs/acts_as_fulltextable
|
36
|
+
licenses: []
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options: []
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
requirements: []
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 1.8.24
|
56
|
+
signing_key:
|
57
|
+
specification_version: 3
|
58
|
+
summary: It allows you to create an auxiliary to be used for full-text searches. It
|
59
|
+
behaves like a polymorphic association, so it can be used with any ActiveRecord
|
60
|
+
model.
|
61
|
+
test_files: []
|