quick_search-core 0.0.1.test
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +168 -0
- data/Rakefile +6 -0
- data/app/assets/images/quick_search/askus.gif +0 -0
- data/app/assets/images/quick_search/favicon.ico +0 -0
- data/app/assets/images/quick_search/greybox.png +0 -0
- data/app/assets/images/quick_search/homescreen-icon-64x64.png +0 -0
- data/app/assets/images/quick_search/ncsu-library-logo-white.png +0 -0
- data/app/assets/javascripts/quick_search/appstats.js +8 -0
- data/app/assets/javascripts/quick_search/event_tracking.js.erb +257 -0
- data/app/assets/javascripts/quick_search/google_analytics.js.erb +8 -0
- data/app/assets/javascripts/quick_search/opensearch.js +2 -0
- data/app/assets/javascripts/quick_search/pages.js.coffee +3 -0
- data/app/assets/javascripts/quick_search/quicksearch.js +33 -0
- data/app/assets/javascripts/quick_search/realtime.js.erb +12 -0
- data/app/assets/javascripts/quick_search/search.js +3 -0
- data/app/assets/javascripts/quick_search/xhr_search.js +82 -0
- data/app/assets/javascripts/quick_search.js.erb +20 -0
- data/app/assets/stylesheets/foundation_and_overrides.scss +1327 -0
- data/app/assets/stylesheets/quick_search/appstats.css.scss +7 -0
- data/app/assets/stylesheets/quick_search/opensearch.css.scss +3 -0
- data/app/assets/stylesheets/quick_search/pages.css.scss +3 -0
- data/app/assets/stylesheets/quick_search/quick_search.css.scss +248 -0
- data/app/assets/stylesheets/quick_search/search.css.scss +3 -0
- data/app/assets/stylesheets/quick_search.css.scss.erb +7 -0
- data/app/controllers/concerns/quick_search/auth.rb +22 -0
- data/app/controllers/concerns/quick_search/doi_trap.rb +43 -0
- data/app/controllers/concerns/quick_search/encode_utf8.rb +12 -0
- data/app/controllers/concerns/quick_search/on_campus.rb +43 -0
- data/app/controllers/concerns/quick_search/query_filter.rb +25 -0
- data/app/controllers/concerns/quick_search/query_parser.rb +54 -0
- data/app/controllers/concerns/quick_search/searcher_concern.rb +101 -0
- data/app/controllers/concerns/quick_search/searcher_config.rb +16 -0
- data/app/controllers/quick_search/application_controller.rb +7 -0
- data/app/controllers/quick_search/appstats_controller.rb +210 -0
- data/app/controllers/quick_search/opensearch_controller.rb +9 -0
- data/app/controllers/quick_search/pages_controller.rb +18 -0
- data/app/controllers/quick_search/search_controller.rb +250 -0
- data/app/helpers/quick_search/application_helper.rb +68 -0
- data/app/helpers/quick_search/appstats_helper.rb +2 -0
- data/app/helpers/quick_search/opensearch_helper.rb +2 -0
- data/app/helpers/quick_search/pages_helper.rb +2 -0
- data/app/helpers/quick_search/search_helper.rb +2 -0
- data/app/models/event.rb +2 -0
- data/app/models/search.rb +11 -0
- data/app/searchers/quick_search/best_bets_searcher.rb +102 -0
- data/app/searchers/quick_search/searcher.rb +43 -0
- data/app/views/layouts/quick_search/_best_bets.html.erb +18 -0
- data/app/views/layouts/quick_search/_common_searches.html.erb +15 -0
- data/app/views/layouts/quick_search/_found_types.html.erb +13 -0
- data/app/views/layouts/quick_search/_google_analytics.html.erb +24 -0
- data/app/views/layouts/quick_search/_more_options.html.erb +10 -0
- data/app/views/layouts/quick_search/_page_title.html.erb +8 -0
- data/app/views/layouts/quick_search/_related_topics.html.erb +8 -0
- data/app/views/layouts/quick_search/_search_form.html.erb +19 -0
- data/app/views/layouts/quick_search/_spelling_suggestion.html.erb +13 -0
- data/app/views/layouts/quick_search/_topic_snippet.html.erb +5 -0
- data/app/views/layouts/quick_search/application.html.erb +40 -0
- data/app/views/quick_search/appstats/_click_count_table.html.erb +23 -0
- data/app/views/quick_search/appstats/_datatables.html.erb +12 -0
- data/app/views/quick_search/appstats/_date_range.html.erb +15 -0
- data/app/views/quick_search/appstats/_menu.html.erb +16 -0
- data/app/views/quick_search/appstats/_top_spot_report_table.html.erb +33 -0
- data/app/views/quick_search/appstats/clicks_overview.html.erb +20 -0
- data/app/views/quick_search/appstats/index.html.erb +31 -0
- data/app/views/quick_search/appstats/module_click_detail.html.erb +32 -0
- data/app/views/quick_search/appstats/realtime.html.erb +15 -0
- data/app/views/quick_search/appstats/top_searches.html.erb +38 -0
- data/app/views/quick_search/appstats/top_spot.html.erb +14 -0
- data/app/views/quick_search/appstats/top_spot_detail.html.erb +35 -0
- data/app/views/quick_search/kaminari/_first_page.html.erb +12 -0
- data/app/views/quick_search/kaminari/_gap.html.erb +11 -0
- data/app/views/quick_search/kaminari/_last_page.html.erb +12 -0
- data/app/views/quick_search/kaminari/_next_page.html.erb +12 -0
- data/app/views/quick_search/kaminari/_page.html.erb +13 -0
- data/app/views/quick_search/kaminari/_paginator.html.erb +25 -0
- data/app/views/quick_search/kaminari/_prev_page.html.erb +11 -0
- data/app/views/quick_search/opensearch/opensearch.xml.builder +11 -0
- data/app/views/quick_search/pages/about.html.erb +9 -0
- data/app/views/quick_search/pages/home.html.erb +6 -0
- data/app/views/quick_search/pages/realtime.html.erb +15 -0
- data/app/views/quick_search/search/_citation.html.erb +18 -0
- data/app/views/quick_search/search/_module.html.erb +37 -0
- data/app/views/quick_search/search/_module_with_paging.html.erb +14 -0
- data/app/views/quick_search/search/_no_results.html.erb +11 -0
- data/app/views/quick_search/search/_result.html.erb +17 -0
- data/app/views/quick_search/search/_result_details.html.erb +45 -0
- data/app/views/quick_search/search/_result_no_thumbnail.html.erb +3 -0
- data/app/views/quick_search/search/_result_title.html.erb +4 -0
- data/app/views/quick_search/search/_result_with_thumbnail.html.erb +21 -0
- data/app/views/quick_search/search/_search_error.html.erb +9 -0
- data/app/views/quick_search/search/_see_all.html.erb +11 -0
- data/app/views/quick_search/search/_xhr_response.html.erb +15 -0
- data/app/views/quick_search/search/_xhr_response.json.erb +14 -0
- data/app/views/quick_search/search/_xhr_response_with_paging.html.erb +1 -0
- data/app/views/quick_search/search/index.html.erb +100 -0
- data/config/application.rb +25 -0
- data/config/best_bets.yml +86 -0
- data/config/boot.rb +4 -0
- data/config/database.yml.example +31 -0
- data/config/locales/en.yml +30 -0
- data/config/quicksearch_config.yml.example +69 -0
- data/config/routes.rb +81 -0
- data/db/migrate/20140130202859_create_events.rb +11 -0
- data/db/migrate/20140130202944_create_searches.rb +9 -0
- data/db/migrate/20140225145441_add_page_to_searches.rb +5 -0
- data/db/schema.rb +31 -0
- data/db/seeds.rb +7 -0
- data/lib/benchmark_logger.rb +2 -0
- data/lib/capistrano/tasks/access_check.cap +10 -0
- data/lib/generators/quick_search/install_generator.rb +49 -0
- data/lib/generators/quick_search/templates/kaminari.rb +5 -0
- data/lib/generators/quick_search/templates/post_install.txt +8 -0
- data/lib/generators/quick_search/templates/quick_search_config.yml +58 -0
- data/lib/quick_search/engine.rb +16 -0
- data/lib/quick_search/version.rb +3 -0
- data/lib/quick_search.rb +12 -0
- data/lib/tasks/best_bets.rake +29 -0
- data/test/controllers/appstats_controller_test.rb +7 -0
- data/test/controllers/concerns/doi_trap_test.rb +19 -0
- data/test/controllers/concerns/on_campus_test.rb +19 -0
- data/test/controllers/concerns/query_parser_test.rb +47 -0
- data/test/controllers/opensearch_controller_test.rb +13 -0
- data/test/controllers/pages_controller_test.rb +7 -0
- data/test/controllers/search_controller_test.rb +15 -0
- data/test/fixtures/events.yml +11 -0
- data/test/fixtures/searches.yml +7 -0
- data/test/helpers/appstats_helper_test.rb +4 -0
- data/test/helpers/opensearch_helper_test.rb +4 -0
- data/test/helpers/pages_helper_test.rb +4 -0
- data/test/helpers/search_helper_test.rb +4 -0
- data/test/integration/catalog_search_test.rb +8 -0
- data/test/integration/google_analytics_test.rb +23 -0
- data/test/integration/landing_page_test.rb +8 -0
- data/test/integration/searching_test.rb +40 -0
- data/test/integration/smart_subjects_test.rb +8 -0
- data/test/integration/website_search_test.rb +8 -0
- data/test/models/event_test.rb +7 -0
- data/test/models/search_test.rb +7 -0
- data/test/searcher/best_bets_test.rb +35 -0
- data/test/searcher/catalog_test.rb +35 -0
- data/test/searcher/ematrix_database_test.rb +27 -0
- data/test/searcher/ematrix_journal_test.rb +27 -0
- data/test/searcher/faq_test.rb +27 -0
- data/test/searcher/smart_subjects_test.rb +27 -0
- data/test/searcher/summon_test.rb +13 -0
- data/test/searcher/website_test.rb +34 -0
- data/test/support/vcr_test.rb +40 -0
- data/test/test_helper.rb +42 -0
- metadata +366 -0
@@ -0,0 +1,210 @@
|
|
1
|
+
module QuickSearch
|
2
|
+
class AppstatsController < ApplicationController
|
3
|
+
include Auth
|
4
|
+
|
5
|
+
before_action :auth, :start_date, :end_date, :days_in_sample
|
6
|
+
|
7
|
+
def index
|
8
|
+
@page_title = 'Search Statistics'
|
9
|
+
search_click_ratio
|
10
|
+
end
|
11
|
+
|
12
|
+
def clicks_overview
|
13
|
+
@page_title = 'Clicks Overview'
|
14
|
+
module_clicks
|
15
|
+
result_types_clicks
|
16
|
+
typeahead_clicks
|
17
|
+
end
|
18
|
+
|
19
|
+
def top_searches
|
20
|
+
@page_title = 'Top Searches'
|
21
|
+
searches_count
|
22
|
+
searches = Search.where(page: 'index').where(date_range)
|
23
|
+
queries = []
|
24
|
+
searches.each do |search|
|
25
|
+
queries << search["query"].downcase
|
26
|
+
end
|
27
|
+
@query_counts = Hash.new(0)
|
28
|
+
queries.each do |query|
|
29
|
+
@query_counts[query] += 1
|
30
|
+
end
|
31
|
+
@query_counts = @query_counts.sort_by {|k,v| v}.reverse[0..199]
|
32
|
+
end
|
33
|
+
|
34
|
+
def top_spot
|
35
|
+
@page_title = params[:ga_top_spot_module]
|
36
|
+
top_spot_reporting(params[:ga_top_spot_module])
|
37
|
+
end
|
38
|
+
|
39
|
+
def detail
|
40
|
+
start_date
|
41
|
+
end_date
|
42
|
+
@category = params[:ga_category]
|
43
|
+
if params[:ga_scope] == 'action'
|
44
|
+
@action = params[:ga_action]
|
45
|
+
@page_title = "#{@category} (#{URI.decode(@action)})"
|
46
|
+
top_spot_detail(@category, @action)
|
47
|
+
render "top_spot_detail"
|
48
|
+
elsif params[:ga_scope] == 'category'
|
49
|
+
@page_title = "#{@category}"
|
50
|
+
module_click_detail(@category)
|
51
|
+
render "module_click_detail"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def realtime
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def module_clicks
|
61
|
+
@module_clicks = Event.where(date_range).where(excluded_categories).group(:category).order("count_category DESC").count(:category)
|
62
|
+
@module_click_total = calculate_total_clicks(@module_clicks)
|
63
|
+
end
|
64
|
+
|
65
|
+
def typeahead_clicks
|
66
|
+
searches_count
|
67
|
+
@typeahead_clicks = Event.where(date_range).where("category LIKE 'typeahead-%'").group(:category).order("count_category DESC").count(:category)
|
68
|
+
@typeahead_clicks_total = calculate_total_clicks(@typeahead_clicks)
|
69
|
+
end
|
70
|
+
|
71
|
+
def result_types_clicks
|
72
|
+
@result_types_clicks = Event.where(date_range).where(:category => "result-types").group(:action).order("count_action DESC").count(:action)
|
73
|
+
@result_types_click_total = calculate_total_clicks(@result_types_clicks)
|
74
|
+
end
|
75
|
+
|
76
|
+
def calculate_total_clicks(clicks)
|
77
|
+
click_total = 0
|
78
|
+
clicks.each do |module_name, click_count|
|
79
|
+
click_total += click_count
|
80
|
+
end
|
81
|
+
|
82
|
+
click_total
|
83
|
+
end
|
84
|
+
|
85
|
+
def searches_count
|
86
|
+
@searches_count = Search.where(page: 'index').where(date_range).count
|
87
|
+
end
|
88
|
+
|
89
|
+
def search_click_ratio
|
90
|
+
searches_count
|
91
|
+
@clicks_count = Event.where(excluded_categories).where(date_range).count
|
92
|
+
@click_serve_ratio = (@clicks_count / @searches_count.to_f)*100
|
93
|
+
end
|
94
|
+
|
95
|
+
def top_spot_reporting(top_spot_module)
|
96
|
+
reports = {"best-bets-regular" => {:category_title => "Best Bets Regular"},
|
97
|
+
"best-bets-algorithmic-journal" => {:category_title => "Journal Algorithmic Best Bets"},
|
98
|
+
"best-bets-algorithmic-database" => {:category_title => "Database Algorithmic Best Bets"},
|
99
|
+
"best-bets-course-tools" => {:category_title => "Course Tools Best Bets"},
|
100
|
+
"spelling-suggestion" => {:category_title => "Spelling Suggestions"}
|
101
|
+
}
|
102
|
+
@top_spot_reporting = []
|
103
|
+
top_spot_report = base_query(top_spot_module)
|
104
|
+
@top_spot_reporting << {:category => top_spot_module, :category_title => reports[top_spot_module][:category_title], :report => top_spot_report}
|
105
|
+
# reports.each do |key, report|
|
106
|
+
# top_spot_report = base_query(key)
|
107
|
+
# @top_spot_reporting << {:category => key, :category_title => report[:category_title], :report => top_spot_report}
|
108
|
+
# end
|
109
|
+
end
|
110
|
+
|
111
|
+
def module_click_detail(category)
|
112
|
+
modules_clicks = Event.where(:category => "#{category}").where(date_range).group(:action).order('count_category DESC').count(:category)
|
113
|
+
total_clicks = modules_clicks.values.sum
|
114
|
+
@modules_clicks_report = []
|
115
|
+
modules_clicks.each do |module_clicks|
|
116
|
+
@modules_clicks_report << {:module => module_clicks[0], :clicks => module_clicks[1], :percent => (module_clicks[1]/total_clicks.to_f)*100}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def top_spot_detail(category, action)
|
121
|
+
# Use a join here?
|
122
|
+
action = URI.decode(action)
|
123
|
+
serves = Event.where(:category => "#{category}-serve", :action => "#{action}").where(date_range).group(:label).order("count_label DESC").count(:label)
|
124
|
+
clicks = Event.where(:category => "#{category}", :action => "#{action}").where(date_range).group(:label).order("count_label DESC").count(:label)
|
125
|
+
|
126
|
+
@top_spot_detail = {}
|
127
|
+
|
128
|
+
|
129
|
+
serves.each do |label|
|
130
|
+
serve_count = label[1].nil? ? 0:label[1]
|
131
|
+
click_count = clicks[label[0].downcase].nil? ? 0:clicks[label[0].downcase]
|
132
|
+
@top_spot_detail[label[0]] = [serve_count, click_count, (click_count/serve_count.to_f)*100]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def base_query(category)
|
137
|
+
serves = Event.where(date_range).where(:category => "#{category}-serve").group(:action).order("count_category DESC").count(:category)
|
138
|
+
clicks = Event.where(date_range).where(:category => "#{category}").group(:action).count(:category)
|
139
|
+
|
140
|
+
result = []
|
141
|
+
serves.each do |data, count|
|
142
|
+
unless clicks[data].nil?
|
143
|
+
click_count = clicks[data]
|
144
|
+
else
|
145
|
+
click_count = 0
|
146
|
+
end
|
147
|
+
|
148
|
+
row = {"action" => data,
|
149
|
+
"servecount" => count,
|
150
|
+
"clickcount" => click_count,
|
151
|
+
"click_serve_ratio" => (click_count/count)*100 }
|
152
|
+
result << row
|
153
|
+
end
|
154
|
+
|
155
|
+
result
|
156
|
+
end
|
157
|
+
|
158
|
+
def start_date
|
159
|
+
if params[:start_date]
|
160
|
+
# If there's a specified start date, use it
|
161
|
+
@start_date = convert_to_time(params[:start_date])
|
162
|
+
else
|
163
|
+
# otherwise use 6 months ago as a default, or the earliest event date if logs are younger
|
164
|
+
first_date = Event.first[:created_at]
|
165
|
+
start = Time.current - 6.months
|
166
|
+
|
167
|
+
if start < first_date
|
168
|
+
@start_date = first_date
|
169
|
+
else
|
170
|
+
@start_date = start
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
def end_date
|
177
|
+
if params[:end_date]
|
178
|
+
# use end date, if specified
|
179
|
+
@end_date = convert_to_time(params[:end_date])
|
180
|
+
else
|
181
|
+
# otherwise use the current date as default
|
182
|
+
@end_date = Time.current
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def convert_to_time(date_input)
|
187
|
+
year = date_input[:year]
|
188
|
+
month = date_input[:month]
|
189
|
+
day = date_input[:day]
|
190
|
+
|
191
|
+
Time.new(year, month, day)
|
192
|
+
end
|
193
|
+
|
194
|
+
def days_in_sample
|
195
|
+
@days_in_sample = ((@end_date - @start_date) / (24*60*60)).round
|
196
|
+
if @days_in_sample < 1
|
197
|
+
@days_in_sample = 1
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def excluded_categories
|
202
|
+
"category NOT LIKE \"%serve\" AND category <> \"common-searches\" AND category <> \"result-types\"AND category NOT LIKE \"typeahead-%\""
|
203
|
+
end
|
204
|
+
|
205
|
+
def date_range
|
206
|
+
{ :created_at => start_date.beginning_of_day..end_date.end_of_day }
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,250 @@
|
|
1
|
+
module QuickSearch
|
2
|
+
class SearchController < ApplicationController
|
3
|
+
include QuickSearch::SearcherConcern
|
4
|
+
include QuickSearch::DoiTrap
|
5
|
+
include QuickSearch::OnCampus
|
6
|
+
include QuickSearch::QueryParser
|
7
|
+
include QuickSearch::EncodeUtf8
|
8
|
+
include QuickSearch::QueryFilter
|
9
|
+
include QuickSearch::SearcherConfig
|
10
|
+
|
11
|
+
require 'benchmark_logger'
|
12
|
+
|
13
|
+
before_filter :doi_trap, :log_query
|
14
|
+
after_filter :realtime_message, only: [:index]
|
15
|
+
protect_from_forgery except: :log_event
|
16
|
+
|
17
|
+
def index
|
18
|
+
loaded_searches
|
19
|
+
@common_searches = common_searches
|
20
|
+
http_search
|
21
|
+
end
|
22
|
+
|
23
|
+
# TODO: throw error if required files not in place
|
24
|
+
def single_searcher
|
25
|
+
searcher_name = params[:searcher_name]
|
26
|
+
|
27
|
+
searcher_cfg = searcher_config(searcher_name)
|
28
|
+
if searcher_cfg and searcher_cfg.has_key? 'loaded_searches'
|
29
|
+
additional_services = Array.new(searcher_cfg['loaded_searches'])
|
30
|
+
else
|
31
|
+
additional_services = []
|
32
|
+
end
|
33
|
+
loaded_searches(additional_services)
|
34
|
+
@common_searches = searcher_cfg['common_searches'] || []
|
35
|
+
#TODO: maybe a default template for single-searcher searches?
|
36
|
+
http_search(searcher_name, "quick_search/search/#{searcher_name}_search")
|
37
|
+
end
|
38
|
+
|
39
|
+
# The following searches for individual sections of the page.
|
40
|
+
# This allows us to do client-side requests in cases where the original server-side
|
41
|
+
# request times out or otherwise fails.
|
42
|
+
def xhr_search
|
43
|
+
endpoint = params[:endpoint]
|
44
|
+
|
45
|
+
if params[:template] == 'with_paging'
|
46
|
+
template = 'xhr_response_with_paging'
|
47
|
+
else
|
48
|
+
template = 'xhr_response'
|
49
|
+
end
|
50
|
+
|
51
|
+
@query = params_q_scrubbed
|
52
|
+
@page = page
|
53
|
+
@per_page = per_page(endpoint)
|
54
|
+
@offset = offset(@page,@per_page)
|
55
|
+
|
56
|
+
http_client = HTTPClient.new
|
57
|
+
update_searcher_timeout(http_client, endpoint, true)
|
58
|
+
|
59
|
+
benchmark "%s xhr #{endpoint}" % CGI.escape(@query.to_str) do
|
60
|
+
|
61
|
+
klass = "QuickSearch::#{endpoint.camelize}Searcher".constantize
|
62
|
+
searcher = klass.new(http_client,
|
63
|
+
extracted_query(params_q_scrubbed),
|
64
|
+
@per_page,
|
65
|
+
@offset,
|
66
|
+
@page,
|
67
|
+
on_campus?(ip),
|
68
|
+
extracted_scope(params_q_scrubbed))
|
69
|
+
searcher.search
|
70
|
+
|
71
|
+
searcher_partials = {}
|
72
|
+
searcher_cfg = searcher_config(endpoint)
|
73
|
+
unless searcher_cfg.blank?
|
74
|
+
services = searcher_cfg['services'].blank? ? [] : searcher_cfg['services']
|
75
|
+
else
|
76
|
+
services = []
|
77
|
+
end
|
78
|
+
services << endpoint
|
79
|
+
|
80
|
+
respond_to do |format|
|
81
|
+
|
82
|
+
format.html {
|
83
|
+
services.each do |service|
|
84
|
+
service_template = render_to_string(
|
85
|
+
:partial => "quick_search/search/#{template}",
|
86
|
+
:layout => false,
|
87
|
+
:locals => { module_display_name: t("#{endpoint}_search.display_name"),
|
88
|
+
searcher: searcher,
|
89
|
+
search: '',
|
90
|
+
service_name: service
|
91
|
+
})
|
92
|
+
searcher_partials[service] = service_template
|
93
|
+
end
|
94
|
+
render :json => searcher_partials
|
95
|
+
}
|
96
|
+
|
97
|
+
format.json {
|
98
|
+
|
99
|
+
# prevents openstruct object from results being nested inside tables
|
100
|
+
# See: http://stackoverflow.com/questions/7835047/collecting-hashes-into-openstruct-creates-table-entry
|
101
|
+
result_list = []
|
102
|
+
searcher.results.each do |result|
|
103
|
+
result_list << result.to_h
|
104
|
+
end
|
105
|
+
|
106
|
+
render :json => { :endpoint => endpoint,
|
107
|
+
:per_page => @per_page.to_s,
|
108
|
+
:page => @page.to_s,
|
109
|
+
:total => searcher.total,
|
110
|
+
:results => result_list
|
111
|
+
}
|
112
|
+
}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def log_search
|
118
|
+
if params[:query].present? && params[:page].present?
|
119
|
+
Search.create(query: params[:query], page: params[:page])
|
120
|
+
render :nothing => true, :status => 200, :content_type => 'text/html'
|
121
|
+
else
|
122
|
+
render :nothing => true, :status => 500, :content_type => 'text/html'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def log_event
|
127
|
+
if params[:category].present? && params[:event_action].present? && params[:label].present?
|
128
|
+
Event.create(category: params[:category], action: params[:event_action], label: params[:label][0..250])
|
129
|
+
if params[:callback].present?
|
130
|
+
render :nothing => true, :status => 200, :content_type => 'text/javascript'
|
131
|
+
else
|
132
|
+
render :nothing => true, :status => 200, :content_type => 'text/html'
|
133
|
+
end
|
134
|
+
else
|
135
|
+
render :nothing => true, :status => 500, :content_type => 'text/html'
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def http_search(endpoint = 'defaults', page_to_render = :index)
|
142
|
+
@ip = request.remote_ip
|
143
|
+
|
144
|
+
@search_form_placeholder = I18n.t "#{endpoint}_search.search_form_placeholder"
|
145
|
+
@page_title = I18n.t "#{endpoint}_search.display_name"
|
146
|
+
@module_callout = I18n.t "#{endpoint}_search.module_callout"
|
147
|
+
|
148
|
+
if search_in_params?
|
149
|
+
@query = params_q_scrubbed
|
150
|
+
@search_in_params = true
|
151
|
+
search_all_in_threads(endpoint)
|
152
|
+
#log_search(@query, page_to_render)
|
153
|
+
render page_to_render
|
154
|
+
else
|
155
|
+
@search_in_params = false
|
156
|
+
render '/quick_search/pages/home'
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def page
|
161
|
+
if page_in_params?
|
162
|
+
page = params[:page].to_i
|
163
|
+
else
|
164
|
+
page = 1
|
165
|
+
end
|
166
|
+
page
|
167
|
+
end
|
168
|
+
helper_method :page
|
169
|
+
|
170
|
+
def per_page(endpoint)
|
171
|
+
searcher_cfg = searcher_config(endpoint)
|
172
|
+
if params[:per_page]
|
173
|
+
per_page = params[:per_page].to_i
|
174
|
+
elsif params[:template] == 'with_paging'
|
175
|
+
if searcher_cfg and searcher_cfg.has_key? 'with_paging'
|
176
|
+
per_page = searcher_cfg['with_paging']['per_page']
|
177
|
+
else
|
178
|
+
per_page = 10
|
179
|
+
end
|
180
|
+
else
|
181
|
+
per_page = QuickSearch::Engine::APP_CONFIG['per_page']
|
182
|
+
end
|
183
|
+
|
184
|
+
if per_page > QuickSearch::Engine::APP_CONFIG['max_per_page']
|
185
|
+
per_page = QuickSearch::Engine::APP_CONFIG['max_per_page']
|
186
|
+
end
|
187
|
+
|
188
|
+
per_page
|
189
|
+
end
|
190
|
+
|
191
|
+
def offset(page, per_page)
|
192
|
+
(page * per_page) - per_page
|
193
|
+
end
|
194
|
+
|
195
|
+
def page_in_params?
|
196
|
+
params[:page] && !params[:page].blank?
|
197
|
+
end
|
198
|
+
|
199
|
+
def search_in_params?
|
200
|
+
params_q_scrubbed && !params_q_scrubbed.blank?
|
201
|
+
end
|
202
|
+
helper_method :search_in_params?
|
203
|
+
|
204
|
+
def common_searches
|
205
|
+
QuickSearch::Engine::APP_CONFIG['common_searches']
|
206
|
+
end
|
207
|
+
|
208
|
+
def loaded_searches(additional_services=[])
|
209
|
+
@search_services_for_display = []
|
210
|
+
@extracted_query = extracted_query(params_q_scrubbed)
|
211
|
+
search_services = additional_services + Array.new(QuickSearch::Engine::APP_CONFIG['loaded_searches'])
|
212
|
+
|
213
|
+
search_services.each do |search_service|
|
214
|
+
if search_in_params?
|
215
|
+
@search_services_for_display << {'name' => search_service['name'], 'link'=> search_service['query'] + extracted_query(params_q_scrubbed)}
|
216
|
+
else
|
217
|
+
@search_services_for_display << {'name' => search_service['name'], 'link'=> search_service['landing_page']}
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def realtime_message
|
223
|
+
if base_url = QuickSearch::Engine::APP_CONFIG['realtime_url']
|
224
|
+
begin
|
225
|
+
client = HTTPClient.new
|
226
|
+
body = {q: params_q_scrubbed}
|
227
|
+
url = File.join(base_url, "/message/quicksearch-#{Rails.env}")
|
228
|
+
res = client.post(url, body)
|
229
|
+
rescue
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def benchmark(message)
|
235
|
+
result = nil
|
236
|
+
ms = Benchmark.ms { result = yield }
|
237
|
+
BenchmarkLogger.info '%s (%.1fms)' % [ message, ms ]
|
238
|
+
result
|
239
|
+
end
|
240
|
+
|
241
|
+
def log_query
|
242
|
+
if search_in_params?
|
243
|
+
@log_query = filter_query(params_q_scrubbed)
|
244
|
+
else
|
245
|
+
@log_query = ""
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
end
|
250
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module QuickSearch::ApplicationHelper
|
2
|
+
include QuickSearch::EncodeUtf8
|
3
|
+
|
4
|
+
def application_name
|
5
|
+
if action_name == "website"
|
6
|
+
I18n.t "website_search.title_name"
|
7
|
+
else
|
8
|
+
I18n.t "defaults_search.title_name"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def organization_name
|
13
|
+
I18n.t 'organization_name'
|
14
|
+
end
|
15
|
+
|
16
|
+
def sanitized_query
|
17
|
+
params_q_scrubbed.nil? || params_q_scrubbed == "" ? "" : sanitize(params_q_scrubbed)
|
18
|
+
end
|
19
|
+
|
20
|
+
def display_query
|
21
|
+
params_q_scrubbed.nil? || params_q_scrubbed == "" ? "" : truncate(params_q_scrubbed, length: 40, separator: ' ')
|
22
|
+
end
|
23
|
+
|
24
|
+
def log_query
|
25
|
+
params_q_scrubbed.nil? || params_q_scrubbed == "" ? "" : truncate(sanitize(params_q_scrubbed), length: 100, separator: ' ')
|
26
|
+
end
|
27
|
+
|
28
|
+
def title(page_title = nil)
|
29
|
+
query = params_q_scrubbed.nil? || params_q_scrubbed == "" ? "" : truncate(params_q_scrubbed, length: 40, separator: ' ', escape: false)
|
30
|
+
page_title ||= ''
|
31
|
+
page_title << "#{query} |" unless query.blank?
|
32
|
+
page_title << " #{application_name} |" unless application_name.blank?
|
33
|
+
page_title << " #{organization_name}" unless organization_name.blank?
|
34
|
+
|
35
|
+
content_for :title, page_title
|
36
|
+
end
|
37
|
+
|
38
|
+
# FIXME this should use en definitions
|
39
|
+
def pages(page)
|
40
|
+
format_pages ||= ''
|
41
|
+
if page.count('-') > 0
|
42
|
+
format_pages << "pp. "
|
43
|
+
else
|
44
|
+
format_pages << "p. "
|
45
|
+
end
|
46
|
+
format_pages << page
|
47
|
+
end
|
48
|
+
|
49
|
+
def render_module(searcher, service_name, partial_name = '/quick_search/search/module', per_page = 3)
|
50
|
+
render partial: partial_name , locals: { module_display_name: t("#{service_name}_search.display_name"), searcher: searcher, search: '', service_name: service_name, per_page: per_page }
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_best_bets_data(best_bets_searcher)
|
54
|
+
best_bets = {}
|
55
|
+
best_bets[:id] = best_bets_searcher.results.first.id
|
56
|
+
best_bets[:title] = best_bets_searcher.results.first.title
|
57
|
+
best_bets[:link] = best_bets_searcher.results.first.link
|
58
|
+
best_bets[:description] = best_bets_searcher.results.first.description
|
59
|
+
best_bets[:best_bets_type] = best_bets_searcher.results.first.best_bets_type
|
60
|
+
|
61
|
+
best_bets
|
62
|
+
end
|
63
|
+
|
64
|
+
def body_class
|
65
|
+
[controller_name, action_name].join('-')
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
data/app/models/event.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
class Search < ActiveRecord::Base
|
2
|
+
|
3
|
+
def self.get_most_frequent_searches
|
4
|
+
frequent_searches = []
|
5
|
+
Search.all(:select => "query, count(query) as query_count", :group => "query", :order => "query_count DESC", :limit => 7).each do |row|
|
6
|
+
frequent_searches << row[:query]
|
7
|
+
end
|
8
|
+
frequent_searches
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module QuickSearch
|
2
|
+
class BestBetsSearcher < QuickSearch::Searcher
|
3
|
+
|
4
|
+
def search
|
5
|
+
search_best_bets_index
|
6
|
+
end
|
7
|
+
|
8
|
+
def results
|
9
|
+
if results_list
|
10
|
+
results_list
|
11
|
+
else
|
12
|
+
@results_list = []
|
13
|
+
unless @response.blank?
|
14
|
+
@results_list << @response
|
15
|
+
end
|
16
|
+
@results_list
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def total
|
21
|
+
results.size
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Regular Best Bets
|
27
|
+
|
28
|
+
def search_best_bets_index
|
29
|
+
response = @http.get(query_url)
|
30
|
+
parsed_response = JSON.parse(response.body)
|
31
|
+
if parsed_response['response']['numFound'].to_s == '0'
|
32
|
+
return nil
|
33
|
+
else
|
34
|
+
resp = parsed_response['response']['docs'][0]
|
35
|
+
result = OpenStruct.new
|
36
|
+
result.title = title(resp)
|
37
|
+
result.link = link(resp)
|
38
|
+
result.id = id(resp)
|
39
|
+
result.description = description(resp)
|
40
|
+
result.best_bets_type = 'best-bets-regular'
|
41
|
+
|
42
|
+
@response = result
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def base_url
|
47
|
+
QuickSearch::Engine::APP_CONFIG['best_bets']['solr_url']
|
48
|
+
end
|
49
|
+
|
50
|
+
def query_url
|
51
|
+
if http_request_queries['not_escaped'].split.size < 3
|
52
|
+
# use non-analyzed keyword field with phrased search
|
53
|
+
params = {
|
54
|
+
'q' => %Q(keywords: "#{query_quotes_stripped}")
|
55
|
+
}
|
56
|
+
else
|
57
|
+
# use DisMax query parser for longer queries
|
58
|
+
params = {
|
59
|
+
'defType' => 'dismax',
|
60
|
+
'q' => query_quotes_stripped
|
61
|
+
}
|
62
|
+
if http_request_queries['not_escaped'].split.size < 6
|
63
|
+
params['mm'] = '4'
|
64
|
+
else
|
65
|
+
params['mm'] = '6'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# return results in json
|
70
|
+
params['wt'] = 'json'
|
71
|
+
base_url + params.to_query
|
72
|
+
end
|
73
|
+
|
74
|
+
def title(value)
|
75
|
+
value['title']
|
76
|
+
end
|
77
|
+
|
78
|
+
def link(value)
|
79
|
+
# TODO: should we make all URLS in the best_bets index absolute? currently some begin with a /
|
80
|
+
if value['url'].start_with?('/')
|
81
|
+
QuickSearch::Engine::APP_CONFIG['website']['website_base_url'] + value['url']
|
82
|
+
else
|
83
|
+
value['url']
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def id(value)
|
88
|
+
value['id']
|
89
|
+
end
|
90
|
+
|
91
|
+
def description(value)
|
92
|
+
value['description']
|
93
|
+
end
|
94
|
+
|
95
|
+
# General Methods
|
96
|
+
|
97
|
+
def query_quotes_stripped
|
98
|
+
http_request_queries['not_escaped'].gsub('"', '')
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|