Jump To …

elasticsearch.js

this.recline = this.recline || {};
this.recline.Backend = this.recline.Backend || {};

(function($, my) {

ElasticSearch Backend

Connecting to ElasticSearch.

Usage:

var backend = new recline.Backend.ElasticSearch({
  // optional as can also be provided by Dataset/Document
  url: {url to ElasticSearch endpoint i.e. ES 'type/table' url - more info below}
  // optional
  headers: {dict of headers to add to each request}
});

@param {String} url: url for ElasticSearch type/table, e.g. for ES running
on localhost:9200 with index // twitter and type tweet it would be:

http://localhost:9200/twitter/tweet
This url is optional since the ES endpoint url may be specified on the the dataset (and on a Document by the document having a dataset attribute) by having one of the following (see also `_getESUrl` function):
elasticsearch_url
webstore_url
url
  my.ElasticSearch = my.Base.extend({
    __type__: 'elasticsearch',
    readonly: false,
    sync: function(method, model, options) {
      var self = this;
      if (method === "read") {
        if (model.__type__ == 'Dataset') {
          var schemaUrl = self._getESUrl(model) + '/_mapping';
          var jqxhr = this._makeRequest({
            url: schemaUrl,
            dataType: 'jsonp'
          });
          var dfd = $.Deferred();
          this._wrapInTimeout(jqxhr).done(function(schema) {

only one top level key in ES = the type so we can ignore it

            var key = _.keys(schema)[0];
            var fieldData = _.map(schema[key].properties, function(dict, fieldName) {
              dict.id = fieldName;
              return dict;
            });
            model.fields.reset(fieldData);
            dfd.resolve(model, jqxhr);
          })
          .fail(function(arguments) {
            dfd.reject(arguments);
          });
          return dfd.promise();
        } else if (model.__type__ == 'Document') {
          var base = this._getESUrl(model.dataset) + '/' + model.id;
          return this._makeRequest({
            url: base,
            dataType: 'json'
          });
        }
      } else if (method === 'update') {
        if (model.__type__ == 'Document') {
          return this.upsert(model.toJSON(), this._getESUrl(model.dataset));
        }
      } else if (method === 'delete') {
        if (model.__type__ == 'Document') {
          var url = this._getESUrl(model.dataset);
          return this.delete(model.id, url);
        }
      }
    },

upsert

create / update a document to ElasticSearch backend

@param {Object} doc an object to insert to the index. @param {string} url (optional) url for ElasticSearch endpoint (if not defined called this._getESUrl()

    upsert: function(doc, url) {
      var data = JSON.stringify(doc);
      url = url ? url : this._getESUrl();
      if (doc.id) {
        url += '/' + doc.id;
      }
      return this._makeRequest({
        url: url,
        type: 'POST',
        data: data,
        dataType: 'json'
      });
    },

delete

Delete a document from the ElasticSearch backend.

@param {Object} id id of object to delete @param {string} url (optional) url for ElasticSearch endpoint (if not provided called this._getESUrl()

    delete: function(id, url) {
      url = url ? url : this._getESUrl();
      url += '/' + id;
      return this._makeRequest({
        url: url,
        type: 'DELETE',
        dataType: 'json'
      });
    },

_getESUrl

get url to ElasticSearch endpoint (see above)

    _getESUrl: function(dataset) {
      if (dataset) {
        var out = dataset.get('elasticsearch_url');
        if (out) return out;
        out = dataset.get('webstore_url');
        if (out) return out;
        out = dataset.get('url');
        return out;
      }
      return this.get('url');
    },
    _normalizeQuery: function(queryObj) {
      var out = queryObj.toJSON ? queryObj.toJSON() : _.extend({}, queryObj);
      if (out.q !== undefined && out.q.trim() === '') {
        delete out.q;
      }
      if (!out.q) {
        out.query = {
          match_all: {}
        };
      } else {
        out.query = {
          query_string: {
            query: out.q
          }
        };
        delete out.q;
      }

now do filters (note the plural)

      if (out.filters && out.filters.length) {
        if (!out.filter) {
          out.filter = {};
        }
        if (!out.filter.and) {
          out.filter.and = [];
        }
        out.filter.and = out.filter.and.concat(out.filters);
      }
      if (out.filters !== undefined) {
        delete out.filters;
      }
      return out;
    },
    query: function(model, queryObj) {
      var queryNormalized = this._normalizeQuery(queryObj);
      var data = {source: JSON.stringify(queryNormalized)};
      var base = this._getESUrl(model);
      var jqxhr = this._makeRequest({
        url: base + '/_search',
        data: data,
        dataType: 'jsonp'
      });
      var dfd = $.Deferred();

TODO: fail case

      jqxhr.done(function(results) {
        _.each(results.hits.hits, function(hit) {
          if (!('id' in hit._source) && hit._id) {
            hit._source.id = hit._id;
          }
        });
        if (results.facets) {
          results.hits.facets = results.facets;
        }
        dfd.resolve(results.hits);
      });
      return dfd.promise();
    }
  });

}(jQuery, this.recline.Backend));