Skip to content

Reuters tutorial: step 7

jpmckinney edited this page Jan 23, 2012 · 34 revisions

Table of Contents

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. 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;

  $(this.target).find('input').unbind().removeData('events').bind('keydown', function(e) {
    if (self.requestSent === false && e.which == 13) {
      var value = $(this).val();
      if (value && self.add(value)) {
        self.manager.doRequest(0);
      }
    }
  });

  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').unautocomplete().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);
    }
  });
}

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 and free-text widgets, respectively. The rest is jQuery Autocomplete plugin implementation. Technical note: We must unautocomplete() to prevent a memory leak. unautocomplete() removes keydown bindings, so we must bind keydown in afterRequest instead of init.

[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 facet values, we will need to retrieve them in a separate Solr request. (In the next release of Solr, Solr 1.5, we will 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').val('');

  var self = this;

  $(this.target).find('input').unbind().removeData('events').bind('keydown', function(e) {
    if (self.requestSent === false && e.which == 13) {
      var value = $(this).val();
      if (value && self.add(value)) {
        self.manager.doRequest(0);
      }
    }
  });

  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').unautocomplete().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);
      }
    });
  } // 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=*:*&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 current filters to limit the auto-completion suggestions. 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]));
}

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.

[What we have so far]

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