search_generator 0.4 → 0.5
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/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
|