bcms_google_mini_search 1.0.0 → 1.1.0
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/README.markdown +11 -5
- data/app/models/search_result.rb +75 -11
- data/app/portlets/google_mini_search_engine_portlet.rb +5 -2
- data/app/views/portlets/google_mini_search_engine/_form.html.erb +3 -3
- data/app/views/portlets/google_mini_search_engine/render.html.erb +15 -1
- data/app/views/portlets/search_box/render.html.erb +1 -1
- data/test/performance/browsing_test.rb +9 -0
- data/test/test_helper.rb +39 -0
- data/test/unit/helpers/search_engine_helper_test.rb +4 -0
- data/test/unit/portlets/google_mini_search_engine_portlet_test.rb +15 -0
- data/test/unit/portlets/search_box_portlet_test.rb +9 -0
- data/test/unit/search_result_test.rb +428 -0
- metadata +38 -15
data/README.markdown
CHANGED
@@ -1,13 +1,19 @@
|
|
1
1
|
# Google Mini Search Module
|
2
2
|
|
3
|
-
This module allows BrowserCMS to integrate with a Google Mini
|
4
|
-
server, which can be configured to crawl your website. This module submits queries to a Mini server, and formats the results.
|
3
|
+
This module allows BrowserCMS to integrate with a Google Search Appliance (Mini or GSA). Google Search Appliance is a standalone search
|
4
|
+
server, which can be configured to crawl your website. This module submits queries to a GSA/Mini server, and formats the results.
|
5
5
|
It consists of the following two portlets.
|
6
6
|
|
7
7
|
1. Search Box - Displays an input box that submits a search query.
|
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
|
+
1. Allows for formatted results of GSA search results.
|
12
|
+
2. Will display Key Matches (as of 1.1)
|
13
|
+
3. Will display Synonyms/Related Queries (as of 1.1)
|
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)
|
15
|
+
|
16
|
+
Note: This module assume the BrowserCMS web site owner has access to their own GSA/Google Mini server, either hosted by
|
11
17
|
themselves or a third party service.
|
12
18
|
|
13
19
|
## A. Instructions
|
@@ -55,8 +61,8 @@ the module work, you will have to configure two portlets.
|
|
55
61
|
1. In your sitemap, create a new section called 'Search', with a path '/search'.
|
56
62
|
2. Create a page called 'Search Results', with a path '/search/search-results'.
|
57
63
|
3. On that page, add a new 'Google Mini Search Engine' portlet. Keep the default for most fields.
|
58
|
-
4. In the Service URL, field, enter in the domain name to your google mini server (i.e. http://google.mini.mysite.com)
|
59
|
-
5. In the
|
64
|
+
4. In the Service URL, field, enter in the domain name to your google mini server (i.e. http://google.mini.mysite.com). Note: This URL should be just the domain, i.e. no /search.
|
65
|
+
5. In the Collection Name field, enter the same name you gave your collection in B.2.2. (i.e. MYSITE)
|
60
66
|
6. In the Front End Name field, enter the same name you have your frontend in B.3.2 (i.e. MYSITE_frontend)
|
61
67
|
7. Make sure the 'path' attribute is the same as the page you are adding the portlet to (i.e. /search/search-results
|
62
68
|
8. Save the portlet
|
data/app/models/search_result.rb
CHANGED
@@ -7,8 +7,9 @@ class SearchResult
|
|
7
7
|
# a paging results of Search Results.
|
8
8
|
#
|
9
9
|
def self.find(query, options={})
|
10
|
+
return QueryResult.new unless query
|
10
11
|
xml_doc = fetch_xml_doc(query, options)
|
11
|
-
results =
|
12
|
+
results = parse_xml(xml_doc, options)
|
12
13
|
results.query = query
|
13
14
|
portlet = find_search_engine_portlet(options)
|
14
15
|
results.path = portlet.path
|
@@ -21,7 +22,7 @@ class SearchResult
|
|
21
22
|
count ? count.text.to_i : 0
|
22
23
|
end
|
23
24
|
|
24
|
-
def self.
|
25
|
+
def self.parse_hits(xml_doc)
|
25
26
|
root = xml_doc.root
|
26
27
|
results = []
|
27
28
|
xml_doc.elements.each('GSP/RES/R') do |ele|
|
@@ -37,10 +38,11 @@ class SearchResult
|
|
37
38
|
results
|
38
39
|
end
|
39
40
|
|
40
|
-
def self.
|
41
|
-
|
42
|
-
|
43
|
-
results
|
41
|
+
def self.parse_xml(xml_doc, options={})
|
42
|
+
hits = parse_hits(xml_doc)
|
43
|
+
results = QueryResult.new(hits)
|
44
|
+
results.key_matches= parse_key_matches(xml_doc)
|
45
|
+
results.synonyms = parse_synonyms(xml_doc, results)
|
44
46
|
results.results_count = parse_results_count(xml_doc)
|
45
47
|
results.num_pages = calculate_results_pages(results.results_count)
|
46
48
|
results.start = options[:start] ? options[:start] : 0
|
@@ -58,11 +60,18 @@ class SearchResult
|
|
58
60
|
|
59
61
|
def self.build_mini_url(options, query)
|
60
62
|
portlet = find_search_engine_portlet(options)
|
61
|
-
|
63
|
+
encoded_query = CGI::escape(query)
|
64
|
+
|
65
|
+
site = portlet.collection_name
|
66
|
+
if options[:site]
|
67
|
+
site = options[:site]
|
68
|
+
end
|
69
|
+
# encoded_query = query
|
70
|
+
url = "#{portlet.service_url}/search?q=#{encoded_query}&output=xml_no_dtd&client=#{portlet.front_end_name}&site=#{site}&filter=0"
|
62
71
|
if options[:start]
|
63
72
|
url = url + "&start=#{options[:start]}"
|
64
73
|
end
|
65
|
-
return url
|
74
|
+
return url
|
66
75
|
end
|
67
76
|
|
68
77
|
def self.find_search_engine_portlet(options)
|
@@ -83,13 +92,45 @@ class SearchResult
|
|
83
92
|
return xml_doc
|
84
93
|
end
|
85
94
|
|
95
|
+
def self.parse_key_matches(xml_doc)
|
96
|
+
matches = []
|
97
|
+
xml_doc.elements.each('GSP/GM') do |ele|
|
98
|
+
key_match = KeyMatch.new
|
99
|
+
key_match.url = ele.elements["GL"].text
|
100
|
+
key_match.title = ele.elements["GD"].text
|
101
|
+
matches << key_match
|
102
|
+
end
|
103
|
+
matches
|
104
|
+
end
|
86
105
|
|
106
|
+
def self.parse_synonyms(xml_doc, query_result)
|
107
|
+
synonyms = []
|
108
|
+
xml_doc.elements.each('GSP/Synonyms/OneSynonym') do |ele|
|
109
|
+
synonym = Synonym.new
|
110
|
+
synonym.query = ele.attributes["q"]
|
111
|
+
synonym.label = ele.text
|
112
|
+
synonym.query_result = query_result
|
113
|
+
synonyms << synonym
|
114
|
+
end
|
115
|
+
synonyms
|
116
|
+
end
|
117
|
+
# Represents the entire result of the query
|
118
|
+
class QueryResult < Array
|
87
119
|
|
88
|
-
|
89
|
-
|
90
|
-
attr_accessor :results_count, :num_pages, :current_page, :start, :query, :pages
|
120
|
+
attr_accessor :results_count, :num_pages, :current_page, :start, :query, :pages, :key_matches, :synonyms
|
91
121
|
attr_writer :path
|
92
122
|
|
123
|
+
def initialize(array=[])
|
124
|
+
# Need to set defaults so an empty result set works.
|
125
|
+
self.start = 0
|
126
|
+
self.results_count=0
|
127
|
+
self.key_matches = []
|
128
|
+
self.synonyms = []
|
129
|
+
self.num_pages = 1
|
130
|
+
super(array)
|
131
|
+
|
132
|
+
end
|
133
|
+
|
93
134
|
def path
|
94
135
|
@path ? @path : "/search/search-results"
|
95
136
|
end
|
@@ -138,6 +179,29 @@ class SearchResult
|
|
138
179
|
def page_path(page_num)
|
139
180
|
"#{path}?query=#{query}&start=#{page_num * 10 - 10}"
|
140
181
|
end
|
182
|
+
|
183
|
+
def key_matches?
|
184
|
+
!key_matches.empty?
|
185
|
+
end
|
186
|
+
|
187
|
+
def synonyms?
|
188
|
+
!synonyms.empty?
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Sometimes refered to as 'Featured Links', though the GSA UI uses the KeyMatch
|
193
|
+
class KeyMatch
|
194
|
+
attr_accessor :url, :title
|
195
|
+
end
|
196
|
+
|
197
|
+
# AKA Related Query in Google UI
|
198
|
+
class Synonym
|
199
|
+
attr_accessor :query, :label, :query_result
|
200
|
+
|
201
|
+
# Return the url that should be
|
202
|
+
def url
|
203
|
+
"#{query_result.path}?query=#{query}"
|
204
|
+
end
|
141
205
|
end
|
142
206
|
|
143
207
|
|
@@ -1,9 +1,12 @@
|
|
1
1
|
class GoogleMiniSearchEnginePortlet < Portlet
|
2
|
-
|
2
|
+
|
3
|
+
enable_template_editor true
|
4
|
+
|
3
5
|
def render
|
4
6
|
@query = params[:query]
|
7
|
+
@site = params[:site]
|
5
8
|
@start = params[:start] ? params[:start].to_i : 0
|
6
|
-
@results = SearchResult.find(@query, {:start => @start, :portlet => @portlet})
|
9
|
+
@results = SearchResult.find(@query, {:start => @start, :portlet => @portlet, :site=>@site})
|
7
10
|
end
|
8
11
|
|
9
12
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%= f.cms_text_field :name, :default_value => "Google Mini Search Engine", :instructions=>"The Search Box portlet will look this up by name, so make it unique." %>
|
2
2
|
<%= f.cms_text_field :path, :default_value => "/search/search-results", :instructions=>"The path of the page this portlet will be embedded in." %>
|
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
|
-
<%= f.cms_text_field :collection_name, :instructions => "Copy the name of the Collection from Google Mini here." %>
|
5
|
-
<%= f.cms_text_field :front_end_name, :instructions => "Copy the name of the Collection front end from Google Mini here." %>
|
6
|
-
<%= f.
|
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
|
+
<%= 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_template_editor :template %>
|
@@ -2,7 +2,21 @@
|
|
2
2
|
For '<%= @results.query %>', found <%= @results.results_count %> results. <br />
|
3
3
|
<% if @results.previous_page? %><%= link_to h("< Back"), @results.previous_page_path %><% end %>
|
4
4
|
<% if @results.next_page? %><%= link_to h("Next >"), @results.next_page_path %><% end %>
|
5
|
-
|
5
|
+
<% if @results.key_matches? %>
|
6
|
+
<ul class="key_matches">
|
7
|
+
<% @results.key_matches.each do |match| %>
|
8
|
+
<li><%= link_to match.title, match.url %></li>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
<% end %>
|
12
|
+
<% if @results.synonyms? %>
|
13
|
+
<span class="synonyms">You could also try:
|
14
|
+
<% @results.synonyms.each do |synonym| %>
|
15
|
+
<%= link_to synonym.label, synonym.url %>
|
16
|
+
<% end %>
|
17
|
+
</span>
|
18
|
+
<% end %>
|
19
|
+
<ul class="search_results">
|
6
20
|
<% @results.each do |result|%>
|
7
21
|
<li>
|
8
22
|
<%= result.number %> <%= link_to result.title, result.url, :class=>"search_result_title" %><br />
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
ENV["RAILS_ENV"] = "test"
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
|
3
|
+
require 'test_help'
|
4
|
+
require 'mocha'
|
5
|
+
|
6
|
+
class ActiveSupport::TestCase
|
7
|
+
# Transactional fixtures accelerate your tests by wrapping each test method
|
8
|
+
# in a transaction that's rolled back on completion. This ensures that the
|
9
|
+
# test database remains unchanged so your fixtures don't have to be reloaded
|
10
|
+
# between every test method. Fewer database queries means faster tests.
|
11
|
+
#
|
12
|
+
# Read Mike Clark's excellent walkthrough at
|
13
|
+
# http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
|
14
|
+
#
|
15
|
+
# Every Active Record database supports transactions except MyISAM tables
|
16
|
+
# in MySQL. Turn off transactional fixtures in this case; however, if you
|
17
|
+
# don't care one way or the other, switching from MyISAM to InnoDB tables
|
18
|
+
# is recommended.
|
19
|
+
#
|
20
|
+
# The only drawback to using transactional fixtures is when you actually
|
21
|
+
# need to test transactions. Since your test is bracketed by a transaction,
|
22
|
+
# any transactions started in your code will be automatically rolled back.
|
23
|
+
self.use_transactional_fixtures = true
|
24
|
+
|
25
|
+
# Instantiated fixtures are slow, but give you @david where otherwise you
|
26
|
+
# would need people(:david). If you don't want to migrate your existing
|
27
|
+
# test cases which use the @david style and don't mind the speed hit (each
|
28
|
+
# instantiated fixtures translates to a database query per test method),
|
29
|
+
# then set this back to true.
|
30
|
+
self.use_instantiated_fixtures = false
|
31
|
+
|
32
|
+
# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
|
33
|
+
#
|
34
|
+
# Note: You'll currently still have to declare fixtures explicitly in integration tests
|
35
|
+
# -- they do not yet inherit this setting
|
36
|
+
fixtures :all
|
37
|
+
|
38
|
+
# Add more helper methods to be used by all tests here...
|
39
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '/../../test_helper')
|
2
|
+
|
3
|
+
class GoogleMiniSearchEngineTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
test "Should be able to create new instance of a portlet" do
|
6
|
+
assert GoogleMiniSearchEnginePortlet.create!(:name => "New Portlet")
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
test "Path attribute can be set in constructor" do
|
12
|
+
portlet = GoogleMiniSearchEnginePortlet.create!(:name=>"Engine", :path => "/engine")
|
13
|
+
assert_equal "/engine", portlet.path
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,428 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '/../test_helper')
|
2
|
+
|
3
|
+
class SearchResultTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@xml_string = <<EOF
|
7
|
+
<GSP>
|
8
|
+
<RES>
|
9
|
+
<M>2</M>
|
10
|
+
<R N="1">
|
11
|
+
<U>http://someurl.com</U>
|
12
|
+
<T>TITLE</T>
|
13
|
+
<S>BLURB</S>
|
14
|
+
<HAS>
|
15
|
+
<C SZ="1k" />
|
16
|
+
</HAS>
|
17
|
+
</R>
|
18
|
+
<R N="2">
|
19
|
+
<U>http://someurl2.com</U>
|
20
|
+
<T>TITLE 2</T>
|
21
|
+
<S>BLURB 2</S>
|
22
|
+
<HAS>
|
23
|
+
<C SZ="2k"/>
|
24
|
+
</HAS>
|
25
|
+
</R>
|
26
|
+
</RES>
|
27
|
+
</GSP>
|
28
|
+
EOF
|
29
|
+
@xml_doc = REXML::Document.new @xml_string
|
30
|
+
|
31
|
+
@large_results_set = <<EOF
|
32
|
+
<GSP>
|
33
|
+
<RES>
|
34
|
+
<M>35</M>
|
35
|
+
<R N="1">
|
36
|
+
<U>http://someurl.com</U>
|
37
|
+
<T>TITLE</T>
|
38
|
+
<S>BLURB</S>
|
39
|
+
<HAS>
|
40
|
+
<C SZ="1k" />
|
41
|
+
</HAS>
|
42
|
+
</R>
|
43
|
+
<R N="2">
|
44
|
+
<U>http://someurl2.com</U>
|
45
|
+
<T>TITLE 2</T>
|
46
|
+
<S>BLURB 2</S>
|
47
|
+
<HAS>
|
48
|
+
<C SZ="2k"/>
|
49
|
+
</HAS>
|
50
|
+
</R>
|
51
|
+
</RES>
|
52
|
+
</GSP>
|
53
|
+
EOF
|
54
|
+
@large_xml_doc = REXML::Document.new(@large_results_set)
|
55
|
+
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
test "Parse result count from google mini results xml." do
|
60
|
+
|
61
|
+
xml = <<EOF
|
62
|
+
<GSP>
|
63
|
+
<RES>
|
64
|
+
<M>35</M>
|
65
|
+
</RES>
|
66
|
+
</GSP>
|
67
|
+
EOF
|
68
|
+
empty_doc = REXML::Document.new xml
|
69
|
+
assert_equal 35, SearchResult.parse_results_count(empty_doc)
|
70
|
+
assert_equal 35, SearchResult.parse_xml(empty_doc).results_count
|
71
|
+
assert_equal 4, SearchResult.parse_xml(empty_doc).num_pages
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
test "Default result count is zero." do
|
76
|
+
|
77
|
+
xml = <<EOF
|
78
|
+
<GSP>
|
79
|
+
<RES>
|
80
|
+
</RES>
|
81
|
+
</GSP>
|
82
|
+
EOF
|
83
|
+
empty_doc = REXML::Document.new xml
|
84
|
+
assert_equal 0, SearchResult.parse_results_count(empty_doc)
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
test "Empty xml gives empty results" do
|
89
|
+
xml = <<EOF
|
90
|
+
<GSP>
|
91
|
+
<RES>
|
92
|
+
</RES>
|
93
|
+
</GSP>
|
94
|
+
EOF
|
95
|
+
empty_doc = REXML::Document.new xml
|
96
|
+
SearchResult.expects(:fetch_xml_doc).with("therapy", {}).returns(empty_doc)
|
97
|
+
|
98
|
+
assert_equal [], SearchResult.find("therapy", {})
|
99
|
+
end
|
100
|
+
|
101
|
+
test "Parse result set" do
|
102
|
+
|
103
|
+
results = SearchResult.parse_hits(@xml_doc)
|
104
|
+
assert_equal 2, results.size
|
105
|
+
|
106
|
+
assert_equal "1", results[0].number
|
107
|
+
assert_equal "http://someurl.com", results[0].url
|
108
|
+
assert_equal "TITLE", results[0].title
|
109
|
+
assert_equal "BLURB", results[0].description
|
110
|
+
assert_equal "1k", results[0].size
|
111
|
+
|
112
|
+
assert_equal "2", results[1].number
|
113
|
+
assert_equal "http://someurl2.com", results[1].url
|
114
|
+
assert_equal "TITLE 2", results[1].title
|
115
|
+
assert_equal "BLURB 2", results[1].description
|
116
|
+
assert_equal "2k", results[1].size
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
test "Calculates the results pages" do
|
121
|
+
assert_equal 1, SearchResult.calculate_results_pages(9)
|
122
|
+
assert_equal 1, SearchResult.calculate_results_pages(10)
|
123
|
+
assert_equal 2, SearchResult.calculate_results_pages(19)
|
124
|
+
assert_equal 2, SearchResult.calculate_results_pages(20)
|
125
|
+
assert_equal 3, SearchResult.calculate_results_pages(21)
|
126
|
+
assert_equal 4, SearchResult.calculate_results_pages(40)
|
127
|
+
assert_equal 0, SearchResult.calculate_results_pages(0)
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
test "Calculates current page based on total results and start" do
|
132
|
+
results = SearchResult::QueryResult.new
|
133
|
+
results.start = 0
|
134
|
+
assert_equal 1, results.current_page
|
135
|
+
|
136
|
+
results.start = 10
|
137
|
+
assert_equal 2, results.current_page
|
138
|
+
|
139
|
+
results.start = 20
|
140
|
+
assert_equal 3, results.current_page
|
141
|
+
|
142
|
+
results.start = 30
|
143
|
+
assert_equal 4, results.current_page
|
144
|
+
end
|
145
|
+
|
146
|
+
test "Next start" do
|
147
|
+
r = SearchResult::QueryResult.new
|
148
|
+
r.start = 0
|
149
|
+
assert_equal 10, r.next_start
|
150
|
+
r.start = 10
|
151
|
+
assert_equal 20, r.next_start
|
152
|
+
r.start = 20
|
153
|
+
assert_equal 30, r.next_start
|
154
|
+
end
|
155
|
+
|
156
|
+
test "Find results should return a paging list of documents with no start" do
|
157
|
+
SearchResult.expects(:fetch_xml_doc).with("therapy", {}).returns(@large_xml_doc)
|
158
|
+
|
159
|
+
results = SearchResult.find("therapy")
|
160
|
+
assert_equal "therapy", results.query
|
161
|
+
assert_equal 35, results.results_count
|
162
|
+
assert_equal 4, results.num_pages
|
163
|
+
assert_equal 0, results.start
|
164
|
+
assert_equal 2, results.size
|
165
|
+
assert_equal 1, results.current_page
|
166
|
+
assert_equal 10, results.next_start
|
167
|
+
assert results.next_page?
|
168
|
+
assert_equal -10, results.previous_start
|
169
|
+
assert_equal false, results.previous_page?
|
170
|
+
assert_equal (1..4), results.pages
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
test "Find results starts on page 2, if a start is specified" do
|
176
|
+
SearchResult.expects(:fetch_xml_doc).with("therapy", :start=>10).returns(@large_xml_doc)
|
177
|
+
|
178
|
+
results = SearchResult.find("therapy", :start => 10)
|
179
|
+
assert_equal 35, results.results_count
|
180
|
+
assert_equal 4, results.num_pages
|
181
|
+
assert_equal 10, results.start
|
182
|
+
assert_equal 2, results.size
|
183
|
+
assert_equal 2, results.current_page
|
184
|
+
assert_equal 20, results.next_start
|
185
|
+
assert results.next_page?
|
186
|
+
assert_equal 0, results.previous_start
|
187
|
+
assert_equal true, results.previous_page?
|
188
|
+
end
|
189
|
+
|
190
|
+
test "No next start when on the last page" do
|
191
|
+
SearchResult.expects(:fetch_xml_doc).with("therapy", {:start => 30}).returns(@large_xml_doc)
|
192
|
+
|
193
|
+
results = SearchResult.find("therapy", :start => 30)
|
194
|
+
assert_equal 40, results.next_start
|
195
|
+
assert_equal false, results.next_page?
|
196
|
+
assert_equal 20, results.previous_start
|
197
|
+
assert_equal true, results.previous_page?
|
198
|
+
end
|
199
|
+
|
200
|
+
test "Should be no previous or next for a single page of results" do
|
201
|
+
SearchResult.expects(:fetch_xml_doc).with("therapy", {}).returns(@xml_doc)
|
202
|
+
|
203
|
+
results = SearchResult.find("therapy")
|
204
|
+
assert_equal false, results.next_page?
|
205
|
+
assert_equal false, results.previous_page?
|
206
|
+
assert_equal [], results.pages
|
207
|
+
end
|
208
|
+
|
209
|
+
test "Behavior of ranges" do
|
210
|
+
c = 0
|
211
|
+
(1..4).each_with_index do |i, count|
|
212
|
+
assert_equal count + 1, i
|
213
|
+
c = count
|
214
|
+
end
|
215
|
+
assert_equal 3, c
|
216
|
+
end
|
217
|
+
|
218
|
+
test "current_page should check to see if the current page matches" do
|
219
|
+
results = SearchResult::QueryResult.new
|
220
|
+
results.start = 0
|
221
|
+
|
222
|
+
assert_equal true, results.current_page?(1)
|
223
|
+
assert_equal false, results.current_page?(2)
|
224
|
+
assert_equal false, results.current_page?(3)
|
225
|
+
assert_equal false, results.current_page?(4)
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
test "Path to next page" do
|
230
|
+
results = SearchResult::QueryResult.new
|
231
|
+
results.start = 0
|
232
|
+
results.path = "/search/search-results"
|
233
|
+
results.query = "X"
|
234
|
+
|
235
|
+
assert_equal "/search/search-results?query=X&start=10", results.next_page_path
|
236
|
+
end
|
237
|
+
|
238
|
+
test "Path to previous page" do
|
239
|
+
results = SearchResult::QueryResult.new
|
240
|
+
results.start = 20
|
241
|
+
results.path = "/search/search-results"
|
242
|
+
results.query = "X"
|
243
|
+
|
244
|
+
assert_equal "/search/search-results?query=X&start=10", results.previous_page_path
|
245
|
+
end
|
246
|
+
|
247
|
+
test "Sets path to default search-results" do
|
248
|
+
results = SearchResult::QueryResult.new
|
249
|
+
assert_equal "/search/search-results", results.path
|
250
|
+
end
|
251
|
+
|
252
|
+
test "Setting path overrides the defaults" do
|
253
|
+
results = SearchResult::QueryResult.new
|
254
|
+
results.path = "/other"
|
255
|
+
assert_equal "/other", results.path
|
256
|
+
end
|
257
|
+
|
258
|
+
test "page_path" do
|
259
|
+
results = SearchResult::QueryResult.new
|
260
|
+
results.query = "X"
|
261
|
+
|
262
|
+
assert_equal "/search/search-results?query=X&start=0", results.page_path(1)
|
263
|
+
assert_equal "/search/search-results?query=X&start=10", results.page_path(2)
|
264
|
+
assert_equal "/search/search-results?query=X&start=20", results.page_path(3)
|
265
|
+
assert_equal "/search/search-results?query=X&start=30", results.page_path(4)
|
266
|
+
|
267
|
+
end
|
268
|
+
|
269
|
+
test "Portlet attributes are used to look up path" do
|
270
|
+
portlet = GoogleMiniSearchEnginePortlet.new(:name=>"Engine", :path => "/engine")
|
271
|
+
SearchResult.expects(:fetch_xml_doc).with("therapy", {:portlet=> portlet}).returns(@xml_doc)
|
272
|
+
|
273
|
+
results = SearchResult.find("therapy", {:portlet=> portlet})
|
274
|
+
|
275
|
+
assert_equal "/engine", results.path
|
276
|
+
end
|
277
|
+
|
278
|
+
test "Default path is used if no portlet specified" do
|
279
|
+
SearchResult.expects(:fetch_xml_doc).with("therapy", {}).returns(@xml_doc)
|
280
|
+
results = SearchResult.find("therapy", {})
|
281
|
+
assert_equal "/search/search-results", results.path
|
282
|
+
end
|
283
|
+
|
284
|
+
test "Uses service URL from 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
|
+
url = SearchResult.build_mini_url({:portlet => portlet}, "STUFF")
|
290
|
+
assert_equal "http://mini.someurl.com/search?q=STUFF&output=xml_no_dtd&client=FRONT_END&site=COLLECT&filter=0", url
|
291
|
+
|
292
|
+
url = SearchResult.build_mini_url({:portlet => portlet, :start=>100}, "STUFF")
|
293
|
+
assert_equal "http://mini.someurl.com/search?q=STUFF&output=xml_no_dtd&client=FRONT_END&site=COLLECT&filter=0&start=100", url
|
294
|
+
|
295
|
+
end
|
296
|
+
|
297
|
+
test "Explicitly passing a collection in will query with that rather than a default collection" do
|
298
|
+
portlet = GoogleMiniSearchEnginePortlet.new(
|
299
|
+
:name=>"Engine", :path => "/engine", :service_url => "http://mini.someurl.com",
|
300
|
+
:collection_name => "COLLECT", :front_end_name => "FRONT_END")
|
301
|
+
|
302
|
+
url = SearchResult.build_mini_url({:portlet => portlet, :site=>"ANOTHER_COLLECTION"}, "STUFF")
|
303
|
+
assert_equal "http://mini.someurl.com/search?q=STUFF&output=xml_no_dtd&client=FRONT_END&site=ANOTHER_COLLECTION&filter=0", url
|
304
|
+
end
|
305
|
+
|
306
|
+
test "Handles multiword queries" do
|
307
|
+
url = SearchResult.build_mini_url({}, "One Two")
|
308
|
+
assert_equal "/search?q=One+Two&output=xml_no_dtd&client=&site=&filter=0", url
|
309
|
+
end
|
310
|
+
|
311
|
+
test "Handles keymatches in results" do
|
312
|
+
@xml_with_keymatches = <<XML
|
313
|
+
<GSP>
|
314
|
+
<GM>
|
315
|
+
<GL>http://url1.org</GL>
|
316
|
+
<GD>URL 1</GD>
|
317
|
+
</GM>
|
318
|
+
<GM>
|
319
|
+
<GL>http://url2.org</GL>
|
320
|
+
<GD>URL 2</GD>
|
321
|
+
</GM>
|
322
|
+
<RES>
|
323
|
+
<M>35</M>
|
324
|
+
<R N="1">
|
325
|
+
<U>http://someurl.com</U>
|
326
|
+
<T>TITLE</T>
|
327
|
+
<S>BLURB</S>
|
328
|
+
<HAS>
|
329
|
+
<C SZ="1k" />
|
330
|
+
</HAS>
|
331
|
+
</R>
|
332
|
+
<R N="2">
|
333
|
+
<U>http://someurl2.com</U>
|
334
|
+
<T>TITLE 2</T>
|
335
|
+
<S>BLURB 2</S>
|
336
|
+
<HAS>
|
337
|
+
<C SZ="2k"/>
|
338
|
+
</HAS>
|
339
|
+
</R>
|
340
|
+
</RES>
|
341
|
+
</GSP>
|
342
|
+
XML
|
343
|
+
@results_with_keymatches = REXML::Document.new @xml_with_keymatches
|
344
|
+
|
345
|
+
result = SearchResult.parse_xml @results_with_keymatches
|
346
|
+
|
347
|
+
assert_equal true, result.key_matches?
|
348
|
+
assert_equal 2, result.key_matches.size
|
349
|
+
assert_equal "http://url1.org", result.key_matches[0].url
|
350
|
+
assert_equal "URL 1", result.key_matches[0].title
|
351
|
+
assert_equal "http://url2.org", result.key_matches[1].url
|
352
|
+
assert_equal "URL 2", result.key_matches[1].title
|
353
|
+
end
|
354
|
+
|
355
|
+
test "Handles results with no keymatches" do
|
356
|
+
result = SearchResult.parse_xml @xml_doc
|
357
|
+
assert_equal false, result.key_matches?
|
358
|
+
end
|
359
|
+
|
360
|
+
test "Handle Synonyms / Related Queries" do
|
361
|
+
xml_with_synonyms = <<XML
|
362
|
+
<GSP>
|
363
|
+
<Synonyms>
|
364
|
+
<OneSynonym q="Query 1">Label 1</OneSynonym>
|
365
|
+
<OneSynonym q="Query 2">Label 2</OneSynonym>
|
366
|
+
</Synonyms>
|
367
|
+
<RES>
|
368
|
+
<M>35</M>
|
369
|
+
<R N="1">
|
370
|
+
<U>http://someurl.com</U>
|
371
|
+
<T>TITLE</T>
|
372
|
+
<S>BLURB</S>
|
373
|
+
<HAS>
|
374
|
+
<C SZ="1k" />
|
375
|
+
</HAS>
|
376
|
+
</R>
|
377
|
+
<R N="2">
|
378
|
+
<U>http://someurl2.com</U>
|
379
|
+
<T>TITLE 2</T>
|
380
|
+
<S>BLURB 2</S>
|
381
|
+
<HAS>
|
382
|
+
<C SZ="2k"/>
|
383
|
+
</HAS>
|
384
|
+
</R>
|
385
|
+
</RES>
|
386
|
+
</GSP>
|
387
|
+
XML
|
388
|
+
xml_doc_with_synonyms = REXML::Document.new xml_with_synonyms
|
389
|
+
|
390
|
+
result = SearchResult.parse_xml xml_doc_with_synonyms
|
391
|
+
|
392
|
+
result.expects(:path).returns("/search").twice
|
393
|
+
assert_equal true, result.synonyms?
|
394
|
+
assert_equal 2, result.synonyms.size
|
395
|
+
assert_equal "Label 1", result.synonyms[0].label
|
396
|
+
assert_equal "Query 1", result.synonyms[0].query
|
397
|
+
assert_equal "/search?query=Query 1", result.synonyms[0].url
|
398
|
+
assert_equal "Label 2", result.synonyms[1].label
|
399
|
+
assert_equal "Query 2", result.synonyms[1].query
|
400
|
+
assert_equal "/search?query=Query 2", result.synonyms[1].url
|
401
|
+
|
402
|
+
end
|
403
|
+
|
404
|
+
test "Handles results with no Synonyms" do
|
405
|
+
result = SearchResult.parse_xml @xml_doc
|
406
|
+
assert_equal false, result.synonyms?
|
407
|
+
end
|
408
|
+
|
409
|
+
test "Calculate URL for Related Queries/Synonyms" do
|
410
|
+
syn = SearchResult::Synonym.new
|
411
|
+
syn.query = "Testing"
|
412
|
+
mock_result = mock()
|
413
|
+
mock_result.expects(:path).returns("/random")
|
414
|
+
syn.query_result = mock_result
|
415
|
+
assert_equal "/random?query=Testing", syn.url
|
416
|
+
end
|
417
|
+
|
418
|
+
test "No query = empty results" do
|
419
|
+
result = SearchResult.find(nil)
|
420
|
+
assert_equal 0, result.size
|
421
|
+
assert_equal 0, result.start
|
422
|
+
assert_equal false, result.next_page?
|
423
|
+
assert_equal false, result.previous_page?
|
424
|
+
assert_equal false, result.key_matches?
|
425
|
+
assert_equal false, result.synonyms?
|
426
|
+
assert_equal [], result.pages
|
427
|
+
end
|
428
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bcms_google_mini_search
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 19
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 1.1.0
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- BrowserMedia
|
@@ -9,11 +15,11 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date:
|
18
|
+
date: 2010-08-18 00:00:00 -04:00
|
13
19
|
default_executable:
|
14
20
|
dependencies: []
|
15
21
|
|
16
|
-
description:
|
22
|
+
description: Allows developers to integrate Google Mini or Google Search Appliance into their CMS sites. Can be used to fetch search results and format them.
|
17
23
|
email: github@browsermedia.com
|
18
24
|
executables: []
|
19
25
|
|
@@ -24,44 +30,61 @@ extra_rdoc_files:
|
|
24
30
|
files:
|
25
31
|
- app/controllers/application_controller.rb
|
26
32
|
- app/helpers/application_helper.rb
|
33
|
+
- app/models/search_result.rb
|
27
34
|
- app/portlets/google_mini_search_engine_portlet.rb
|
28
35
|
- app/portlets/search_box_portlet.rb
|
29
|
-
- app/models/search_result.rb
|
30
|
-
- app/views/portlets/search_box/render.html.erb
|
31
|
-
- app/views/portlets/search_box/_form.html.erb
|
32
|
-
- app/views/portlets/google_mini_search_engine/render.html.erb
|
33
36
|
- app/views/portlets/google_mini_search_engine/_form.html.erb
|
37
|
+
- app/views/portlets/google_mini_search_engine/render.html.erb
|
38
|
+
- app/views/portlets/search_box/_form.html.erb
|
39
|
+
- app/views/portlets/search_box/render.html.erb
|
34
40
|
- lib/bcms_google_mini_search.rb
|
35
41
|
- lib/bcms_google_mini_search/routes.rb
|
36
42
|
- rails/init.rb
|
37
43
|
- README.markdown
|
44
|
+
- test/performance/browsing_test.rb
|
45
|
+
- test/test_helper.rb
|
46
|
+
- test/unit/helpers/search_engine_helper_test.rb
|
47
|
+
- test/unit/portlets/google_mini_search_engine_portlet_test.rb
|
48
|
+
- test/unit/portlets/search_box_portlet_test.rb
|
49
|
+
- test/unit/search_result_test.rb
|
38
50
|
has_rdoc: true
|
39
|
-
homepage: http://
|
51
|
+
homepage: http://github.com/browsermedia/bcms_google_mini_search
|
40
52
|
licenses: []
|
41
53
|
|
42
54
|
post_install_message:
|
43
|
-
rdoc_options:
|
44
|
-
|
55
|
+
rdoc_options:
|
56
|
+
- --charset=UTF-8
|
45
57
|
require_paths:
|
46
58
|
- lib
|
47
59
|
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
48
61
|
requirements:
|
49
62
|
- - ">="
|
50
63
|
- !ruby/object:Gem::Version
|
64
|
+
hash: 3
|
65
|
+
segments:
|
66
|
+
- 0
|
51
67
|
version: "0"
|
52
|
-
version:
|
53
68
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
54
70
|
requirements:
|
55
71
|
- - ">="
|
56
72
|
- !ruby/object:Gem::Version
|
73
|
+
hash: 3
|
74
|
+
segments:
|
75
|
+
- 0
|
57
76
|
version: "0"
|
58
|
-
version:
|
59
77
|
requirements: []
|
60
78
|
|
61
79
|
rubyforge_project: bcms_google_mini_search
|
62
|
-
rubygems_version: 1.3.
|
80
|
+
rubygems_version: 1.3.7
|
63
81
|
signing_key:
|
64
82
|
specification_version: 3
|
65
83
|
summary: A Google Mini Search Module for BrowserCMS
|
66
|
-
test_files:
|
67
|
-
|
84
|
+
test_files:
|
85
|
+
- test/performance/browsing_test.rb
|
86
|
+
- test/test_helper.rb
|
87
|
+
- test/unit/helpers/search_engine_helper_test.rb
|
88
|
+
- test/unit/portlets/google_mini_search_engine_portlet_test.rb
|
89
|
+
- test/unit/portlets/search_box_portlet_test.rb
|
90
|
+
- test/unit/search_result_test.rb
|