-
Notifications
You must be signed in to change notification settings - Fork 211
Reuters tutorial: step 7
- Reuters tutorial
- Step 1: Talk to Solr
- Step 2: Add a results widget
- Step 3: Add a pager widget
- Step 4: Add a tagcloud widget
- Step 5: Display the current filters
- Step 6: Add a free-text widget
- Step 7: Add an autocomplete widget
- Step 8: Add a map widget
- Step 9: Add a calendar widget
- Step 10: Extra credit
We’ll now replace the free-text widget we created in the last step with a widget that can also do auto-completion on the facet values of the topics, organizations, and exchanges facet fields.
Create a new widget, AutocompleteWidget.js, inheriting from AbstractTextWidget, like in the last step:
(function ($) { AjaxSolr.AutocompleteWidget = AjaxSolr.AbstractTextWidget.extend({ }); })(jQuery);
In index.html, replace:
<script src="widgets/TextWidget.js"></script>
With:
<script src="widgets/AutocompleteWidget.js"></script>
And, in reuters.js, replace:
Manager.addWidget(new AjaxSolr.TextWidget({ id: 'text', target: '#search' }));
With:
Manager.addWidget(new AjaxSolr.AutocompleteWidget({ id: 'text', target: '#search', fields: [ 'topics', 'organisations', 'exchanges' ] }));
Note: If you are using your own Solr instance, you may want to change the values assigned to the fields keys.
The autocompletion widget will take a custom fields parameter, listing the facet fields on which to perform auto-completion. By not hard-coding these facet fields, we make the widget re-usable.
Implement the abstract method afterRequest, like so:
afterRequest: function () { $(this.target).find('input').unbind().removeData('events').val(''); var self = this; var list = []; for (var i = 0; i < this.fields.length; i++) { var field = this.fields[i]; for (var facet in this.manager.response.facet_counts.facet_fields[field]) { list.push({ field: field, value: facet, label: facet + ' (' + this.manager.response.facet_counts.facet_fields[field][facet] + ') - ' + field }); } } this.requestSent = false; $(this.target).find('input').autocomplete('destroy').autocomplete({ source: list, select: function(event, ui) { if (ui.item) { self.requestSent = true; if (self.manager.store.addByValue('fq', ui.item.field + ':' + AjaxSolr.Parameter.escapeValue(ui.item.value))) { self.doRequest(); } } } }); // This has lower priority so that requestSent is set. $(this.target).find('input').bind('keydown', function(e) { if (self.requestSent === false && e.which == 13) { var value = $(this).val(); if (value && self.set(value)) { self.doRequest(); } } }); }
There is actually very little going on here specific to your understanding of AJAX Solr. We are again using the ParameterStore addByValue and set API methods, as we did in the results and free-text widgets, respectively. The rest is jQuery UI Autocomplete plugin implementation. Technical note: We must autocomplete(‘destroy’) to prevent a memory leak. autocomplete(‘destroy’) removes keydown bindings, so we must bind keydown in afterRequest instead of init. autocomplete(‘destroy’) doesn’t remove all bindings, however, so we must call unbind and removeData(‘events’) to avoid having duplicate bindings.
A limitation is that it will only do auto-completion for the facet values that fall within the facet.limit for the given facet fields. For example, it will only do auto-completion for twenty of the facet values in the exchanges facet field, because we set facet.limit to 20 in reuters.js. If we want to do auto-completion for all facet values, we will need to retrieve them in a separate Solr request. (As of Solr 1.5, we would be able to retrieve everything in a single request.)
First, wrap the code after var self = this;
in a callback function, replacing all occurrences of this, in which this refers to the widget, with self, because this will not refer to the widget instance inside the callback function. Then, replace all occurrences of this.manager.response with response. The callback function will take response as an argument, which will contain the Solr response we will request in a moment.
afterRequest: function () { $(this.target).find('input').unbind().removeData('events').val(''); var self = this; var callback = function (response) { var list = []; for (var i = 0; i < self.fields.length; i++) { var field = self.fields[i]; for (var facet in response.facet_counts.facet_fields[field]) { list.push({ field: field, value: facet, label: facet + ' (' + response.facet_counts.facet_fields[field][facet] + ') - ' + field }); } } self.requestSent = false; $(self.target).find('input').autocomplete('destroy').autocomplete({ source: list, select: function(event, ui) { if (ui.item) { self.requestSent = true; if (self.manager.store.addByValue('fq', ui.item.field + ':' + AjaxSolr.Parameter.escapeValue(ui.item.value))) { self.doRequest(); } } } }); // This has lower priority so that requestSent is set. $(self.target).find('input').bind('keydown', function(e) { if (self.requestSent === false && e.which == 13) { var value = $(this).val(); if (value && self.set(value)) { self.doRequest(); } } }); } // end callback }
The only important change we’ve made so far is to change the code to inspect a different Solr response than the one stored in this.manager.response. Before we send a new request to Solr, we must first build the list of parameters to send to Solr. Add the following code after the callback function:
var params = [ 'rows=0&facet=true&facet.limit=-1&facet.mincount=1&json.nl=map' ]; for (var i = 0; i < this.fields.length; i++) { params.push('facet.field=' + this.fields[i]); }
These parameters should be familiar; they are a subset of the parameters we set in reuters.js, with an important difference – we’ve set facet.limit to a negative value, so that Solr returns all facet values.
You probably want to limit the auto-completion suggestions according to the current filters and query. To do this, add:
var values = this.manager.store.values('fq'); for (var i = 0; i < values.length; i++) { params.push('fq=' + encodeURIComponent(values[i])); } params.push('q=' + this.manager.store.get('q').val());
We now borrow some code from managers/Manager.jquery.js to send the request to Solr:
$.getJSON(this.manager.solrUrl + '?' + params.join('&') + '&wt=json&json.wrf=?', {}, callback);
We can now do auto-completion for all facet values!
To support sending the request through a proxy, we would have borrowed more code from Manager.jquery.js.
OK, now let’s get fancy and add a map widget.