search_generator 0.4 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/USAGE +3 -3
- data/search_generator.rb +4 -0
- data/templates/README +36 -10
- data/templates/app/controllers/controller.rb +23 -4
- data/templates/config/search.yml +10 -0
- data/templates/script/indexer +83 -68
- data/templates/script/searcher +27 -20
- metadata +4 -2
data/USAGE
CHANGED
@@ -9,13 +9,13 @@ SYNOPSIS
|
|
9
9
|
Good names, anything that means search would be good. Search,
|
10
10
|
Forage, Frisk or Shakedown.
|
11
11
|
|
12
|
-
search has an additional option --inspect which will
|
12
|
+
search has an additional option --inspect which will traverse your
|
13
13
|
Model objects and tell you which ones could be made searchable
|
14
14
|
|
15
15
|
DESCRIPTION
|
16
16
|
|
17
17
|
This generator integrates the SimpleSearch search engine into a
|
18
|
-
rails
|
18
|
+
rails application. In addition, once configured for you site, it
|
19
19
|
provides an Open Search (http://opensearch.a9.com/) compatible
|
20
20
|
interface.
|
21
21
|
|
@@ -27,7 +27,7 @@ DESCRIPTION
|
|
27
27
|
- Open Search RSS style results
|
28
28
|
* an indexer to run periodically against your data to keep the
|
29
29
|
search indexes up to date.
|
30
|
-
* a README on the additional hand coded tasks to
|
30
|
+
* a README on the additional hand coded tasks to integrate
|
31
31
|
the search engine with your application
|
32
32
|
|
33
33
|
EXAMPLE
|
data/search_generator.rb
CHANGED
@@ -73,6 +73,10 @@ howto_make_searchable
|
|
73
73
|
# layout
|
74
74
|
m.template "app/views/layouts/layout.rhtml", "app/views/layouts/#{file_name}.rhtml"
|
75
75
|
|
76
|
+
|
77
|
+
# config
|
78
|
+
m.template "config/search.yml", "config/search.yml"
|
79
|
+
|
76
80
|
# views
|
77
81
|
m.directory File.join("app/views", class_path, file_name)
|
78
82
|
m.template "app/views/search/index.rhtml", "app/views/#{file_name}/index.rhtml"
|
data/templates/README
CHANGED
@@ -26,6 +26,29 @@
|
|
26
26
|
make_searchable [ :title, :author, :content ]
|
27
27
|
end
|
28
28
|
|
29
|
+
=== Configuration
|
30
|
+
|
31
|
+
A default configuration file 'search.yml' in installed in your
|
32
|
+
application config directory. It should work as-is, but if you want
|
33
|
+
to move the simple search index file to a different location or
|
34
|
+
rename it, feel free to do so. Just be aware that the index file
|
35
|
+
must be relative to RAILS_ROOT.
|
36
|
+
|
37
|
+
Yes, if you look at the index file it does appear that other
|
38
|
+
backends could be available, but for now, there are no other search
|
39
|
+
backends besides SimpleSearch.
|
40
|
+
|
41
|
+
=== Index the data
|
42
|
+
|
43
|
+
Once your Models class are updated with make_searchable you'll need
|
44
|
+
to run script/indexer to crawl through your data and create the
|
45
|
+
index that is used to search the data.
|
46
|
+
|
47
|
+
Currently, this index is not automatically created and any new
|
48
|
+
content that is added to the database will not get indexed. You
|
49
|
+
should have cron, or a scheduled task run the indexer periodically
|
50
|
+
to keep your index in sync with your data.
|
51
|
+
|
29
52
|
=== Routes
|
30
53
|
|
31
54
|
Add the following lines to your config/routes.rb to hook in search
|
@@ -37,6 +60,9 @@
|
|
37
60
|
map.connect 'rss/opensearch/description.xml', :controller => '<%= file_name %>', :action => 'description'
|
38
61
|
map.connect 'rss/opensearch/:search_terms/:count', :controller => '<%= file_name %>', :action => 'rss', :count => '-1'
|
39
62
|
|
63
|
+
Remember to put these routes before the default route or you will
|
64
|
+
get errors.
|
65
|
+
|
40
66
|
=== Layouts
|
41
67
|
|
42
68
|
A 'very' basic layout/<%= file_name %>.rhtml is installed that shows the basic
|
@@ -61,21 +87,21 @@
|
|
61
87
|
the results of your search that are of Article class.
|
62
88
|
|
63
89
|
The generic index.rhtml that is present will automatically use
|
64
|
-
the appropriate
|
90
|
+
the appropriate _partial.rhtml file based upon the class name if
|
65
91
|
it is present.
|
66
92
|
|
67
93
|
2. Open Search
|
68
94
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
95
|
+
This view is to return results in the format of OpenSearch RSS
|
96
|
+
(http://opensearch.a9.com/spec/opensearchrss/1.0/). This
|
97
|
+
combined with the rss/opensearch Routes above and the
|
98
|
+
description.rxml file found in this view should provide you with
|
99
|
+
an OpenSearch-able site.
|
74
100
|
|
75
|
-
|
76
|
-
|
101
|
+
Again, you should create a _partial.rhtml file in this directory
|
102
|
+
for each Model you have that can return search results.
|
77
103
|
|
78
|
-
|
79
|
-
|
104
|
+
Also, the description.xml file should be configure with your
|
105
|
+
sites information.
|
80
106
|
|
81
107
|
|
@@ -12,16 +12,34 @@ class <%= class_name %>Controller < ApplicationController
|
|
12
12
|
# return those to the caller
|
13
13
|
#-----------------------------------------------------------------------
|
14
14
|
def search
|
15
|
-
@
|
15
|
+
@search_config = YAML.load(File.open(File.join(RAILS_ROOT,"config","search.yml")))
|
16
|
+
|
17
|
+
if @params['search_terms'].nil? then
|
18
|
+
@search_terms = @search_config['default_search_terms']
|
19
|
+
else
|
20
|
+
@search_terms = @params['search_terms'].split
|
21
|
+
end
|
22
|
+
|
16
23
|
@count = @params['count'].to_i || -1
|
17
|
-
|
24
|
+
|
25
|
+
self.send("#{@search_config['search_backend']}_search")
|
26
|
+
end
|
27
|
+
|
28
|
+
def simple_search
|
29
|
+
|
18
30
|
empty_contents = MockContents.new
|
19
|
-
|
31
|
+
|
32
|
+
index_filename = File.join(RAILS_ROOT,@search_config['simple_backend']['index_filename'])
|
33
|
+
|
34
|
+
if not File.exists?(index_filename) then
|
35
|
+
raise "content index file (#{File.expand_path(index_filename)}) does not exist. Did you run scripts/indexer ?"
|
36
|
+
end
|
37
|
+
|
38
|
+
simple_index = Search::Simple::Searcher.load(empty_contents,index_filename)
|
20
39
|
|
21
40
|
# what to return to the caller
|
22
41
|
@results = Array.new
|
23
42
|
|
24
|
-
# TODO: sort by score at some point ?
|
25
43
|
search_results = simple_index.find_words(@search_terms)
|
26
44
|
|
27
45
|
if search_results.contains_matches then
|
@@ -36,6 +54,7 @@ class <%= class_name %>Controller < ApplicationController
|
|
36
54
|
@results.uniq!
|
37
55
|
end
|
38
56
|
|
57
|
+
|
39
58
|
def description
|
40
59
|
@headers["Content-Type"] = "text/xml"
|
41
60
|
render 'opensearch/description'
|
data/templates/script/indexer
CHANGED
@@ -1,82 +1,97 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
3
|
+
# load the rails environment
|
4
|
+
require File.join(File.dirname(__FILE__),"..","config", "environment")
|
4
5
|
|
5
|
-
|
6
|
+
puts "Loading config [#{File.expand_path(File.join(RAILS_ROOT,"config","search.yml"))}]"
|
7
|
+
SEARCH_CONFIG = YAML.load(File.open(File.join(RAILS_ROOT,"config","search.yml")))
|
6
8
|
|
7
9
|
#-----------------------------------------------------------------------
|
8
|
-
#
|
10
|
+
# define how to build a index using SimpleSearch
|
9
11
|
#-----------------------------------------------------------------------
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
#-----------------------------------------------------------------------
|
16
|
-
|
17
|
-
|
18
|
-
#-----------------------------------------------------------------------
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#-----------------------------------------------------------------------
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
if
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
12
|
+
def simple_index
|
13
|
+
require_gem "SimpleSearch"
|
14
|
+
|
15
|
+
#-----------------------------------------------------------------------
|
16
|
+
# the container for all the contents to be indexed
|
17
|
+
#-----------------------------------------------------------------------
|
18
|
+
contents = Search::Simple::Contents.new
|
19
|
+
|
20
|
+
#-----------------------------------------------------------------------
|
21
|
+
# the current time is used to invalidate the Searcher's cache so it will
|
22
|
+
# force an indexing
|
23
|
+
#-----------------------------------------------------------------------
|
24
|
+
ts = Time.now
|
25
|
+
|
26
|
+
#-----------------------------------------------------------------------
|
27
|
+
# iterate over all the files in the models directory looking for those
|
28
|
+
# Models that are descendants of ActiveRecord
|
29
|
+
#-----------------------------------------------------------------------
|
30
|
+
puts "Searching for Models..."
|
31
|
+
Dir.glob(File.join(RAILS_ROOT,"app","models","*.rb")).each do |rbfile|
|
32
|
+
|
33
|
+
bname = File.basename(rbfile,'.rb')
|
34
|
+
classname = Inflector.camelize(bname)
|
35
|
+
classvar = eval(classname)
|
36
|
+
|
37
|
+
if classvar.respond_to?(:descends_from_active_record?) and
|
38
|
+
classvar.descends_from_active_record? then
|
39
|
+
puts "\tFound #{classname} "
|
40
|
+
|
41
|
+
|
42
|
+
#---------------------------------------------------------------
|
43
|
+
# if the class that is a dscendant from ActiveRecord also has
|
44
|
+
# the special method :searchable_fields then those fields are
|
45
|
+
# considered searchable
|
46
|
+
#---------------------------------------------------------------
|
47
|
+
if classvar.respond_to?(:searchable_fields) then
|
48
|
+
|
49
|
+
if not classvar.searchable_fields.nil?
|
50
|
+
|
51
|
+
cnames = classvar.searchable_fields.collect {|c| c.to_s}
|
52
|
+
puts "\t\t- searching columns #{cnames.join(",")}"
|
53
|
+
indexed_records = 0
|
54
|
+
|
55
|
+
#-----------------------------------------------------------
|
56
|
+
# now we interate over all the records of the particular
|
57
|
+
# type and record all the information for indexing.
|
58
|
+
#-----------------------------------------------------------
|
59
|
+
classvar.find_all.each do |ar|
|
60
|
+
record_id = "#{classname}.#{ar.send(classvar.primary_key)}"
|
61
|
+
indexed_records = indexed_records + 1
|
62
|
+
|
63
|
+
classvar.searchable_fields.each do |column|
|
64
|
+
column_id = "#{record_id}.#{column.to_s}"
|
65
|
+
data = ar.send(column)
|
66
|
+
|
67
|
+
# only index non-nil content
|
68
|
+
if not data.nil? then
|
69
|
+
contents << Search::Simple::Content.new(ar.send(column), column_id, ts)
|
70
|
+
end
|
62
71
|
end
|
63
72
|
end
|
64
|
-
end
|
65
73
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
74
|
+
puts "\t\t- indexed #{indexed_records} records"
|
75
|
+
else
|
76
|
+
puts "\t\t- no make_searchable fields"
|
77
|
+
end
|
70
78
|
|
71
|
-
|
72
|
-
|
79
|
+
else
|
80
|
+
puts "\t\t- class is not searchable"
|
81
|
+
end
|
73
82
|
end
|
74
83
|
end
|
75
|
-
end
|
76
84
|
|
77
|
-
#-----------------------------------------------------------------------
|
78
|
-
# this is where the actual indexing is done. There previous sections of
|
79
|
-
# code just collected the data to do the indexing.
|
80
|
-
#-----------------------------------------------------------------------
|
81
|
-
|
85
|
+
#-----------------------------------------------------------------------
|
86
|
+
# this is where the actual indexing is done. There previous sections of
|
87
|
+
# code just collected the data to do the indexing.
|
88
|
+
#-----------------------------------------------------------------------
|
89
|
+
index_file = File.join(RAILS_ROOT,SEARCH_CONFIG['simple_backend']['index_filename'])
|
90
|
+
puts "creating index file #{File.expand_path(index_file)}"
|
91
|
+
s = Search::Simple::Searcher.load(contents,index_file)
|
92
|
+
end
|
82
93
|
|
94
|
+
case SEARCH_CONFIG['search_backend']
|
95
|
+
when 'simple'
|
96
|
+
simple_index
|
97
|
+
end
|
data/templates/script/searcher
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
3
|
+
require File.join(File.dirname(__FILE__),"..","config","environment")
|
4
4
|
|
5
5
|
require_dependency "search_system"
|
6
|
+
#-----------------------------------------------------------------------
|
7
|
+
# load the config
|
8
|
+
#-----------------------------------------------------------------------
|
9
|
+
search_config = YAML.load(File.open(File.join(RAILS_ROOT,"config","search.yml")))
|
6
10
|
|
7
11
|
#-----------------------------------------------------------------------
|
8
12
|
# grab the first word on the command line as the term to search for
|
@@ -11,28 +15,31 @@ search_terms = Array.new
|
|
11
15
|
if ARGV.size > 0 then
|
12
16
|
search_terms << ARGV[0]
|
13
17
|
else
|
14
|
-
puts "no search term given, searching for '
|
15
|
-
search_terms
|
18
|
+
puts "no search term given, searching for #{search_config['default_search_terms'].join(',')}"
|
19
|
+
search_terms = search_config['default_search_terms']
|
16
20
|
end
|
17
21
|
|
18
|
-
|
19
|
-
|
20
|
-
#-----------------------------------------------------------------------
|
21
|
-
contents
|
22
|
-
|
22
|
+
case search_config['search_backend']
|
23
|
+
when 'simple'
|
24
|
+
#-----------------------------------------------------------------------
|
25
|
+
# a mock contents so that we use the cache
|
26
|
+
#-----------------------------------------------------------------------
|
27
|
+
contents = MockContents.new
|
28
|
+
s = Search::Simple::Searcher.load(contents,search_config['simple_backend']['index_filename'])
|
23
29
|
|
24
|
-
#-----------------------------------------------------------------------
|
25
|
-
# do the search
|
26
|
-
#-----------------------------------------------------------------------
|
27
|
-
sr = s.find_words(search_terms)
|
30
|
+
#-----------------------------------------------------------------------
|
31
|
+
# do the search
|
32
|
+
#-----------------------------------------------------------------------
|
33
|
+
sr = s.find_words(search_terms)
|
28
34
|
|
29
|
-
if sr.contains_matches then
|
30
|
-
|
31
|
-
|
32
|
-
|
35
|
+
if sr.contains_matches then
|
36
|
+
puts "Score\t#File"
|
37
|
+
sr.results.sort.each do |res|
|
38
|
+
puts "#{res.score}\t#{res.name}"
|
39
|
+
end
|
40
|
+
else
|
41
|
+
puts sr.warnings
|
42
|
+
puts "No matches"
|
33
43
|
end
|
34
|
-
|
35
|
-
puts sr.warnings
|
36
|
-
puts "No matches"
|
44
|
+
|
37
45
|
end
|
38
|
-
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.10
|
|
3
3
|
specification_version: 1
|
4
4
|
name: search_generator
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: "0.
|
7
|
-
date: 2005-04-
|
6
|
+
version: "0.5"
|
7
|
+
date: 2005-04-24
|
8
8
|
summary: "[Rails] Search generator."
|
9
9
|
require_paths:
|
10
10
|
- "."
|
@@ -30,10 +30,12 @@ files:
|
|
30
30
|
- USAGE
|
31
31
|
- LICENSE
|
32
32
|
- search_generator.rb
|
33
|
+
- templates/config
|
33
34
|
- templates/README
|
34
35
|
- templates/lib
|
35
36
|
- templates/app
|
36
37
|
- templates/script
|
38
|
+
- templates/config/search.yml
|
37
39
|
- templates/lib/search_system.rb
|
38
40
|
- templates/app/controllers
|
39
41
|
- templates/app/views
|