acts_as_indexed 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/CHANGELOG +90 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +137 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/acts_as_indexed.gemspec +67 -0
- data/lib/acts_as_indexed.rb +248 -0
- data/lib/acts_as_indexed/configuration.rb +41 -0
- data/lib/acts_as_indexed/search_atom.rb +104 -0
- data/lib/acts_as_indexed/search_index.rb +325 -0
- data/lib/will_paginate_search.rb +29 -0
- data/rails/init.rb +2 -0
- data/test/abstract_unit.rb +52 -0
- data/test/acts_as_indexed_test.rb +133 -0
- data/test/configuration_test.rb +57 -0
- data/test/database.yml +10 -0
- data/test/fixtures/post.rb +5 -0
- data/test/fixtures/posts.yml +31 -0
- data/test/schema.rb +6 -0
- data/test/search_atom_test.rb +98 -0
- data/test/search_index_test.rb +50 -0
- metadata +94 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
===0.6.2 [11th June 2010]
|
2
|
+
- Now available as a Gem as well as the original plugin. [parndt - Thanks for doing most of the hard work.]
|
3
|
+
|
4
|
+
===0.6.0 [10th June 2010]
|
5
|
+
- Now supports Rails 3.x.x as well as Rails 2.x.x.
|
6
|
+
- Added global configuration options.
|
7
|
+
- Now recommending using with_query scope for searching.
|
8
|
+
- Deprecated find_with_index and will_paginate_search methods.
|
9
|
+
|
10
|
+
===0.5.3 [6th June 2010]
|
11
|
+
- Now supports non-standard table names automatically. [nandalopes]
|
12
|
+
|
13
|
+
===0.5.2 [3rd May 2010]
|
14
|
+
- Fix for Errno::ERANGE error related to certain Math.log calculations. [parndt]
|
15
|
+
- Improved index detection in a shared-directory environment. [bob-p]
|
16
|
+
|
17
|
+
===0.5.1 [11 June 2009]
|
18
|
+
- Fixed Ruby 1.8.6 compatibility.
|
19
|
+
|
20
|
+
===0.5.0 [24 April 2009]
|
21
|
+
- Ruby 1.9 and Rails 2.3 compatibility.
|
22
|
+
- Index location can now be set. Provides Heroku compatibility.
|
23
|
+
- Better errors on bad options.
|
24
|
+
- ActiveRecord order argument overrides ranking returned by find_by_index.
|
25
|
+
- Various test environment improvements
|
26
|
+
- Various Bugfixes
|
27
|
+
|
28
|
+
===0.4.6 [10 August 2008]
|
29
|
+
- Rolled in pagination.
|
30
|
+
|
31
|
+
===0.4.5 [04 February 2008]
|
32
|
+
- Fixed a bug where the find_options :limit would be added to the :offset, which caused incorrectly sized collections to be returned.
|
33
|
+
- Fixed an 'ambiguous column' error when using the :includes find_options key.
|
34
|
+
|
35
|
+
===0.4.4 [29 November 2007]
|
36
|
+
- Fixed a bug causing the weighting section of the code to error out.
|
37
|
+
|
38
|
+
===0.4.3 [27 September 2007]
|
39
|
+
- Fixed a bug causing records to be deleted from index during record updates.
|
40
|
+
|
41
|
+
===0.4.2 [27 September 2007]
|
42
|
+
- Fixed a bug causing identically ranked records to be lost.
|
43
|
+
|
44
|
+
===0.4.1 [22 September 2007]
|
45
|
+
- Fixed a bug in the main search method.
|
46
|
+
|
47
|
+
===0.4.0 [22 September 2007]
|
48
|
+
- Search results now ranked by relevance.
|
49
|
+
|
50
|
+
===0.3.3 [20 September 2007]
|
51
|
+
- Fixed index update bug where deleted atoms were not removed from index.
|
52
|
+
- Improved performance of quoted queries.
|
53
|
+
- Improved performance of index updates.
|
54
|
+
- When building a full index, records are retrieved and indexed in batches to reduce memory consumption.
|
55
|
+
|
56
|
+
===0.3.2 [19 September 2007]
|
57
|
+
- Fixed index update bug.
|
58
|
+
|
59
|
+
===0.3.1 [18 September 2007]
|
60
|
+
- Added RDoc documentation comments.
|
61
|
+
|
62
|
+
===0.3.0 [18 September 2007]
|
63
|
+
- Minor bug fixes.
|
64
|
+
- min_word_size now works properly, with quieries containing small words in
|
65
|
+
quotes or being preceded by a '+' symbol are now searched on.
|
66
|
+
|
67
|
+
===0.2.2 [06 September 2007]
|
68
|
+
- Search now caches query results within a session. Call the search twice in an
|
69
|
+
action? Only runs once!
|
70
|
+
|
71
|
+
===0.2.1 [05 September 2007]
|
72
|
+
- AR find options can now be passed to the search to allow finer control of
|
73
|
+
returned Model Objects.
|
74
|
+
|
75
|
+
===0.2.0 [04 September 2007]
|
76
|
+
- Major performance improvements.
|
77
|
+
- Index segmentation can now be tuned.
|
78
|
+
|
79
|
+
===0.1.1 [31 August 2007]
|
80
|
+
- Added a full set of tests.
|
81
|
+
- Fixed various set-manipulation based errors.
|
82
|
+
- Fixed a bug when searching for quoted phrases.
|
83
|
+
|
84
|
+
===0.1.01 [31 August 2007]
|
85
|
+
|
86
|
+
- Fixed a casting bug occurring when adding non-string fields to the index.
|
87
|
+
|
88
|
+
===0.1 [31 August 2007]
|
89
|
+
|
90
|
+
- Initial release.
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 - 2010 Douglas Shearer
|
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
@@ -0,0 +1,137 @@
|
|
1
|
+
= acts_as_indexed
|
2
|
+
|
3
|
+
If you find this plugin useful, please consider a donation to show your
|
4
|
+
support!
|
5
|
+
|
6
|
+
http://www.paypal.com/cgi-bin/webscr?cmd=_send-money
|
7
|
+
|
8
|
+
Paypal address: mailto:dougal.s@gmail.com
|
9
|
+
|
10
|
+
|
11
|
+
== Instructions
|
12
|
+
|
13
|
+
This plugin allows boolean-queried fulltext search to be added to any Rails
|
14
|
+
app with no dependencies and minimal setup.
|
15
|
+
|
16
|
+
|
17
|
+
== Resources
|
18
|
+
|
19
|
+
=== Install
|
20
|
+
|
21
|
+
== Rails 2.x.x
|
22
|
+
./script/plugin install git://github.com/dougal/acts_as_indexed.git
|
23
|
+
|
24
|
+
== Rails 3.x.x
|
25
|
+
rails plugin install git://github.com/dougal/acts_as_indexed.git
|
26
|
+
|
27
|
+
=== As a Gem
|
28
|
+
Despite this being slightly against the the original ethos of the project,
|
29
|
+
acts_as_indexed is now available as a Gem as several people have requested it.
|
30
|
+
|
31
|
+
gem install acts_as_indexed
|
32
|
+
|
33
|
+
Make sure to specify the Gem in your environment.rb file (Rails 2.x.x), or the Gemfile (Rails 3.x.x).
|
34
|
+
|
35
|
+
If you don't have git installed, you can download the plugin from the GitHub
|
36
|
+
page (http://github.com/dougal/acts_as_indexed) and unpack it into the
|
37
|
+
<tt>vendor/plugins</tt> directory of your rails app.
|
38
|
+
|
39
|
+
== Usage
|
40
|
+
|
41
|
+
|
42
|
+
=== Setup
|
43
|
+
|
44
|
+
Add +acts_as_indexed+ to the top of any models you want to index, along with a
|
45
|
+
list of the fields you wish to be indexed.
|
46
|
+
|
47
|
+
class Post < ActiveRecord::Base
|
48
|
+
acts_as_indexed :fields => [:title, :body]
|
49
|
+
|
50
|
+
...
|
51
|
+
end
|
52
|
+
|
53
|
+
The fields are not limited to model fields, but can be any instance method of
|
54
|
+
the current model.
|
55
|
+
|
56
|
+
class User < ActiveRecord::Base
|
57
|
+
acts_as_indexed :fields => [:address, :fullname]
|
58
|
+
|
59
|
+
def fullname
|
60
|
+
self.firstname + ' ' + self.lastname
|
61
|
+
end
|
62
|
+
|
63
|
+
...
|
64
|
+
end
|
65
|
+
|
66
|
+
Any of the configuration options in the Further Configuration section can be added as to the acts_as_indexed method call. These will override any defaults or global configuration.
|
67
|
+
|
68
|
+
|
69
|
+
=== Searching
|
70
|
+
|
71
|
+
To search, call the +with_query+ named scope on your model, passing a query as
|
72
|
+
an argument.
|
73
|
+
|
74
|
+
# Returns array of Post objects.
|
75
|
+
my_search_results = Post.with_query('my search query')
|
76
|
+
|
77
|
+
# Chain it with any number of ActiveRecord methods and named_scopes.
|
78
|
+
my_search_results = Post.public.with_query('my search query').find(:all, :limit => 10) # return the first 10 matches which are public.
|
79
|
+
|
80
|
+
|
81
|
+
=== Query Options
|
82
|
+
|
83
|
+
The following query operators are supported:
|
84
|
+
|
85
|
+
* AND:: This is the default option. 'cat dog' will find records matching 'cat' AND 'dog'.
|
86
|
+
* NOT:: 'cat -dog' will find records matching 'cat' AND NOT 'dog'
|
87
|
+
* INCLUDE:: 'cat +me' will find records matching 'cat' and 'me', even if 'me' is smaller than the +min_word_size+
|
88
|
+
* "":: Quoted terms are matched as phrases. '"cat dog"' will find records matching the whole phrase. Quoted terms can be preceded by the NOT operator; 'cat -"big dog"' etc. Quoted terms can include words shorter than the +min_word_size+.
|
89
|
+
|
90
|
+
=== Pagination
|
91
|
+
|
92
|
+
Since +with_query+ is a named scope, WillPaginate can be used in the normal
|
93
|
+
fashion.
|
94
|
+
|
95
|
+
@images = Image.with_query('girl').paginate(:page => 1, :per_page => 5)
|
96
|
+
|
97
|
+
=== Further Configuration
|
98
|
+
|
99
|
+
A config block can be provided in your environment files or initializers.
|
100
|
+
Example showing defaults:
|
101
|
+
|
102
|
+
ActsAsIndexed.configure do |config|
|
103
|
+
config.index_file = [RAILS_ROOT,'index']
|
104
|
+
config.index_file_depth = 3
|
105
|
+
config.min_word_size = 3
|
106
|
+
end
|
107
|
+
|
108
|
+
A full rundown of the available configuration options can be found in
|
109
|
+
<tt>lib/acts_as_indexed/configuration.rb</tt>
|
110
|
+
|
111
|
+
== RDoc Documentation
|
112
|
+
|
113
|
+
To generate the RDoc documentation, run the <tt>rake rdoc</tt> task in the
|
114
|
+
acts_as_indexed plugin folder. Then point your web browser at
|
115
|
+
<tt>vendor/plugins/acts_as_indexed/rdoc/index.html</tt>.
|
116
|
+
|
117
|
+
Alternatively, you can view the rdoc documentation
|
118
|
+
online[http://rdoc.info/projects/dougal/acts_as_indexed/].
|
119
|
+
|
120
|
+
== Problems, Comments, Suggestions?
|
121
|
+
|
122
|
+
All of the above are most welcome. mailto:dougal.s@gmail.com
|
123
|
+
|
124
|
+
|
125
|
+
== Credits
|
126
|
+
|
127
|
+
Douglas F Shearer - http:douglasfshearer.com
|
128
|
+
|
129
|
+
|
130
|
+
== Future Releases
|
131
|
+
|
132
|
+
Future releases will be looking to add the following features:
|
133
|
+
* Optional html scrubbing during indexing.
|
134
|
+
* Ranking affected by field weightings.
|
135
|
+
* Support for DataMapper, Sequel and the various MongoDB ORMs.
|
136
|
+
* UTF-8 support. See the current solution here:
|
137
|
+
https://gist.github.com/193903bb4e0d6e5debe1
|
data/Rakefile
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the acts_as_indexed plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the acts_as_indexed plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'ActsAsIndexed'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README.rdoc')
|
21
|
+
rdoc.rdoc_files.include('CHANGELOG')
|
22
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
23
|
+
end
|
24
|
+
|
25
|
+
namespace :rcov do
|
26
|
+
desc "Generate a coverage report in coverage/"
|
27
|
+
task :gen do
|
28
|
+
sh "rcov --output coverage test/*_test.rb"
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Remove generated coverage files."
|
32
|
+
task :clobber do
|
33
|
+
sh "rm -rdf coverage"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
begin
|
38
|
+
require 'jeweler'
|
39
|
+
Jeweler::Tasks.new do |gemspec|
|
40
|
+
gemspec.name = "acts_as_indexed"
|
41
|
+
gemspec.summary = "Acts As Indexed is a plugin which provides a pain-free way to add fulltext search to your Ruby on Rails app"
|
42
|
+
gemspec.description = gemspec.summary
|
43
|
+
gemspec.email = "dougal.s@gmail.com"
|
44
|
+
gemspec.homepage = "http://github.com/dougal/acts_as_indexed"
|
45
|
+
gemspec.authors = ["Douglas F Shearer"]
|
46
|
+
end
|
47
|
+
Jeweler::GemcutterTasks.new
|
48
|
+
rescue LoadError
|
49
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
50
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.6.2
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{acts_as_indexed}
|
8
|
+
s.version = "0.6.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Douglas F Shearer"]
|
12
|
+
s.date = %q{2010-06-11}
|
13
|
+
s.description = %q{Acts As Indexed is a plugin which provides a pain-free way to add fulltext search to your Ruby on Rails app}
|
14
|
+
s.email = %q{dougal.s@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.rdoc"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".gitignore",
|
20
|
+
"CHANGELOG",
|
21
|
+
"MIT-LICENSE",
|
22
|
+
"README.rdoc",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"acts_as_indexed.gemspec",
|
26
|
+
"lib/acts_as_indexed.rb",
|
27
|
+
"lib/acts_as_indexed/configuration.rb",
|
28
|
+
"lib/acts_as_indexed/search_atom.rb",
|
29
|
+
"lib/acts_as_indexed/search_index.rb",
|
30
|
+
"lib/will_paginate_search.rb",
|
31
|
+
"rails/init.rb",
|
32
|
+
"test/abstract_unit.rb",
|
33
|
+
"test/acts_as_indexed_test.rb",
|
34
|
+
"test/configuration_test.rb",
|
35
|
+
"test/database.yml",
|
36
|
+
"test/fixtures/post.rb",
|
37
|
+
"test/fixtures/posts.yml",
|
38
|
+
"test/schema.rb",
|
39
|
+
"test/search_atom_test.rb",
|
40
|
+
"test/search_index_test.rb"
|
41
|
+
]
|
42
|
+
s.homepage = %q{http://github.com/dougal/acts_as_indexed}
|
43
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
44
|
+
s.require_paths = ["lib"]
|
45
|
+
s.rubygems_version = %q{1.3.7}
|
46
|
+
s.summary = %q{Acts As Indexed is a plugin which provides a pain-free way to add fulltext search to your Ruby on Rails app}
|
47
|
+
s.test_files = [
|
48
|
+
"test/abstract_unit.rb",
|
49
|
+
"test/acts_as_indexed_test.rb",
|
50
|
+
"test/configuration_test.rb",
|
51
|
+
"test/fixtures/post.rb",
|
52
|
+
"test/schema.rb",
|
53
|
+
"test/search_atom_test.rb",
|
54
|
+
"test/search_index_test.rb"
|
55
|
+
]
|
56
|
+
|
57
|
+
if s.respond_to? :specification_version then
|
58
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
59
|
+
s.specification_version = 3
|
60
|
+
|
61
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
62
|
+
else
|
63
|
+
end
|
64
|
+
else
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -0,0 +1,248 @@
|
|
1
|
+
# ActsAsIndexed
|
2
|
+
# Copyright (c) 2007 - 2010 Douglas F Shearer.
|
3
|
+
# http://douglasfshearer.com
|
4
|
+
# Distributed under the MIT license as included with this plugin.
|
5
|
+
|
6
|
+
require 'active_record'
|
7
|
+
|
8
|
+
require 'acts_as_indexed/configuration'
|
9
|
+
require 'acts_as_indexed/search_index'
|
10
|
+
require 'acts_as_indexed/search_atom'
|
11
|
+
|
12
|
+
module ActsAsIndexed #:nodoc:
|
13
|
+
|
14
|
+
# Holds the default configuration for acts_as_indexed.
|
15
|
+
|
16
|
+
@configuration = Configuration.new
|
17
|
+
|
18
|
+
# Returns the current configuration for acts_as_indexed.
|
19
|
+
|
20
|
+
def self.configuration
|
21
|
+
@configuration
|
22
|
+
end
|
23
|
+
|
24
|
+
# Call this method to modify defaults in your initializers.
|
25
|
+
#
|
26
|
+
# Example showing defaults:
|
27
|
+
# ActsAsIndexed.configure do |config|
|
28
|
+
# config.index_file = [Rails.root,'index']
|
29
|
+
# config.index_file_depth = 3
|
30
|
+
# config.min_word_size = 3
|
31
|
+
# end
|
32
|
+
|
33
|
+
def self.configure
|
34
|
+
self.configuration ||= Configuration.new
|
35
|
+
yield(configuration)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.included(mod)
|
39
|
+
mod.extend(ClassMethods)
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
|
44
|
+
# Declares a class as searchable.
|
45
|
+
#
|
46
|
+
# ====options:
|
47
|
+
# fields:: Names of fields to include in the index. Symbols pointing to
|
48
|
+
# instance methods of your model may also be given here.
|
49
|
+
# index_file_depth:: Tuning value for the index partitioning. Larger
|
50
|
+
# values result in quicker searches, but slower
|
51
|
+
# indexing. Default is 3.
|
52
|
+
# min_word_size:: Sets the minimum length for a word in a query. Words
|
53
|
+
# shorter than this value are ignored in searches
|
54
|
+
# unless preceded by the '+' operator. Default is 3.
|
55
|
+
# index_file:: Sets the location for the index. By default this is
|
56
|
+
# RAILS_ROOT/index. Specify as an array. Heroku, for
|
57
|
+
# example would use RAILS_ROOT/tmp/index, which would be
|
58
|
+
# set as [Rails.root,'tmp','index]
|
59
|
+
|
60
|
+
def acts_as_indexed(options = {})
|
61
|
+
class_eval do
|
62
|
+
extend ActsAsIndexed::SingletonMethods
|
63
|
+
end
|
64
|
+
include ActsAsIndexed::InstanceMethods
|
65
|
+
|
66
|
+
after_create :add_to_index
|
67
|
+
before_update :update_index
|
68
|
+
after_destroy :remove_from_index
|
69
|
+
|
70
|
+
# scope for Rails 3.x, named_scope for Rails 2.x.
|
71
|
+
if self.respond_to?(:where)
|
72
|
+
scope :with_query, lambda { |query| where("#{table_name}.id IN (?)", search_index(query, {}, {:ids_only => true})) }
|
73
|
+
else
|
74
|
+
named_scope :with_query, lambda { |query| { :conditions => ["#{table_name}.id IN (?)", search_index(query, {}, {:ids_only => true}) ] } }
|
75
|
+
end
|
76
|
+
|
77
|
+
cattr_accessor :aai_config, :aai_fields
|
78
|
+
|
79
|
+
self.aai_fields = options.delete(:fields)
|
80
|
+
raise(ArgumentError, 'no fields specified') if self.aai_fields.nil? || self.aai_fields.empty?
|
81
|
+
|
82
|
+
self.aai_config = ActsAsIndexed.configuration.dup
|
83
|
+
options.each do |k, v|
|
84
|
+
self.aai_config.send("#{k}=", v)
|
85
|
+
end
|
86
|
+
self.aai_config.index_file += [Rails.env, self.name]
|
87
|
+
end
|
88
|
+
|
89
|
+
# Adds the passed +record+ to the index. Index is built if it does not already exist. Clears the query cache.
|
90
|
+
|
91
|
+
def index_add(record)
|
92
|
+
build_index if !File.exists?(File.join(aai_config.index_file))
|
93
|
+
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size)
|
94
|
+
index.add_record(record)
|
95
|
+
index.save
|
96
|
+
@query_cache = {}
|
97
|
+
true
|
98
|
+
end
|
99
|
+
|
100
|
+
# Removes the passed +record+ from the index. Clears the query cache.
|
101
|
+
|
102
|
+
def index_remove(record)
|
103
|
+
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size)
|
104
|
+
# record won't be in index if it doesn't exist. Just return true.
|
105
|
+
return true if !index.exists?
|
106
|
+
index.remove_record(record)
|
107
|
+
index.save
|
108
|
+
@query_cache = {}
|
109
|
+
true
|
110
|
+
end
|
111
|
+
|
112
|
+
# Updates the index.
|
113
|
+
# 1. Removes the previous version of the record from the index
|
114
|
+
# 2. Adds the new version to the index.
|
115
|
+
|
116
|
+
def index_update(record)
|
117
|
+
build_index if !File.exists?(File.join(aai_config.index_file))
|
118
|
+
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size)
|
119
|
+
#index.remove_record(find(record.id))
|
120
|
+
#index.add_record(record)
|
121
|
+
index.update_record(record,find(record.id))
|
122
|
+
index.save
|
123
|
+
@query_cache = {}
|
124
|
+
true
|
125
|
+
end
|
126
|
+
|
127
|
+
# Finds instances matching the terms passed in +query+. Terms are ANDed by
|
128
|
+
# default. Returns an array of model instances or, if +ids_only+ is
|
129
|
+
# true, an array of integer IDs.
|
130
|
+
#
|
131
|
+
# Keeps a cache of matched IDs for the current session to speed up
|
132
|
+
# multiple identical searches.
|
133
|
+
#
|
134
|
+
# ====find_options
|
135
|
+
# Same as ActiveRecord#find options hash. An :order key will override
|
136
|
+
# the relevance ranking
|
137
|
+
#
|
138
|
+
# ====options
|
139
|
+
# ids_only:: Method returns an array of integer IDs when set to true.
|
140
|
+
# no_query_cache:: Turns off the query cache when set to true. Useful for testing.
|
141
|
+
|
142
|
+
def search_index(query, find_options={}, options={})
|
143
|
+
# Clear the query cache off if the key is set.
|
144
|
+
@query_cache = {} if (options.has_key?('no_query_cache') || options[:no_query_cache])
|
145
|
+
if !@query_cache || !@query_cache[query]
|
146
|
+
logger.debug('Query not in cache, running search.')
|
147
|
+
build_index if !File.exists?(File.join(aai_config.index_file))
|
148
|
+
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size)
|
149
|
+
@query_cache = {} if !@query_cache
|
150
|
+
@query_cache[query] = index.search(query)
|
151
|
+
else
|
152
|
+
logger.debug('Query held in cache.')
|
153
|
+
end
|
154
|
+
return @query_cache[query].sort.reverse.map(&:first) if options[:ids_only] || @query_cache[query].empty?
|
155
|
+
|
156
|
+
# slice up the results by offset and limit
|
157
|
+
offset = find_options[:offset] || 0
|
158
|
+
limit = find_options.include?(:limit) ? find_options[:limit] : @query_cache[query].size
|
159
|
+
part_query = @query_cache[query].sort.reverse.slice(offset,limit).map(&:first)
|
160
|
+
|
161
|
+
# Set these to nil as we are dealing with the pagination by setting
|
162
|
+
# exactly what records we want.
|
163
|
+
find_options[:offset] = nil
|
164
|
+
find_options[:limit] = nil
|
165
|
+
|
166
|
+
with_scope :find => find_options do
|
167
|
+
# Doing the find like this eliminates the possibility of errors occuring
|
168
|
+
# on either missing records (out-of-sync) or an empty results array.
|
169
|
+
records = find(:all, :conditions => [ "#{table_name}.id IN (?)", part_query])
|
170
|
+
|
171
|
+
if find_options.include?(:order)
|
172
|
+
records # Just return the records without ranking them.
|
173
|
+
else
|
174
|
+
# Results come back in random order from SQL, so order again.
|
175
|
+
ranked_records = {}
|
176
|
+
records.each do |r|
|
177
|
+
ranked_records[r] = @query_cache[query][r.id]
|
178
|
+
end
|
179
|
+
|
180
|
+
ranked_records.to_a.sort_by{|a| a.last }.reverse.map(&:first)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
private
|
187
|
+
|
188
|
+
# Builds an index from scratch for the current model class.
|
189
|
+
def build_index
|
190
|
+
increment = 500
|
191
|
+
offset = 0
|
192
|
+
while (records = find(:all, :limit => increment, :offset => offset)).size > 0
|
193
|
+
#p "offset is #{offset}, increment is #{increment}"
|
194
|
+
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size)
|
195
|
+
offset += increment
|
196
|
+
index.add_records(records)
|
197
|
+
index.save
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
# Adds model class singleton methods.
|
204
|
+
module SingletonMethods
|
205
|
+
|
206
|
+
# DEPRECATED. Use +with_query+ scope instead.
|
207
|
+
# Finds instances matching the terms passed in +query+.
|
208
|
+
#
|
209
|
+
# See ActsAsIndexed::ClassMethods#search_index.
|
210
|
+
def find_with_index(query='', find_options = {}, options = {})
|
211
|
+
warn "[DEPRECATION] `find_with_index` is deprecated and will be removed in a later release. Use `with_query(query)` instead."
|
212
|
+
search_index(query, find_options, options)
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
# Adds model class instance methods.
|
218
|
+
# Methods are called automatically by ActiveRecord on +save+, +destroy+,
|
219
|
+
# and +update+ of model instances.
|
220
|
+
module InstanceMethods
|
221
|
+
|
222
|
+
# Adds the current model instance to index.
|
223
|
+
# Called by ActiveRecord on +save+.
|
224
|
+
def add_to_index
|
225
|
+
self.class.index_add(self)
|
226
|
+
end
|
227
|
+
|
228
|
+
# Removes the current model instance to index.
|
229
|
+
# Called by ActiveRecord on +destroy+.
|
230
|
+
def remove_from_index
|
231
|
+
self.class.index_remove(self)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Updates current model instance index.
|
235
|
+
# Called by ActiveRecord on +update+.
|
236
|
+
def update_index
|
237
|
+
self.class.index_update(self)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
|
243
|
+
# reopen ActiveRecord and include all the above to make
|
244
|
+
# them available to all our models if they want it
|
245
|
+
|
246
|
+
ActiveRecord::Base.class_eval do
|
247
|
+
include ActsAsIndexed
|
248
|
+
end
|