Skip to content

Reuters tutorial: step 7

jpmckinney edited this page Sep 13, 2010 · 34 revisions

Table of Contents

We’ll now replace the free-text widget we created in the last step with a widget that can do auto-completion on the facet values of the topics, organizations, and exchanges facet fields. For this, we’ll use the jQuery Autocomplete plugin.

Create a new widget, AutocompleteWidget.js, inheriting from AbstractFacetWidget:

(function ($) {
AjaxSolr.AutocompleteWidget = AjaxSolr.AbstractFacetWidget.extend({
});
})(jQuery);

In index.html, replace:

<script type="text/javascript" src="widgets/TextWidget.js"></script>

With:

<link rel="stylesheet" type="text/css" href="ext/jquery.autocomplete.css" media="screen" />
<script type="text/javascript" src="ext/jquery.autocomplete.js"></script>
<script type="text/javascript" src="widgets/AutocompleteWidget.js"></script>

And, in reuters.js, replace:

Manager.addWidget(new AjaxSolr.TextWidget({
  id: 'text',
  target: '#search',
  field: 'allText'
}));

With:

Manager.addWidget(new AjaxSolr.AutocompleteWidget({
  id: 'text',
  target: '#search',
  field: 'allText',
  fields: [ 'topics', 'organisations', 'exchanges' ]
}));

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').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,
        text: facet + ' (' + this.manager.response.facet_counts.facet_fields[field][facet] + ') - ' + field
      });
    }
  }

  this.requestSent = false;
  $(this.target).find('input').autocomplete(list, {
    formatItem: function(facet) {
      return facet.text;
    }
  }).result(function(e, facet) {
    self.requestSent = true;
    if (self.manager.store.addByValue('fq', facet.field + ':' + facet.value)) {
      self.manager.doRequest(0);
    }
  }).bind('keydown', function(e) {
    if (self.requestSent === false && e.which == 13) {
      if (self.add($(this).val())) {
        self.manager.doRequest(0);
      }
    }
  });
}

There is, actually, very little going on here specific to your understanding of AJAX Solr. We are again using the ParameterStore addByValue and add API methods, as we did in the results widget and free-text widget, respectively. The rest is jQuery Autocomplete plugin implementation.

[What we have so far]

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 of its facet values, we will need to retrieve them in a separate Solr request. We can do so by modifying afterRequest.

First, wrap the code after var self = this; in a callback function, replacing all occurrences of this with self, as this will not refer to the widget instance inside the callback function. The callback function will take response as an argument, which will contain the Solr response we will request in a moment. Replace all occurrences of this.manager.response with response.

afterRequest: function () {
  $(this.target).find('input').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,
          text: facet + ' (' + response.facet_counts.facet_fields[field][facet] + ') - ' + field
        });
      }
    }

    self.requestSent = false;
    $(self.target).find('input').autocomplete(list, {
      formatItem: function(facet) {
        return facet.text;
      }
    }).result(function(e, facet) {
      self.requestSent = true;
      if (self.manager.store.addByValue('fq', facet.field + ':' + facet.value)) {
        self.manager.doRequest(0);
      }
    }).bind('keydown', function(e) {
      if (self.requestSent === false && e.which == 13) {
        if (self.add($(self).val())) {
          self.manager.doRequest(0);
        }
      }
    });
  } // 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 = [ 'q=*:*&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 one important difference – we’ve set facet.limit to a negative value, so that Solr returns all facet values.

We will 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);

To support sending the request to Solr through a proxy, we would have borrowed more code from managers/Manager.jquery.js. For this tutorial, we will keep things simple. We can now do auto-completion for all facet values.

[What we have so far]

OK, now let’s get fancy and add a map widget.

Clone this wiki locally