Skip to content

Commit

Permalink
add autocomplete feature for Blacklight using Solr suggestor
Browse files Browse the repository at this point in the history
  • Loading branch information
mejackreed committed Nov 5, 2015
1 parent 89b5757 commit f5f6216
Show file tree
Hide file tree
Showing 14 changed files with 273 additions and 1 deletion.
31 changes: 31 additions & 0 deletions app/assets/javascripts/blacklight/autocomplete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*global Bloodhound */

Blacklight.onLoad(function() {
'use strict';

$('[data-autocomplete-enabled="true"]').each(function() {
var $el = $(this);
var suggestUrl = $el.data().autocompletePath;

var terms = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: suggestUrl + '?q=%QUERY'
}
});

terms.initialize();

$el.typeahead({
hint: true,
highlight: true,
minLength: 2
},
{
name: 'terms',
displayKey: 'term',
source: terms.ttAdapter()
});
});
});
15 changes: 15 additions & 0 deletions app/controllers/concerns/blacklight/suggest.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Blacklight
module Suggest
extend ActiveSupport::Concern
include Blacklight::Suggest::SuggestHelper

def index
@response = get_suggestions params
respond_to do |format|
format.json do
render json: @response.suggestions
end
end
end
end
end
32 changes: 32 additions & 0 deletions app/controllers/concerns/blacklight/suggest/suggest_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module Blacklight
module Suggest
module SuggestHelper
extend ActiveSupport::Concern
include Blacklight::SearchHelper

##
# For now, only use the q parameter to create a
# Blacklight::Suggest::Response
# @param [Hash] params
# @return [Earthworks::Suggest::Response]
def get_suggestions(params)
request_params = { q: params[:q] }
Blacklight::Suggest::Response.new suggest_results(request_params), request_params, suggest_handler_path
end

##
# Query the suggest handler using RSolr::Client::send_and_receive
# @param [Hash] request_params
# @return [RSolr::HashWithResponse]
def suggest_results(request_params)
repository.connection.send_and_receive(suggest_handler_path, params: request_params)
end

##
# @param [String]
def suggest_handler_path
repository.blacklight_config.autocomplete_path
end
end
end
end
3 changes: 3 additions & 0 deletions app/controllers/suggest_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class SuggestController < ApplicationController
include Blacklight::Suggest
end
10 changes: 10 additions & 0 deletions app/helpers/blacklight/suggest_helper_behavior.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module Blacklight
module SuggestHelperBehavior
##
# @return [Boolean] should autocomplete be enabled in the UI
def autocomplete_enabled?
blacklight_config.autocomplete_enabled.present? &&
blacklight_config.autocomplete_path.present?
end
end
end
3 changes: 3 additions & 0 deletions app/helpers/suggest_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module SuggestHelper
include Blacklight::SuggestHelperBehavior
end
26 changes: 26 additions & 0 deletions app/models/concerns/blacklight/suggest/response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module Blacklight
module Suggest
class Response
attr_reader :response, :request_params, :suggest_path

##
# Creates a suggest response
# @param [RSolr::HashWithResponse] response
# @param [Hash] request_params
# @param [String] suggest_path
def initialize(response, request_params, suggest_path)
@response = response
@request_params = request_params
@suggest_path = suggest_path
end

##
# Trys the suggestor response to return suggestions if they are
# present
# @return [Array]
def suggestions
response.try(:[], suggest_path).try(:[], 'mySuggester').try(:[], request_params[:q]).try(:[], 'suggestions') || []
end
end
end
end
2 changes: 1 addition & 1 deletion app/views/catalog/_search_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<% end %>

<label for="q" class="sr-only"><%= t('blacklight.search.form.search.label') %></label>
<%= text_field_tag :q, params[:q], placeholder: t('blacklight.search.form.search.placeholder'), class: "search_q q form-control", id: "q", autofocus: should_autofocus_on_search_box? %>
<%= text_field_tag :q, params[:q], placeholder: t('blacklight.search.form.search.placeholder'), class: "search_q q form-control", id: "q", autofocus: should_autofocus_on_search_box?, data: { autocomplete_enabled: autocomplete_enabled?, autocomplete_path: blacklight.suggest_index_path } %>

<span class="input-group-btn">
<button type="submit" class="btn btn-primary search-btn" id="search">
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
put "saved_searches/save/:id", :to => "saved_searches#save", :as => "save_search"
delete "saved_searches/forget/:id", :to => "saved_searches#forget", :as => "forget_search"
post "saved_searches/forget/:id", :to => "saved_searches#forget"
resources :suggest, only: :index, defaults: { format: 'json' }
end
4 changes: 4 additions & 0 deletions lib/generators/blacklight/templates/catalog_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@ class <%= controller_name.classify %>Controller < ApplicationController
# If there are more than this many search results, no spelling ("did you
# mean") suggestion is offered.
config.spell_max = 5

# Configuration for autocomplete suggestor
config.autocomplete_enabled = true
config.autocomplete_path = 'suggest'
end

end
29 changes: 29 additions & 0 deletions spec/controllers/blacklight/suggest/suggest_helper_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require 'spec_helper'

describe Blacklight::Suggest::SuggestHelper do
let(:controller) { (Class.new(ApplicationController) { include Blacklight::Suggest::SuggestHelper }).new }
subject { controller}

let(:blacklight_config) { Blacklight::Configuration.new }
let(:blacklight_solr) { RSolr.connect(Blacklight.connection_config) }

before do
blacklight_config.autocomplete_path = 'suggest'
end

describe '#get_suggestions' do
it 'returns a Blacklight::Suggest::Response' do
expect(subject).to receive(:suggest_results).with(q: 'test')
expect(subject.get_suggestions q: 'test').to be_an Blacklight::Suggest::Response
end
end
describe '#suggest_results' do
it 'queries the suggest handler with params' do
allow(blacklight_solr).to receive(:get) do |path, params|
expect(path).to eq 'suggest'
expect(params).to eq params: { q: 'test' }
end
subject.suggest_results q: 'test'
end
end
end
22 changes: 22 additions & 0 deletions spec/controllers/suggest_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require 'spec_helper'

describe SuggestController do
routes { Blacklight::Engine.routes }
describe 'GET index' do
it 'assigns @response' do
get :index, format: 'json'
expect(assigns(:response)).to be_an Blacklight::Suggest::Response
end
it 'returns JSON' do
get :index, format: 'json'
puts response.body
expect(response.body).to eq [].to_json
end
it 'returns suggestions' do
get :index, format: 'json', q: 'new'
json = JSON.parse(response.body)
expect(json.count).to eq 3
expect(json.first['term']).to eq 'new jersey'
end
end
end
41 changes: 41 additions & 0 deletions spec/helpers/suggest_helper_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require 'spec_helper'

describe SuggestHelper do
before do
allow(helper).to receive(:blacklight_config).and_return(blacklight_config)
end
describe '#autocomplete_enabled?' do
describe 'with autocomplete config' do
let(:blacklight_config) do
Blacklight::Configuration.new.configure do |config|
config.autocomplete_enabled = true
config.autocomplete_path = 'suggest'
end
end
it 'is enabled' do
expect(helper.autocomplete_enabled?).to be true
end
end
describe 'without disabled config' do
let(:blacklight_config) do
Blacklight::Configuration.new.configure do |config|
config.autocomplete_enabled = false
config.autocomplete_path = 'suggest'
end
end
it 'is disabled' do
expect(helper.autocomplete_enabled?).to be false
end
end
describe 'without path config' do
let(:blacklight_config) do
Blacklight::Configuration.new.configure do |config|
config.autocomplete_enabled = true
end
end
it 'is disabled' do
expect(helper.autocomplete_enabled?).to be false
end
end
end
end
55 changes: 55 additions & 0 deletions spec/models/blacklight/suggest/response_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
require 'spec_helper'

describe Blacklight::Suggest::Response do
let(:empty_response) { described_class.new({}, { q: 'hello' }, 'suggest') }
let(:full_response) do
described_class.new(
{
'responseHeader' => {
'status' => 200
},
'suggest' => {
'mySuggester' => {
'new' => {
'numFound' => 3,
'suggestions' => [
{
'term' => 'new jersey',
'weight' => 3,
'payload' => ''
},
{
'term' => 'new jersey bridgeton biography',
'weight' => 3,
'payload' => ''
},
{
'term' => 'new jersey bridgeton history',
'weight' => 3,
'payload' => ''
}
]
}
}
}
},
{
q: 'new'
},
'suggest'
)
end

describe '#initialize' do
it 'creates a Blacklight::Suggest::Response' do
expect(empty_response).to be_an Blacklight::Suggest::Response
end
end
describe '#suggestions' do
it 'returns an array of suggestions' do
expect(full_response.suggestions).to be_an Array
expect(full_response.suggestions.count).to eq 3
expect(full_response.suggestions.first['term']).to eq 'new jersey'
end
end
end

0 comments on commit f5f6216

Please sign in to comment.