Jump To …
backend.csv.js backend.dataproxy.js backend.elasticsearch.js backend.gdocs.js backend.memory.js ecma-fixes.js model.js view.flot.js view.graph.js view.grid.js view.map.js view.multiview.js view.slickgrid.js view.timeline.js widget.facetviewer.js widget.fields.js widget.filtereditor.js widget.pager.js widget.queryeditor.js widget.valuefilter.js

# backend.elasticsearch.js |
--- | ---
¶ |
    this.recline = this.recline || {};
    this.recline.Backend = this.recline.Backend || {};
    this.recline.Backend.ElasticSearch = this.recline.Backend.ElasticSearch || {};

    (function($, my) {
      my.__type__ = 'elasticsearch';
¶ use either jQuery or Underscore Deferred depending on what is available |
      var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
¶ ## ElasticSearch Wrapper A simple JS wrapper around an ElasticSearch endpoints. @param {String} endpoint: url for ElasticSearch type/table, e.g. for ES running on http://localhost:9200 with index twitter and type tweet it would be:
    http://localhost:9200/twitter/tweet
@param {Object} options: set of options such as: headers - {dict of headers to add to each request} dataType: dataType for AJAx requests e.g. set to jsonp to make jsonp requests (default is json requests) |
      my.Wrapper = function(endpoint, options) {
        var self = this;
        this.endpoint = endpoint;
        this.options = _.extend({
            dataType: 'json'
          },
          options);
¶ ### mapping Get ES mapping for this type/table @return promise compatible deferred object. |
        this.mapping = function() {
          var schemaUrl = self.endpoint + '/_mapping';
          var jqxhr = makeRequest({
            url: schemaUrl,
            dataType: this.options.dataType
          });
          return jqxhr;
        };
¶ ### get Get record corresponding to specified id @return promise compatible deferred object. |
        this.get = function(id) {
          var base = this.endpoint + '/' + id;
          return makeRequest({
            url: base,
            dataType: 'json'
          });
        };
¶ ### upsert create / update a record to ElasticSearch backend @param {Object} doc an object to insert to the index. @return deferred supporting promise API |
        this.upsert = function(doc) {
          var data = JSON.stringify(doc);
          url = this.endpoint;
          if (doc.id) {
            url += '/' + doc.id;
          }
          return makeRequest({
            url: url,
            type: 'POST',
            data: data,
            dataType: 'json'
          });
        };
¶ ### delete Delete a record from the ElasticSearch backend. @param {Object} id id of object to delete @return deferred supporting promise API |
        this.remove = function(id) {
          url = this.endpoint;
          url += '/' + id;
          return makeRequest({
            url: url,
            type: 'DELETE',
            dataType: 'json'
          });
        };

        this._normalizeQuery = function(queryObj) {
          var self = this;
          var queryInfo = (queryObj && queryObj.toJSON) ? queryObj.toJSON() : _.extend({}, queryObj);
          var out = {
            constant_score: {
              query: {}
            }
          };
          if (!queryInfo.q) {
            out.constant_score.query = {
              match_all: {}
            };
          } else {
            out.constant_score.query = {
              query_string: {
                query: queryInfo.q
              }
            };
          }
          if (queryInfo.filters && queryInfo.filters.length) {
            out.constant_score.filter = {
              and: []
            };
            _.each(queryInfo.filters, function(filter) {
              out.constant_score.filter.and.push(self._convertFilter(filter));
            });
          }
          return out;
        },
¶ convert from Recline sort structure to ES form http://www.elasticsearch.org/guide/reference/api/search/sort.html |
        this._normalizeSort = function(sort) {
          var out = _.map(sort, function(sortObj) {
            var _tmp = {};
            var _tmp2 = _.clone(sortObj);
            delete _tmp2['field'];
            _tmp[sortObj.field] = _tmp2;
            return _tmp;
          });
          return out;
        },

        this._convertFilter = function(filter) {
          var out = {};
          out[filter.type] = {}
          if (filter.type === 'term') {
            out.term[filter.field] = filter.term.toLowerCase();
          } else if (filter.type === 'geo_distance') {
            out.geo_distance[filter.field] = filter.point;
            out.geo_distance.distance = filter.distance;
            out.geo_distance.unit = filter.unit;
          }
          return out;
        },
¶ ### query @return deferred supporting promise API |
        this.query = function(queryObj) {
          var esQuery = (queryObj && queryObj.toJSON) ? queryObj.toJSON() : _.extend({}, queryObj);
          esQuery.query = this._normalizeQuery(queryObj);
          delete esQuery.q;
          delete esQuery.filters;
          if (esQuery.sort && esQuery.sort.length > 0) {
            esQuery.sort = this._normalizeSort(esQuery.sort);
          }
          var data = {source: JSON.stringify(esQuery)};
          var url = this.endpoint + '/_search';
          var jqxhr = makeRequest({
            url: url,
            data: data,
            dataType: this.options.dataType
          });
          return jqxhr;
        }
      };
¶ ## Recline Connectors Requires URL of ElasticSearch endpoint to be specified on the dataset via the url attribute. |
¶ ES options which are passed through to `options` on Wrapper (see Wrapper for details) |
      my.esOptions = {};
¶ ### fetch |
      my.fetch = function(dataset) {
        var es = new my.Wrapper(dataset.url, my.esOptions);
        var dfd = new Deferred();
        es.mapping().done(function(schema) {

          if (!schema){
            dfd.reject({'message':'Elastic Search did not return a mapping'});
            return;
          }
¶ 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;
          });
          dfd.resolve({
            fields: fieldData
          });
        })
        .fail(function(arguments) {
          dfd.reject(arguments);
        });
        return dfd.promise();
      };
¶ ### save |
      my.save = function(changes, dataset) {
        var es = new my.Wrapper(dataset.url, my.esOptions);
        if (changes.creates.length + changes.updates.length + changes.deletes.length > 1) {
          var dfd = new Deferred();
          msg = 'Saving more than one item at a time not yet supported';
          alert(msg);
          dfd.reject(msg);
          return dfd.promise();
        }
        if (changes.creates.length > 0) {
          return es.upsert(changes.creates[0]);
        }
        else if (changes.updates.length >0) {
          return es.upsert(changes.updates[0]);
        } else if (changes.deletes.length > 0) {
          return es.remove(changes.deletes[0].id);
        }
      };
¶ ### query |
      my.query = function(queryObj, dataset) {
        var dfd = new Deferred();
        var es = new my.Wrapper(dataset.url, my.esOptions);
        var jqxhr = es.query(queryObj);
        jqxhr.done(function(results) {
          var out = {
            total: results.hits.total
          };
          out.hits = _.map(results.hits.hits, function(hit) {
            if (!('id' in hit._source) && hit._id) {
              hit._source.id = hit._id;
            }
            return hit._source;
          });
          if (results.facets) {
            out.facets = results.facets;
          }
          dfd.resolve(out);
        }).fail(function(errorObj) {
          var out = {
            title: 'Failed: ' + errorObj.status + ' code',
            message: errorObj.responseText
          };
          dfd.reject(out);
        });
        return dfd.promise();
      };
¶ ### makeRequest Just $.ajax but in any headers in the 'headers' attribute of this Backend instance. Example:
    var jqxhr = this._makeRequest({
      url: the-url
    });
    var makeRequest = function(data, headers) {
      var extras = {};
      if (headers) {
        extras = {
          beforeSend: function(req) {
            _.each(headers, function(value, key) {
              req.setRequestHeader(key, value);
            });
          }
        };
      }
      var data = _.extend(extras, data);
      return $.ajax(data);
    };

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