bcms_google_mini_search 1.3.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +7 -3
- data/app/models/search_result.rb +104 -31
- data/app/portlets/google_mini_search_engine_portlet.rb +20 -4
- data/app/views/layouts/templates/default.html.erb.html.erb +17 -0
- data/app/views/portlets/google_mini_search_engine/_form.html.erb +2 -0
- data/app/views/portlets/google_mini_search_engine/render.html.erb +34 -13
- data/bcms_google_mini_search.gemspec +1 -3
- data/doc/sample_clustering_result.xml +39 -0
- data/lib/bcms_google_mini_search.rb +2 -0
- data/lib/bcms_google_mini_search/gsa.rb +144 -0
- data/lib/bcms_google_mini_search/version.rb +1 -1
- data/release_notes.txt +40 -0
- data/test/unit/gsa_test.rb +197 -0
- data/test/unit/portlets/google_mini_search_engine_portlet_test.rb +40 -2
- data/test/unit/search_result_test.rb +178 -57
- metadata +14 -9
- data/doc/README_FOR_APP +0 -2
data/README.markdown
CHANGED
@@ -8,10 +8,14 @@ It consists of the following two portlets.
|
|
8
8
|
2. Google Mini Search Results Portlet - Sends query to the Mini, formats the XML response and displays the results.
|
9
9
|
|
10
10
|
Features:
|
11
|
+
|
11
12
|
1. Allows for formatted results of GSA search results.
|
12
|
-
2. Will display Key Matches
|
13
|
-
3. Will display Synonyms/Related Queries
|
14
|
-
4. Allows conditional overriding of collections (i.e. by default it will search one collection, but a 'site=OTHER_COLLECTION' param can be passed to override that)
|
13
|
+
2. Will display Key Matches
|
14
|
+
3. Will display Synonyms/Related Queries
|
15
|
+
4. Allows conditional overriding of collections (i.e. by default it will search one default collection, but a 'site=OTHER_COLLECTION' param can be passed to override that)
|
16
|
+
5. Sort by Date/Relevance - Users can now toggle the results display between date and relevance.
|
17
|
+
6. Cached Links - Each result now correctly generate a link to the cached version of the document as stored by the GSA.
|
18
|
+
7. [GSA Only] Narrow Your Search - Shows users a set of other suggested queries to allow them to 'Narrow Your Search', based on the current query and what results are stored in GSA. Only available for GSA instances, as Google Mini does not support Dynamic Result Clustering.
|
15
19
|
|
16
20
|
Note: This module assume the BrowserCMS web site owner has access to their own GSA/Google Mini server, either hosted by
|
17
21
|
themselves or a third party service.
|
data/app/models/search_result.rb
CHANGED
@@ -3,12 +3,19 @@ require 'net/http'
|
|
3
3
|
|
4
4
|
class SearchResult
|
5
5
|
|
6
|
-
|
6
|
+
# Creates a new GSA::Appliance from a GoogleMiniSearchPortlet.
|
7
|
+
#
|
8
|
+
def self.new_gsa(portlet)
|
9
|
+
options = {:portlet=>portlet}
|
10
|
+
normalize_query_options(options)
|
11
|
+
GSA::Appliance.new(options)
|
12
|
+
end
|
7
13
|
|
8
14
|
#
|
9
15
|
# Queries google mini by a specific URL to find all the results. Converts XML results to
|
10
16
|
# a paging results of Search Results.
|
11
17
|
#
|
18
|
+
# See SearchResult#query_url for acceptable _options_ params
|
12
19
|
def self.find(query, options={})
|
13
20
|
return QueryResult.new unless query
|
14
21
|
xml_doc = fetch_xml_doc(query, options)
|
@@ -19,6 +26,14 @@ class SearchResult
|
|
19
26
|
results
|
20
27
|
end
|
21
28
|
|
29
|
+
def self.create_query(query, options={})
|
30
|
+
opts = options.clone
|
31
|
+
normalize_query_options(opts)
|
32
|
+
opts[:query] = query
|
33
|
+
opts[:engine] = GSA::Engine.new({:host => opts[:host]})
|
34
|
+
GSA::Query.new(opts)
|
35
|
+
end
|
36
|
+
|
22
37
|
def self.parse_results_count(xml_doc)
|
23
38
|
root = xml_doc.root
|
24
39
|
count = root.elements["RES/M"]
|
@@ -29,16 +44,7 @@ class SearchResult
|
|
29
44
|
root = xml_doc.root
|
30
45
|
results = []
|
31
46
|
xml_doc.elements.each('GSP/RES/R') do |ele|
|
32
|
-
|
33
|
-
result.number = ele.attributes["N"]
|
34
|
-
result.title = ele.elements["T"].text
|
35
|
-
result.url = ele.elements["U"].text
|
36
|
-
result.description = ele.elements["S"].text
|
37
|
-
|
38
|
-
doc_size_ele = ele.elements["HAS/C"]
|
39
|
-
result.size = doc_size_ele ? doc_size_ele.attributes["SZ"] : ""
|
40
|
-
|
41
|
-
results << result
|
47
|
+
results << GSA::Result.new(ele)
|
42
48
|
end
|
43
49
|
results
|
44
50
|
end
|
@@ -62,39 +68,76 @@ class SearchResult
|
|
62
68
|
num_pages
|
63
69
|
end
|
64
70
|
|
71
|
+
# Construct a query url for the GSA.
|
72
|
+
#
|
73
|
+
# @param [String] query
|
74
|
+
# @param [Hash] options
|
75
|
+
# @option :host
|
76
|
+
# @option :start
|
77
|
+
# @option :front_end
|
78
|
+
# @option :collection
|
79
|
+
# @option :sort
|
80
|
+
# @option :as_xml [Boolean] Determines if the results are returned as xml or html. Default to false.
|
81
|
+
def self.query_url(query, options)
|
82
|
+
options[:as_xml] = true if options[:as_xml].nil?
|
65
83
|
|
66
|
-
def self.build_mini_url(options, query)
|
67
|
-
portlet = find_search_engine_portlet(options)
|
68
84
|
encoded_query = CGI::escape(query)
|
69
85
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
74
|
-
# encoded_query = query
|
75
|
-
url = "#{portlet.service_url}/search?q=#{encoded_query}&output=xml_no_dtd&client=#{portlet.front_end_name}&site=#{site}&filter=0"
|
86
|
+
# Turns off automatic results filter (filter=0), which when set to 1, allows mini to reduces the # of similar/duplicate results,
|
87
|
+
# but makes it hard to determine the total # of results.
|
88
|
+
url = "#{options[:host]}/search?q=#{encoded_query}&output=xml_no_dtd&client=#{options[:front_end]}&site=#{options[:collection]}&filter=0"
|
76
89
|
if options[:start]
|
77
90
|
url = url + "&start=#{options[:start]}"
|
78
91
|
end
|
79
|
-
|
92
|
+
|
93
|
+
if options[:sort]
|
94
|
+
url += "&sort=#{CGI::escape(options[:sort])}"
|
95
|
+
end
|
96
|
+
|
97
|
+
unless options[:as_xml]
|
98
|
+
url += "&proxystylesheet=#{options[:front_end]}"
|
99
|
+
end
|
100
|
+
|
101
|
+
# Ensure both results (oe) and query/input values (ie) are interpreted as UTF-8.
|
102
|
+
# See http://code.google.com/apis/searchappliance/documentation/46/xml_reference.html#request_i18n
|
103
|
+
url += "&oe=UTF-8&ie=UTF-8"
|
104
|
+
return url
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.create_url_for_query(options, query)
|
108
|
+
normalize_query_options(options)
|
109
|
+
return query_url(query, options)
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.normalize_query_options(options)
|
113
|
+
portlet = find_search_engine_portlet(options)
|
114
|
+
options[:front_end] = portlet.front_end_name
|
115
|
+
options[:collection] = portlet.collection_name
|
116
|
+
options[:host] = portlet.service_url
|
117
|
+
|
118
|
+
options[:collection] = options.delete(:site) if options[:site]
|
80
119
|
end
|
81
120
|
|
82
121
|
def self.find_search_engine_portlet(options)
|
83
122
|
portlet = GoogleMiniSearchEnginePortlet.new
|
84
123
|
if options[:portlet]
|
85
|
-
portlet = options
|
124
|
+
portlet = options.delete(:portlet)
|
86
125
|
end
|
87
126
|
portlet
|
88
127
|
end
|
89
128
|
|
129
|
+
# Given a URL, GET it and return the contents
|
130
|
+
# @param [String] url A URL formatted string
|
131
|
+
def self.fetch_document(url)
|
132
|
+
Rails.logger.debug {"GSA: Fetching '#{url}'"}
|
133
|
+
Net::HTTP.get(URI.parse(url))
|
134
|
+
end
|
135
|
+
|
90
136
|
# Fetches the xml response from the google mini server.
|
91
137
|
def self.fetch_xml_doc(query, options={})
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
response = Net::HTTP.get(URI.parse(url))
|
96
|
-
xml_doc = REXML::Document.new(response)
|
97
|
-
return xml_doc
|
138
|
+
url = create_url_for_query(options, query)
|
139
|
+
response = fetch_document(url)
|
140
|
+
REXML::Document.new(response)
|
98
141
|
end
|
99
142
|
|
100
143
|
def self.parse_key_matches(xml_doc)
|
@@ -125,6 +168,10 @@ class SearchResult
|
|
125
168
|
attr_accessor :results_count, :num_pages, :current_page, :start, :query, :pages, :key_matches, :synonyms
|
126
169
|
attr_writer :path
|
127
170
|
|
171
|
+
# For what these codes mean, see http://code.google.com/apis/searchappliance/documentation/46/xml_reference.html#request_sort
|
172
|
+
SORT_BY_DATE_PARAM = "date:D:S:d1"
|
173
|
+
SORT_BY_RELEVANCE_PARAM = "date:D:L:d1"
|
174
|
+
|
128
175
|
def initialize(array=[])
|
129
176
|
# Need to set defaults so an empty result set works.
|
130
177
|
self.start = 0
|
@@ -149,11 +196,15 @@ class SearchResult
|
|
149
196
|
previous_start >= 0 && num_pages > 1
|
150
197
|
end
|
151
198
|
|
199
|
+
# Returns a range of pages that should appear in the pager control. This is design to mimic GSA's pager control,
|
200
|
+
# which will show up to 20 pages at a time, based on the 'range' of pages around the current page.
|
201
|
+
#
|
202
|
+
# i.e. on page 12: < 2 3 4 5 6 7 8 9 10 11 _12_ 13 14 15 16 17 18 19 20 21 22 >
|
152
203
|
def pages
|
153
|
-
if num_pages
|
154
|
-
|
155
|
-
|
156
|
-
|
204
|
+
return [] if num_pages <= 1
|
205
|
+
first_page = current_page - 10 > 1 ? current_page - 10 : 1
|
206
|
+
last_page = current_page + 9 > num_pages ? num_pages : current_page + 9
|
207
|
+
(first_page..last_page)
|
157
208
|
end
|
158
209
|
|
159
210
|
def next_start
|
@@ -173,6 +224,28 @@ class SearchResult
|
|
173
224
|
1
|
174
225
|
end
|
175
226
|
|
227
|
+
# Determines the current Query is sorting by date.
|
228
|
+
#
|
229
|
+
# @param [Hash] params The query parameter from the search page. (same as Rails params)
|
230
|
+
def sorting_by_date?(params)
|
231
|
+
params[:sort] == SearchResult::QueryResult::SORT_BY_DATE_PARAM
|
232
|
+
end
|
233
|
+
|
234
|
+
def path_for(new_query)
|
235
|
+
"#{path}?query=#{new_query}"
|
236
|
+
end
|
237
|
+
# Return the path to sort the current search results by date.
|
238
|
+
#
|
239
|
+
# Based on http://code.google.com/apis/searchappliance/documentation/46/xml_reference.html#request_sort
|
240
|
+
def sort_by_date_path
|
241
|
+
"#{path}?query=#{query}&sort=#{SORT_BY_DATE_PARAM}"
|
242
|
+
end
|
243
|
+
|
244
|
+
# Returns the path to sort the current results by relevance (inverse of sort_by_date_path).
|
245
|
+
def sort_by_relevance_path
|
246
|
+
"#{path}?query=#{query}&sort=#{SORT_BY_RELEVANCE_PARAM}"
|
247
|
+
end
|
248
|
+
|
176
249
|
def next_page_path
|
177
250
|
"#{path}?query=#{query}&start=#{next_start}"
|
178
251
|
end
|
@@ -1,12 +1,28 @@
|
|
1
1
|
class GoogleMiniSearchEnginePortlet < Portlet
|
2
2
|
|
3
3
|
enable_template_editor true
|
4
|
-
|
4
|
+
|
5
5
|
def render
|
6
|
-
@query = params[:query]
|
7
6
|
@site = params[:site]
|
8
7
|
@start = params[:start] ? params[:start].to_i : 0
|
9
|
-
|
8
|
+
options = {:start => @start, :portlet => self, :site=>@site, :sort=>params[:sort]}
|
9
|
+
query_string = params[:query]
|
10
|
+
|
11
|
+
@results = SearchResult.find(query_string, options.clone) # Need to clone, so that :portlet isn't removed for the 2nd call.
|
12
|
+
|
13
|
+
# This is temporary, while the API is being reworked. Ideally, the search results would contain a reference
|
14
|
+
# to the query, so that two X calls isn't needed.
|
15
|
+
@query = SearchResult.create_query(query_string, options.clone)
|
16
|
+
|
17
|
+
if narrow_your_search?
|
18
|
+
@appliance = SearchResult.new_gsa(self)
|
19
|
+
@suggested_queries = @appliance.find_narrow_search_results(query_string)
|
20
|
+
end
|
10
21
|
end
|
11
|
-
|
22
|
+
|
23
|
+
# Handles the fact that all portlet attributes, including checkboxes like enable_your_search are stored as strings.
|
24
|
+
def narrow_your_search?
|
25
|
+
self.enable_narrow_your_search == "1"
|
26
|
+
end
|
27
|
+
|
12
28
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
5
|
+
<title><%= page_title %></title>
|
6
|
+
<%= yield :html_head %>
|
7
|
+
</head>
|
8
|
+
<body style="margin: 0; padding: 0; text-align: center;">
|
9
|
+
<%= cms_toolbar %>
|
10
|
+
<div id="wrapper" style="width: 700px; margin: 0 auto; text-align: left; padding: 30px">
|
11
|
+
Breadcrumbs: <%= render_breadcrumbs %>
|
12
|
+
Main Menu: <%= render_menu %>
|
13
|
+
<h1><%= page_title %></h1>
|
14
|
+
<%= container :main %>
|
15
|
+
</div>
|
16
|
+
</body>
|
17
|
+
</html>
|
@@ -3,4 +3,6 @@
|
|
3
3
|
<%= f.cms_text_field :service_url, :default_value=> "http://", :instructions => "Full Domain name where the Google Mini server is hosted, i.e. 'http://mini.somedomain.com'." %>
|
4
4
|
<%= f.cms_text_field :collection_name, :instructions => "Copy the name of the Collection from Google Mini here. (Can pass 'site' param to override as well).", :default_value=>"default_collection" %>
|
5
5
|
<%= f.cms_text_field :front_end_name, :instructions => "Copy the name of the Collection front end from Google Mini here.", :default_value=>"default_frontend" %>
|
6
|
+
<%= f.cms_check_box :enable_narrow_your_search, :instructions => "The 'Narrow Your Search' feature is a only available for Google Search Appliance, not with Google Mini.",
|
7
|
+
:label=>"Enable Narrowing?" %>
|
6
8
|
<%= f.cms_template_editor :template %>
|
@@ -1,17 +1,22 @@
|
|
1
|
+
|
1
2
|
<h2>Search Results</h2>
|
2
|
-
For '<%= @results.query %>', found <%= @results.results_count %> results. <br
|
3
|
-
|
4
|
-
|
3
|
+
For '<%= @results.query %>', found <%= @results.results_count %> results. <br/>
|
4
|
+
<%= link_to_unless @results.sorting_by_date?(params), "Sort by Date", @results.sort_by_date_path %> /
|
5
|
+
<%= link_to_if @results.sorting_by_date?(params), "Sort by Relevance", @results.sort_by_relevance_path %> <br/>
|
6
|
+
<% if @results.previous_page? %><%= link_to h("< Back"), @results.previous_page_path %>
|
7
|
+
<% end %>
|
8
|
+
<% if @results.next_page? %><%= link_to h("Next >"), @results.next_page_path %>
|
9
|
+
<% end %>
|
5
10
|
<% if @results.key_matches? %>
|
6
11
|
<ul class="key_matches">
|
7
|
-
|
8
|
-
|
9
|
-
|
12
|
+
<% @results.key_matches.each do |match| %>
|
13
|
+
<li><%= link_to match.title, match.url %></li>
|
14
|
+
<% end %>
|
10
15
|
</ul>
|
11
16
|
<% end %>
|
12
17
|
<% if @results.synonyms? %>
|
13
18
|
<span class="synonyms">You could also try:
|
14
|
-
|
19
|
+
<% @results.synonyms.each do |synonym| %>
|
15
20
|
<%= link_to synonym.label, synonym.url %>
|
16
21
|
<% end %>
|
17
22
|
</span>
|
@@ -20,14 +25,30 @@ For '<%= @results.query %>', found <%= @results.results_count %> results. <br />
|
|
20
25
|
<% @results.each do |result|%>
|
21
26
|
<li>
|
22
27
|
<%= result.number %> <%= link_to result.title, result.url, :class=>"search_result_title" %><br />
|
23
|
-
<span class="search_result_description"><%= result.description.html_safe %></span> <br />
|
28
|
+
<span class="search_result_description"><%= result.description.try(:html_safe) %></span> <br />
|
24
29
|
<span class="search_result_url"><%= result.url %></span> -
|
25
30
|
<span class="search_result_size"><%= result.size %></span>
|
26
|
-
|
31
|
+
<span class="search_result_cached"><%= link_to "Cached", result.cached_document_url(@query) %></span>
|
32
|
+
|
33
|
+
</li>
|
27
34
|
<% end %>
|
28
35
|
</ul>
|
29
|
-
|
30
|
-
<%
|
31
|
-
|
36
|
+
|
37
|
+
<% if @portlet.narrow_your_search? %>
|
38
|
+
<div id="clustering">
|
39
|
+
<h3>Narrow your search</h3>
|
40
|
+
<ul>
|
41
|
+
<% @suggested_queries.each_with_index do |suggestion, i| %>
|
42
|
+
<li id="cluster_label<%= i %>"><%= link_to suggestion.query, @results.path_for(suggestion.query) %></li>
|
43
|
+
<% end %>
|
44
|
+
</ul>
|
45
|
+
</div>
|
46
|
+
<% end %>
|
47
|
+
|
48
|
+
<% if @results.previous_page? %><%= link_to h("< Back"), @results.previous_page_path %>
|
49
|
+
<% end %>
|
50
|
+
<% @results.pages.each do |p| %>
|
51
|
+
<%= link_to_unless @results.current_page?(p), p, @results.page_path(p) %>
|
52
|
+
<% end %>
|
53
|
+
<% if @results.next_page? %><%= link_to h("Next >"), @results.next_page_path %>
|
32
54
|
<% end %>
|
33
|
-
<% if @results.next_page? %><%= link_to h("Next >"), @results.next_page_path %><% end %>
|
@@ -8,13 +8,12 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.authors = ["BrowserMedia"]
|
9
9
|
s.email = %q{github@browsermedia.com}
|
10
10
|
s.homepage = %q{http://github.com/browsermedia/bcms_google_mini_search}
|
11
|
-
s.description = %q{A Google Appliance
|
11
|
+
s.description = %q{A Google Appliance module for BrowserCMS. Used to display search results from a Google Mini/Search Appliance on a site.}
|
12
12
|
s.summary = %q{A Google Mini Search Module for BrowserCMS}
|
13
13
|
s.extra_rdoc_files = [
|
14
14
|
"README.markdown"
|
15
15
|
]
|
16
16
|
s.rdoc_options = ["--charset=UTF-8"]
|
17
|
-
|
18
17
|
s.rubyforge_project = "bcms_google_mini_search"
|
19
18
|
|
20
19
|
s.files = `git ls-files`.split("\n")
|
@@ -28,7 +27,6 @@ Gem::Specification.new do |s|
|
|
28
27
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
29
28
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
30
29
|
s.require_paths = ["lib"]
|
31
|
-
|
32
30
|
s.add_dependency(%q<browsercms>, ["~> 3.3.0"])
|
33
31
|
end
|
34
32
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<toplevel>
|
3
|
+
<Response>
|
4
|
+
<algorithm data="Concepts"/>
|
5
|
+
<t_cluster int="95"/>
|
6
|
+
<cluster>
|
7
|
+
<gcluster>
|
8
|
+
<label data="nonalcoholic fatty liver disease"/>
|
9
|
+
</gcluster>
|
10
|
+
<gcluster>
|
11
|
+
<label data="advanced liver disease"/>
|
12
|
+
</gcluster>
|
13
|
+
<gcluster>
|
14
|
+
<label data="liver disease patients"/>
|
15
|
+
</gcluster>
|
16
|
+
<gcluster>
|
17
|
+
<label data="chronic liver disease"/>
|
18
|
+
</gcluster>
|
19
|
+
<gcluster>
|
20
|
+
<label data="patients nonalcoholic fatty liver"/>
|
21
|
+
</gcluster>
|
22
|
+
<gcluster>
|
23
|
+
<label data="end stage liver disease"/>
|
24
|
+
</gcluster>
|
25
|
+
<gcluster>
|
26
|
+
<label data="cholestatic liver disease"/>
|
27
|
+
</gcluster>
|
28
|
+
<gcluster>
|
29
|
+
<label data="patients advanced liver disease"/>
|
30
|
+
</gcluster>
|
31
|
+
<gcluster>
|
32
|
+
<label data="safe patients advanced liver"/>
|
33
|
+
</gcluster>
|
34
|
+
<gcluster>
|
35
|
+
<label data="model end stage liver"/>
|
36
|
+
</gcluster>
|
37
|
+
</cluster>
|
38
|
+
</Response>
|
39
|
+
</toplevel>
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# A general purpose API for querying a Google Search Appliance for results.
|
2
|
+
module GSA
|
3
|
+
|
4
|
+
# Represents a single instance of a Google Mini
|
5
|
+
class Engine
|
6
|
+
attr_accessor :host, :port, :path, :default_collection, :default_front_end
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
self.port = 8080
|
10
|
+
self.path = "/search"
|
11
|
+
self.host = options[:host]
|
12
|
+
self.default_front_end = options[:front_end]
|
13
|
+
self.default_collection = options[:collection]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Return a Hash suitable to be passed to SearchResult.find()
|
17
|
+
def options_for_query
|
18
|
+
{:host=>host, :front_end=>default_front_end, :collection=>default_collection}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# GSA support slightly different features than Google Mini.
|
23
|
+
class Appliance < Engine
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
# Fetch a set of Suggested queries, based on a given query.
|
28
|
+
#
|
29
|
+
# See http://code.google.com/apis/searchappliance/documentation/50/admin_searchexp/ce_understanding.html#h3drc for the spec.
|
30
|
+
# See http://groups.google.com/group/Google-Search-Appliance-Help/browse_thread/thread/8a821fc8475a5e24/34a5c3c8ab74ed35?hl=en&lnk=gst#34a5c3c8ab74ed35
|
31
|
+
# for details about how this is implemented.
|
32
|
+
#
|
33
|
+
# Clustering (aka Narrow your search) is only supported by GSA.
|
34
|
+
# @param [String] query A term to fetch 'suggested' queries for/
|
35
|
+
# @return [GSA::SuggestedQueries] A set of suggested queries
|
36
|
+
def find_narrow_search_results(query)
|
37
|
+
return [] unless query
|
38
|
+
|
39
|
+
url = narrow_search_results_url(query)
|
40
|
+
document = SearchResult.fetch_document(url)
|
41
|
+
SuggestedQueries.new(document)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Returns the URL to GET a set of Dynamic Search Clusters for a particular query.
|
47
|
+
def narrow_search_results_url(query)
|
48
|
+
"#{host}/cluster?coutput=xml&q=#{CGI::escape(query)}&site=#{default_collection}&client=#{default_front_end}&output=xml_no_dtd&oe=UTF-8&ie=UTF-8"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Represents a set of suggested search terms, based on results from a GSA.
|
53
|
+
# AKA DynamicResultClusters
|
54
|
+
class SuggestedQueries
|
55
|
+
def initialize(xml_as_string)
|
56
|
+
@clusters = []
|
57
|
+
doc = REXML::Document.new(xml_as_string)
|
58
|
+
doc.elements.each('toplevel/Response/cluster/gcluster') do |ele|
|
59
|
+
@clusters << Suggestion.new(ele.elements["label"].attributes["data"])
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
delegate :each, :each_with_index, :size, '[]', :to=>:clusters
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def clusters
|
69
|
+
@clusters
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# Since generating are handled in the view, this might no longer be necessary a separate class, and could probably be converted into a String.
|
74
|
+
class Suggestion
|
75
|
+
attr_accessor :query
|
76
|
+
|
77
|
+
def initialize(query)
|
78
|
+
self.query = query
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
class Query
|
88
|
+
attr_reader :engine, :query, :front_end, :collection
|
89
|
+
|
90
|
+
def initialize(options={})
|
91
|
+
@engine = options[:engine]
|
92
|
+
@query = options[:query]
|
93
|
+
@front_end = options[:front_end]
|
94
|
+
@collection = options[:collection]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Represent a collection of results from a GSA search.
|
99
|
+
class Results
|
100
|
+
|
101
|
+
attr_accessor :query
|
102
|
+
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
# Represents a single result (aka Hit) from a GSA query.
|
107
|
+
# Defined by http://code.google.com/apis/searchappliance/documentation/46/xml_reference.html#results_xml_tag_r
|
108
|
+
class Result
|
109
|
+
attr_accessor :number, :title, :url, :description, :size, :cache_id, :results
|
110
|
+
|
111
|
+
# @param [RXEML::Element] xml_element The <R> result from GSA a search.
|
112
|
+
def initialize(xml_element = nil)
|
113
|
+
return if xml_element == nil
|
114
|
+
self.number = xml_element.attributes["N"]
|
115
|
+
self.title = xml_element.elements["T"].try(:text)
|
116
|
+
self.url = xml_element.elements["U"].try(:text)
|
117
|
+
self.description = xml_element.elements["S"].try(:text)
|
118
|
+
|
119
|
+
cache_element = xml_element.elements["HAS/C"]
|
120
|
+
|
121
|
+
if cache_element
|
122
|
+
self.size = cache_element.attributes["SZ"]
|
123
|
+
self.cache_id = cache_element.attributes["CID"]
|
124
|
+
else
|
125
|
+
self.size = ""
|
126
|
+
self.cache_id=""
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns the value for q if a user wants to request the cached version of this document.
|
132
|
+
def cached_document_param
|
133
|
+
param = "cache:#{cache_id}:#{url}"
|
134
|
+
if results
|
135
|
+
param += "+#{results.query}"
|
136
|
+
end
|
137
|
+
param
|
138
|
+
end
|
139
|
+
|
140
|
+
def cached_document_url(gsa_query)
|
141
|
+
SearchResult.query_url(cached_document_param, {:host=>gsa_query.engine.host, :collection=>gsa_query.collection, :front_end=>gsa_query.front_end, :as_xml=>false})
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
data/release_notes.txt
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
v1.3.1
|
2
|
+
======
|
3
|
+
|
4
|
+
* Merge changes from 1.2.2 into 1.3.x line
|
5
|
+
* Ensure documents with empty titles work correctly
|
6
|
+
* Endure Narowing/Caching/Paging/etc are available for rails 3
|
7
|
+
|
8
|
+
v1.3.0
|
9
|
+
======
|
10
|
+
|
11
|
+
* Upgrade GSA module to be Rails 3/BrowserCMS 3.3.x compatible (Did not include some v1.2.x changes)
|
12
|
+
|
13
|
+
v1.2.0
|
14
|
+
======
|
15
|
+
|
16
|
+
This release adds several refinements to the gmini/gsa module, including:
|
17
|
+
|
18
|
+
* Better Paging - The results pages control now behaves like the test center does, where it shows a maximum of 20 page links, based on the 'current' page the user is on.
|
19
|
+
* Sort by Date/Relevance - Users can now toggle the results display between date and relevance.
|
20
|
+
* Caching - Each result now correctly generate a link to the cached version of the document as stored by the GSA.
|
21
|
+
* Narrow Your Search - Shows users a set of other suggested queries to allow them to 'Narrow Your Search', based on the current query and what results are stored in GSA. Only available for GSA instances, as Google Mini does not support Dynamic Result Clustering.
|
22
|
+
* [Bug Fix] Queries and results are now interpreted as UTF-8. Should avoid weird encoding issues with results.
|
23
|
+
|
24
|
+
How Google Results Pager works
|
25
|
+
====================
|
26
|
+
* Start with 1 - 9.
|
27
|
+
* Add current page plus 9,
|
28
|
+
* Show a maximum of 20 results, plus Previous and Next
|
29
|
+
* Range is 10 previous, current, next 9
|
30
|
+
CP = 11, range = 1 - 20
|
31
|
+
CP = 1, range = 1-10
|
32
|
+
CP = 12, range = 2 - 21
|
33
|
+
|
34
|
+
v1.1
|
35
|
+
====
|
36
|
+
|
37
|
+
Adds the following features:
|
38
|
+
|
39
|
+
* Displays Key Matches for queries
|
40
|
+
* Display Synonyms for queries
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class GSA::ApplianceTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
test "Create Engine" do
|
6
|
+
app = GSA::Engine.new
|
7
|
+
app.host = "http://example.com"
|
8
|
+
assert_equal "http://example.com", app.host
|
9
|
+
assert_equal 8080, app.port
|
10
|
+
assert_equal "/search", app.path
|
11
|
+
end
|
12
|
+
|
13
|
+
test "Create from options" do
|
14
|
+
app = GSA::Engine.new({:host=>"http://example.com", :front_end=>"F", :collection=>"C"})
|
15
|
+
assert_equal "http://example.com", app.host
|
16
|
+
assert_equal "F", app.default_front_end
|
17
|
+
assert_equal "C", app.default_collection
|
18
|
+
end
|
19
|
+
|
20
|
+
test "options_for_query" do
|
21
|
+
options = {:host=>"http://example.com", :front_end=>"F", :collection=>"C"}
|
22
|
+
app = GSA::Engine.new(options)
|
23
|
+
assert_equal options, app.options_for_query
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class SuggestedQueries < ActiveSupport::TestCase
|
28
|
+
|
29
|
+
def setup
|
30
|
+
@cluster_xml = <<XML
|
31
|
+
<toplevel>
|
32
|
+
<Response>
|
33
|
+
<algorithm data="Concepts"/>
|
34
|
+
<t_cluster int="95"/>
|
35
|
+
<cluster>
|
36
|
+
<gcluster>
|
37
|
+
<label data="label 0"/>
|
38
|
+
</gcluster>
|
39
|
+
<gcluster>
|
40
|
+
<label data="label 1"/>
|
41
|
+
</gcluster>
|
42
|
+
<gcluster>
|
43
|
+
<label data="label 2"/>
|
44
|
+
</gcluster>
|
45
|
+
<gcluster>
|
46
|
+
<label data="label 3"/>
|
47
|
+
</gcluster>
|
48
|
+
<gcluster>
|
49
|
+
<label data="label 4"/>
|
50
|
+
</gcluster>
|
51
|
+
<gcluster>
|
52
|
+
<label data="label 5"/>
|
53
|
+
</gcluster>
|
54
|
+
<gcluster>
|
55
|
+
<label data="label 6"/>
|
56
|
+
</gcluster>
|
57
|
+
<gcluster>
|
58
|
+
<label data="label 7"/>
|
59
|
+
</gcluster>
|
60
|
+
<gcluster>
|
61
|
+
<label data="label 8"/>
|
62
|
+
</gcluster>
|
63
|
+
<gcluster>
|
64
|
+
<label data="label 9"/>
|
65
|
+
</gcluster>
|
66
|
+
</cluster>
|
67
|
+
</Response>
|
68
|
+
</toplevel>
|
69
|
+
XML
|
70
|
+
|
71
|
+
@app = GSA::Appliance.new(:host=>"http://example.com", :collection=>"My_Collection", :front_end=>"My_Front")
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
test "parse results" do
|
76
|
+
suggested_queries = GSA::SuggestedQueries.new(@cluster_xml)
|
77
|
+
assert_equal 10, suggested_queries.size
|
78
|
+
assert_equal "label 0", suggested_queries[0].query
|
79
|
+
assert_equal "label 9", suggested_queries[9].query
|
80
|
+
end
|
81
|
+
|
82
|
+
test "Google Search Appliances should generate the URL for Dynamic Results Clustering" do
|
83
|
+
expected = "http://example.com/cluster?coutput=xml&q=TEST&site=My_Collection&client=My_Front&output=xml_no_dtd&oe=UTF-8&ie=UTF-8"
|
84
|
+
assert_equal expected, @app.send(:narrow_search_results_url, "TEST")
|
85
|
+
end
|
86
|
+
|
87
|
+
test "URLs will escape queries" do
|
88
|
+
expected = "http://example.com/cluster?coutput=xml&q=TWO+WORDS&site=My_Collection&client=My_Front&output=xml_no_dtd&oe=UTF-8&ie=UTF-8"
|
89
|
+
assert_equal expected, @app.send(:narrow_search_results_url, "TWO WORDS")
|
90
|
+
end
|
91
|
+
|
92
|
+
test "find narrowed search results" do
|
93
|
+
@app.expects(:narrow_search_results_url).with("TEST").returns("EXPECTED URL")
|
94
|
+
SearchResult.expects(:fetch_document).with("EXPECTED URL").returns("XML Content")
|
95
|
+
expected_suggestions = mock()
|
96
|
+
GSA::SuggestedQueries.expects(:new).with("XML Content").returns(expected_suggestions)
|
97
|
+
|
98
|
+
assert_equal expected_suggestions, @app.find_narrow_search_results("TEST")
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
test "each_with_index" do
|
103
|
+
suggestions = GSA::SuggestedQueries.new(@cluster_xml)
|
104
|
+
count = 0
|
105
|
+
suggestions.each_with_index do |s, i|
|
106
|
+
assert_not_nil s
|
107
|
+
assert_not_nil i
|
108
|
+
count += 1
|
109
|
+
end
|
110
|
+
assert_equal suggestions.size, count
|
111
|
+
end
|
112
|
+
|
113
|
+
test "each" do
|
114
|
+
suggestions = GSA::SuggestedQueries.new(@cluster_xml)
|
115
|
+
count = 0
|
116
|
+
suggestions.each do |s|
|
117
|
+
assert_not_nil s
|
118
|
+
count += 1
|
119
|
+
end
|
120
|
+
assert_equal suggestions.size, count
|
121
|
+
end
|
122
|
+
|
123
|
+
test "A nil query should return an empty set of Suggested Queries" do
|
124
|
+
r = @app.find_narrow_search_results(nil)
|
125
|
+
assert_equal 0, r.size
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class ResultsTest < ActiveSupport::TestCase
|
130
|
+
|
131
|
+
def setup
|
132
|
+
@results = GSA::Results.new
|
133
|
+
@results.query = "QUERY"
|
134
|
+
@result = GSA::Result.new
|
135
|
+
@result.results = @results
|
136
|
+
|
137
|
+
@engine = GSA::Engine.new(:host=>"http://mini.someurl.com")
|
138
|
+
@query = GSA::Query.new(:engine=>@engine, :collection=>"COLLECT", :front_end=>"FRONT_END")
|
139
|
+
end
|
140
|
+
|
141
|
+
test "Handles missing elements that we kinda expect to be there" do
|
142
|
+
xml = <<XML
|
143
|
+
<R N="1">
|
144
|
+
<HAS>
|
145
|
+
<C SZ="1k" CID="Ax1j5"/>
|
146
|
+
</HAS>
|
147
|
+
</R>
|
148
|
+
XML
|
149
|
+
xml_doc = REXML::Document.new(xml)
|
150
|
+
result = GSA::Result.new(xml_doc.elements.first)
|
151
|
+
assert_nil result.url
|
152
|
+
assert_nil result.title
|
153
|
+
assert_nil result.description
|
154
|
+
end
|
155
|
+
|
156
|
+
test "Create result from xml" do
|
157
|
+
xml = <<XML
|
158
|
+
<R N="1">
|
159
|
+
<U>http://someurl.com</U>
|
160
|
+
<T>TITLE</T>
|
161
|
+
<S>BLURB</S>
|
162
|
+
<HAS>
|
163
|
+
<C SZ="1k" CID="Ax1j5"/>
|
164
|
+
</HAS>
|
165
|
+
</R>
|
166
|
+
XML
|
167
|
+
xml_doc = REXML::Document.new(xml)
|
168
|
+
result = GSA::Result.new(xml_doc.elements.first)
|
169
|
+
assert_equal "http://someurl.com", result.url
|
170
|
+
assert_equal "TITLE", result.title
|
171
|
+
assert_equal "BLURB", result.description
|
172
|
+
assert_equal "1k", result.size
|
173
|
+
assert_equal "1", result.number
|
174
|
+
assert_equal "Ax1j5", result.cache_id
|
175
|
+
end
|
176
|
+
|
177
|
+
test "cached_document_param" do
|
178
|
+
@result.cache_id = "A2B"
|
179
|
+
@result.url = "http://example.com"
|
180
|
+
|
181
|
+
assert_equal "cache:A2B:http://example.com+QUERY", @result.cached_document_param
|
182
|
+
end
|
183
|
+
|
184
|
+
test "cached_document_param with no result attached" do
|
185
|
+
@result.results = nil
|
186
|
+
@result.cache_id = "A2B"
|
187
|
+
@result.url = "http://example.com"
|
188
|
+
|
189
|
+
assert_equal "cache:A2B:http://example.com", @result.cached_document_param
|
190
|
+
end
|
191
|
+
|
192
|
+
test "cached_document_url" do
|
193
|
+
@result.expects(:cached_document_param).returns("cache:something")
|
194
|
+
expected_url = "http://mini.someurl.com/search?q=cache%3Asomething&output=xml_no_dtd&client=FRONT_END&site=COLLECT&filter=0&proxystylesheet=FRONT_END&oe=UTF-8&ie=UTF-8"
|
195
|
+
assert_equal expected_url, @result.cached_document_url(@query)
|
196
|
+
end
|
197
|
+
end
|
@@ -2,14 +2,52 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
class GoogleMiniSearchEngineTest < ActiveSupport::TestCase
|
4
4
|
|
5
|
+
def setup
|
6
|
+
@portlet = GoogleMiniSearchEnginePortlet.new(:name=>"Engine", :path => "/engine")
|
7
|
+
end
|
8
|
+
|
5
9
|
test "Should be able to create new instance of a portlet" do
|
6
10
|
assert GoogleMiniSearchEnginePortlet.create!(:name => "New Portlet")
|
7
11
|
end
|
8
12
|
|
9
|
-
|
10
|
-
|
11
13
|
test "Path attribute can be set in constructor" do
|
12
14
|
portlet = GoogleMiniSearchEnginePortlet.create!(:name=>"Engine", :path => "/engine")
|
13
15
|
assert_equal "/engine", portlet.path
|
14
16
|
end
|
17
|
+
|
18
|
+
test "Determine if Narrow Your Search is enabled?" do
|
19
|
+
@portlet.enable_narrow_your_search = "1"
|
20
|
+
assert_equal true, @portlet.narrow_your_search?
|
21
|
+
|
22
|
+
@portlet.enable_narrow_your_search = "0"
|
23
|
+
assert_equal false, @portlet.narrow_your_search?
|
24
|
+
|
25
|
+
@portlet.enable_narrow_your_search = ""
|
26
|
+
assert_equal false, @portlet.narrow_your_search?
|
27
|
+
|
28
|
+
@portlet.enable_narrow_your_search = nil
|
29
|
+
assert_equal false, @portlet.narrow_your_search?
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
class RenderTest < ActiveSupport::TestCase
|
36
|
+
def setup
|
37
|
+
@portlet = GoogleMiniSearchEnginePortlet.new(:name=>"Engine", :path => "/engine")
|
38
|
+
@params = {:start => 10, :query => "X", :site=>'default_collection', :sort=>"date:D:S:d1"}
|
39
|
+
@portlet.expects('params').returns(@params).at_least_once
|
40
|
+
end
|
41
|
+
|
42
|
+
test "Sort params" do
|
43
|
+
SearchResult.expects(:find).with("X", {:start => 10, :portlet => @portlet, :site=>'default_collection', :sort=>"date:D:S:d1"})
|
44
|
+
@portlet.render
|
45
|
+
end
|
46
|
+
|
47
|
+
test "Find narrow queries only if enabled" do
|
48
|
+
@portlet.enable_narrow_your_search = "1"
|
49
|
+
GSA::Appliance.any_instance.expects(:find_narrow_search_results).with("X")
|
50
|
+
SearchResult.expects(:find).with("X", {:start => 10, :portlet => @portlet, :site=>'default_collection', :sort=>"date:D:S:d1"})
|
51
|
+
@portlet.render
|
52
|
+
end
|
15
53
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
+
|
4
|
+
|
3
5
|
class SearchResultTest < ActiveSupport::TestCase
|
4
6
|
|
5
7
|
def setup
|
@@ -56,8 +58,9 @@ EOF
|
|
56
58
|
end
|
57
59
|
|
58
60
|
test "fetch_xml_doc should download and parse the xml results from the GSA" do
|
59
|
-
SearchResult.expects(:
|
60
|
-
|
61
|
+
SearchResult.expects(:create_url_for_query).returns("http://example.com")
|
62
|
+
SearchResult.expects(:fetch_document).with("http://example.com").returns(nil)
|
63
|
+
REXML::Document.expects(:new).with(nil).returns("EXPECTED_RESULTS")
|
61
64
|
assert_equal "EXPECTED_RESULTS", SearchResult.fetch_xml_doc("")
|
62
65
|
end
|
63
66
|
|
@@ -211,14 +214,7 @@ EOF
|
|
211
214
|
assert_equal [], results.pages
|
212
215
|
end
|
213
216
|
|
214
|
-
|
215
|
-
c = 0
|
216
|
-
(1..4).each_with_index do |i, count|
|
217
|
-
assert_equal count + 1, i
|
218
|
-
c = count
|
219
|
-
end
|
220
|
-
assert_equal 3, c
|
221
|
-
end
|
217
|
+
|
222
218
|
|
223
219
|
test "current_page should check to see if the current page matches" do
|
224
220
|
results = SearchResult::QueryResult.new
|
@@ -231,45 +227,6 @@ EOF
|
|
231
227
|
|
232
228
|
end
|
233
229
|
|
234
|
-
test "Path to next page" do
|
235
|
-
results = SearchResult::QueryResult.new
|
236
|
-
results.start = 0
|
237
|
-
results.path = "/search/search-results"
|
238
|
-
results.query = "X"
|
239
|
-
|
240
|
-
assert_equal "/search/search-results?query=X&start=10", results.next_page_path
|
241
|
-
end
|
242
|
-
|
243
|
-
test "Path to previous page" do
|
244
|
-
results = SearchResult::QueryResult.new
|
245
|
-
results.start = 20
|
246
|
-
results.path = "/search/search-results"
|
247
|
-
results.query = "X"
|
248
|
-
|
249
|
-
assert_equal "/search/search-results?query=X&start=10", results.previous_page_path
|
250
|
-
end
|
251
|
-
|
252
|
-
test "Sets path to default search-results" do
|
253
|
-
results = SearchResult::QueryResult.new
|
254
|
-
assert_equal "/search/search-results", results.path
|
255
|
-
end
|
256
|
-
|
257
|
-
test "Setting path overrides the defaults" do
|
258
|
-
results = SearchResult::QueryResult.new
|
259
|
-
results.path = "/other"
|
260
|
-
assert_equal "/other", results.path
|
261
|
-
end
|
262
|
-
|
263
|
-
test "page_path" do
|
264
|
-
results = SearchResult::QueryResult.new
|
265
|
-
results.query = "X"
|
266
|
-
|
267
|
-
assert_equal "/search/search-results?query=X&start=0", results.page_path(1)
|
268
|
-
assert_equal "/search/search-results?query=X&start=10", results.page_path(2)
|
269
|
-
assert_equal "/search/search-results?query=X&start=20", results.page_path(3)
|
270
|
-
assert_equal "/search/search-results?query=X&start=30", results.page_path(4)
|
271
|
-
|
272
|
-
end
|
273
230
|
|
274
231
|
test "Portlet attributes are used to look up path" do
|
275
232
|
portlet = GoogleMiniSearchEnginePortlet.new(:name=>"Engine", :path => "/engine")
|
@@ -291,12 +248,48 @@ EOF
|
|
291
248
|
:name=>"Engine", :path => "/engine", :service_url => "http://mini.someurl.com",
|
292
249
|
:collection_name => "COLLECT", :front_end_name => "FRONT_END")
|
293
250
|
|
294
|
-
url = SearchResult.
|
295
|
-
assert_equal "http://mini.someurl.com/search?q=STUFF&output=xml_no_dtd&client=FRONT_END&site=COLLECT&filter=0", url
|
251
|
+
url = SearchResult.create_url_for_query({:portlet => portlet}, "STUFF")
|
252
|
+
assert_equal "http://mini.someurl.com/search?q=STUFF&output=xml_no_dtd&client=FRONT_END&site=COLLECT&filter=0&oe=UTF-8&ie=UTF-8", url
|
296
253
|
|
297
|
-
url = SearchResult.
|
298
|
-
assert_equal "http://mini.someurl.com/search?q=STUFF&output=xml_no_dtd&client=FRONT_END&site=COLLECT&filter=0&start=100", url
|
254
|
+
url = SearchResult.create_url_for_query({:portlet => portlet, :start=>100}, "STUFF")
|
255
|
+
assert_equal "http://mini.someurl.com/search?q=STUFF&output=xml_no_dtd&client=FRONT_END&site=COLLECT&filter=0&start=100&oe=UTF-8&ie=UTF-8", url
|
256
|
+
end
|
257
|
+
|
258
|
+
|
259
|
+
test "Create Engine and Query from portlet attributes" do
|
260
|
+
portlet = GoogleMiniSearchEnginePortlet.new(
|
261
|
+
:name=>"Engine", :path => "/engine", :service_url => "http://mini.someurl.com",
|
262
|
+
:collection_name => "COLLECT", :front_end_name => "FRONT_END")
|
299
263
|
|
264
|
+
query = SearchResult.create_query("therapy", {:portlet=>portlet})
|
265
|
+
assert_equal "http://mini.someurl.com", query.engine.host
|
266
|
+
assert_equal portlet.front_end_name, query.front_end
|
267
|
+
assert_equal portlet.collection_name, query.collection
|
268
|
+
assert_equal "therapy", query.query
|
269
|
+
end
|
270
|
+
|
271
|
+
test "should look up options from portlet and add to hash" do
|
272
|
+
portlet = GoogleMiniSearchEnginePortlet.new(
|
273
|
+
:name=>"Engine", :path => "/engine", :service_url => "http://mini.someurl.com",
|
274
|
+
:collection_name => "COLLECT", :front_end_name => "FRONT_END")
|
275
|
+
options = {:portlet=>portlet}
|
276
|
+
SearchResult.normalize_query_options(options)
|
277
|
+
|
278
|
+
assert_equal "FRONT_END", options[:front_end]
|
279
|
+
assert_equal "COLLECT", options[:collection]
|
280
|
+
assert_equal "http://mini.someurl.com", options[:host]
|
281
|
+
assert_equal nil, options[:portlet]
|
282
|
+
end
|
283
|
+
|
284
|
+
test "Create an appliance from attributes in the portlet." do
|
285
|
+
portlet = GoogleMiniSearchEnginePortlet.new(
|
286
|
+
:name=>"Engine", :path => "/engine", :service_url => "http://mini.someurl.com",
|
287
|
+
:collection_name => "COLLECT", :front_end_name => "FRONT_END")
|
288
|
+
|
289
|
+
gsa = SearchResult.new_gsa(portlet)
|
290
|
+
assert_equal "http://mini.someurl.com", gsa.host
|
291
|
+
assert_equal "FRONT_END", gsa.default_front_end
|
292
|
+
assert_equal "COLLECT", gsa.default_collection
|
300
293
|
end
|
301
294
|
|
302
295
|
test "Explicitly passing a collection in will query with that rather than a default collection" do
|
@@ -304,13 +297,23 @@ EOF
|
|
304
297
|
:name=>"Engine", :path => "/engine", :service_url => "http://mini.someurl.com",
|
305
298
|
:collection_name => "COLLECT", :front_end_name => "FRONT_END")
|
306
299
|
|
307
|
-
url = SearchResult.
|
308
|
-
assert_equal "http://mini.someurl.com/search?q=STUFF&output=xml_no_dtd&client=FRONT_END&site=ANOTHER_COLLECTION&filter=0", url
|
300
|
+
url = SearchResult.create_url_for_query({:portlet => portlet, :site=>"ANOTHER_COLLECTION"}, "STUFF")
|
301
|
+
assert_equal "http://mini.someurl.com/search?q=STUFF&output=xml_no_dtd&client=FRONT_END&site=ANOTHER_COLLECTION&filter=0&oe=UTF-8&ie=UTF-8", url
|
309
302
|
end
|
310
303
|
|
311
304
|
test "Handles multiword queries" do
|
312
|
-
url = SearchResult.
|
313
|
-
assert_equal "/search?q=One+Two&output=xml_no_dtd&client=&site=&filter=0", url
|
305
|
+
url = SearchResult.create_url_for_query({}, "One Two")
|
306
|
+
assert_equal "/search?q=One+Two&output=xml_no_dtd&client=&site=&filter=0&oe=UTF-8&ie=UTF-8", url
|
307
|
+
end
|
308
|
+
|
309
|
+
test "sort is added to google mini query" do
|
310
|
+
url = SearchResult.create_url_for_query({:sort=>"XYZ"}, "STUFF")
|
311
|
+
assert_equal "/search?q=STUFF&output=xml_no_dtd&client=&site=&filter=0&sort=XYZ&oe=UTF-8&ie=UTF-8", url
|
312
|
+
end
|
313
|
+
|
314
|
+
test "sort params are escaped" do
|
315
|
+
url = SearchResult.create_url_for_query({:sort=>"date:D:S:d1"}, "STUFF")
|
316
|
+
assert_equal "/search?q=STUFF&output=xml_no_dtd&client=&site=&filter=0&sort=date%3AD%3AS%3Ad1&oe=UTF-8&ie=UTF-8", url
|
314
317
|
end
|
315
318
|
|
316
319
|
test "Handles keymatches in results" do
|
@@ -470,5 +473,123 @@ EOF
|
|
470
473
|
assert_equal "TITLE 2", results[1].title
|
471
474
|
assert_equal "BLURB 2", results[1].description
|
472
475
|
assert_equal "", results[1].size
|
476
|
+
end
|
477
|
+
|
478
|
+
end
|
479
|
+
|
480
|
+
class SearchPathsTest < ActiveSupport::TestCase
|
481
|
+
|
482
|
+
def setup
|
483
|
+
@results = SearchResult::QueryResult.new
|
484
|
+
@results.start = 0
|
485
|
+
@results.path = "/search/search-results"
|
486
|
+
@results.query = "X"
|
487
|
+
end
|
488
|
+
|
489
|
+
test "path_for" do
|
490
|
+
assert_equal "/search/search-results?query=Y", @results.path_for("Y")
|
491
|
+
end
|
492
|
+
|
493
|
+
test "sort by date" do
|
494
|
+
assert_equal "#{@results.path}?query=#{@results.query}&sort=#{SearchResult::QueryResult::SORT_BY_DATE_PARAM}", @results.sort_by_date_path
|
495
|
+
end
|
496
|
+
|
497
|
+
test "sort by relevance" do
|
498
|
+
assert @results.sort_by_relevance_path != @results.sort_by_date_path, "Paths should not be the same."
|
499
|
+
assert_equal "#{@results.path}?query=#{@results.query}&sort=#{SearchResult::QueryResult::SORT_BY_RELEVANCE_PARAM}", @results.sort_by_relevance_path
|
500
|
+
end
|
501
|
+
|
502
|
+
test "Path to next page" do
|
503
|
+
assert_equal "/search/search-results?query=X&start=10", @results.next_page_path
|
504
|
+
end
|
505
|
+
|
506
|
+
test "Path to previous page" do
|
507
|
+
@results.start = 20
|
508
|
+
assert_equal "/search/search-results?query=X&start=10", @results.previous_page_path
|
509
|
+
end
|
510
|
+
|
511
|
+
test "Sets path to default search-results" do
|
512
|
+
assert_equal "/search/search-results", @results.path
|
513
|
+
end
|
514
|
+
|
515
|
+
test "Setting path overrides the defaults" do
|
516
|
+
@results.path = "/other"
|
517
|
+
assert_equal "/other", @results.path
|
518
|
+
end
|
519
|
+
|
520
|
+
test "page_path" do
|
521
|
+
assert_equal "/search/search-results?query=X&start=0", @results.page_path(1)
|
522
|
+
assert_equal "/search/search-results?query=X&start=10", @results.page_path(2)
|
523
|
+
assert_equal "/search/search-results?query=X&start=20", @results.page_path(3)
|
524
|
+
assert_equal "/search/search-results?query=X&start=30", @results.page_path(4)
|
525
|
+
end
|
526
|
+
|
527
|
+
test "sorting_by_date?" do
|
528
|
+
assert_equal true, @results.sorting_by_date?({:sort=>SearchResult::QueryResult::SORT_BY_DATE_PARAM})
|
529
|
+
assert_equal false, @results.sorting_by_date?({:sort=>SearchResult::QueryResult::SORT_BY_RELEVANCE_PARAM})
|
530
|
+
assert_equal false, @results.sorting_by_date?({})
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
|
535
|
+
class PagingTest < ActiveSupport::TestCase
|
536
|
+
|
537
|
+
def setup
|
538
|
+
@results = SearchResult::QueryResult.new
|
539
|
+
and_the_max_number_pages_is 100
|
540
|
+
end
|
541
|
+
|
542
|
+
test "Behavior of Ruby Ranges" do
|
543
|
+
c = 0
|
544
|
+
(1..4).each_with_index do |i, count|
|
545
|
+
assert_equal count + 1, i
|
546
|
+
c = count
|
547
|
+
end
|
548
|
+
assert_equal 3, c
|
473
549
|
end
|
550
|
+
|
551
|
+
|
552
|
+
test "When on page 1, show links for pages 1 - 10" do
|
553
|
+
when_current_page_is(1)
|
554
|
+
assert_equal (1..10), @results.pages
|
555
|
+
end
|
556
|
+
|
557
|
+
test "When on page 11, show links for pages 1-20" do
|
558
|
+
when_current_page_is(11)
|
559
|
+
assert_equal (1..20), @results.pages
|
560
|
+
end
|
561
|
+
|
562
|
+
test "When on page 12, show links for pages 2-22" do
|
563
|
+
when_current_page_is 12
|
564
|
+
assert_equal (2..21), @results.pages
|
565
|
+
end
|
566
|
+
|
567
|
+
test "When less than 10 pages only show up to last page" do
|
568
|
+
when_current_page_is 1
|
569
|
+
and_the_max_number_pages_is 4
|
570
|
+
|
571
|
+
assert_equal (1..4), @results.pages
|
572
|
+
end
|
573
|
+
|
574
|
+
test "When no results, should be empty set of pages." do
|
575
|
+
when_current_page_is 1
|
576
|
+
and_the_max_number_pages_is 0
|
577
|
+
assert_equal [], @results.pages
|
578
|
+
end
|
579
|
+
|
580
|
+
test "With one page, return a single page." do
|
581
|
+
when_current_page_is 1
|
582
|
+
and_the_max_number_pages_is 1
|
583
|
+
assert_equal [], @results.pages, "A single page of results needs no pager control"
|
584
|
+
end
|
585
|
+
|
586
|
+
private
|
587
|
+
|
588
|
+
def and_the_max_number_pages_is(number)
|
589
|
+
@results.expects(:num_pages).returns(number).times(0..5)
|
590
|
+
end
|
591
|
+
|
592
|
+
def when_current_page_is(current_page)
|
593
|
+
@results.expects(:current_page).returns(current_page).times(0..5)
|
594
|
+
end
|
474
595
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bcms_google_mini_search
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-02-03 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: browsercms
|
16
|
-
requirement: &
|
16
|
+
requirement: &70256812077540 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,8 +21,8 @@ dependencies:
|
|
21
21
|
version: 3.3.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
25
|
-
description: A Google Appliance
|
24
|
+
version_requirements: *70256812077540
|
25
|
+
description: A Google Appliance module for BrowserCMS. Used to display search results
|
26
26
|
from a Google Mini/Search Appliance on a site.
|
27
27
|
email: github@browsermedia.com
|
28
28
|
executables: []
|
@@ -42,22 +42,26 @@ files:
|
|
42
42
|
- app/portlets/google_mini_search_engine_portlet.rb
|
43
43
|
- app/portlets/search_box_portlet.rb
|
44
44
|
- app/views/layouts/templates/default.html.erb
|
45
|
+
- app/views/layouts/templates/default.html.erb.html.erb
|
45
46
|
- app/views/portlets/google_mini_search_engine/_form.html.erb
|
46
47
|
- app/views/portlets/google_mini_search_engine/render.html.erb
|
47
48
|
- app/views/portlets/search_box/_form.html.erb
|
48
49
|
- app/views/portlets/search_box/render.html.erb
|
49
50
|
- bcms_google_mini_search.gemspec
|
50
|
-
- doc/
|
51
|
+
- doc/sample_clustering_result.xml
|
51
52
|
- lib/bcms_google_mini_search.rb
|
52
53
|
- lib/bcms_google_mini_search/engine.rb
|
54
|
+
- lib/bcms_google_mini_search/gsa.rb
|
53
55
|
- lib/bcms_google_mini_search/routes.rb
|
54
56
|
- lib/bcms_google_mini_search/version.rb
|
55
57
|
- lib/generators/bcms_google_mini_search/install/USAGE
|
56
58
|
- lib/generators/bcms_google_mini_search/install/install_generator.rb
|
57
59
|
- lib/tasks/.gitkeep
|
58
60
|
- public/stylesheets/.gitkeep
|
61
|
+
- release_notes.txt
|
59
62
|
- test/performance/browsing_test.rb
|
60
63
|
- test/test_helper.rb
|
64
|
+
- test/unit/gsa_test.rb
|
61
65
|
- test/unit/helpers/search_engine_helper_test.rb
|
62
66
|
- test/unit/portlets/google_mini_search_engine_portlet_test.rb
|
63
67
|
- test/unit/portlets/search_box_portlet_test.rb
|
@@ -78,7 +82,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
82
|
version: '0'
|
79
83
|
segments:
|
80
84
|
- 0
|
81
|
-
hash:
|
85
|
+
hash: -1846199177349674308
|
82
86
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
87
|
none: false
|
84
88
|
requirements:
|
@@ -87,16 +91,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
91
|
version: '0'
|
88
92
|
segments:
|
89
93
|
- 0
|
90
|
-
hash:
|
94
|
+
hash: -1846199177349674308
|
91
95
|
requirements: []
|
92
96
|
rubyforge_project: bcms_google_mini_search
|
93
|
-
rubygems_version: 1.8.
|
97
|
+
rubygems_version: 1.8.15
|
94
98
|
signing_key:
|
95
99
|
specification_version: 3
|
96
100
|
summary: A Google Mini Search Module for BrowserCMS
|
97
101
|
test_files:
|
98
102
|
- test/performance/browsing_test.rb
|
99
103
|
- test/test_helper.rb
|
104
|
+
- test/unit/gsa_test.rb
|
100
105
|
- test/unit/helpers/search_engine_helper_test.rb
|
101
106
|
- test/unit/portlets/google_mini_search_engine_portlet_test.rb
|
102
107
|
- test/unit/portlets/search_box_portlet_test.rb
|
data/doc/README_FOR_APP
DELETED