[#61,backend/elasticearch][s]: refactor plus support for setting request headers (e.g. Authorization) plus can set backend url on initialization.

* support for headers went into new _makeRequest method on backend base class
* support for setting backend url on initialization (rather than depending on it being on Dataset/Document objects)
* move upsert and delete methods out into distinct methods from being inside backbone sync

The two last of these pave the way for use of ElasticSearch backend standalone (both independent from Recline and independent of Backbone)
This commit is contained in:
Rufus Pollock 2012-04-23 02:30:16 +01:00
parent 1bf64c5f94
commit a577866932
2 changed files with 104 additions and 37 deletions

View File

@ -99,6 +99,32 @@ this.recline.Backend = this.recline.Backend || {};
query: function(model, queryObj) {
},
// ### _makeRequest
//
// Just $.ajax but in any headers in the 'headers' attribute of this
// Backend instance. Example:
//
// <pre>
// var jqxhr = this._makeRequest({
// url: the-url
// });
// </pre>
_makeRequest: function(data) {
var headers = this.get('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);
},
// convenience method to convert simple set of documents / rows to a QueryResult
_docsToQueryResult: function(rows) {
var hits = _.map(rows, function(row) {

View File

@ -6,37 +6,39 @@ this.recline.Backend = this.recline.Backend || {};
//
// Connecting to [ElasticSearch](http://www.elasticsearch.org/).
//
// To use this backend ensure your Dataset has one of the following
// attributes (first one found is used):
// Usage:
//
// <pre>
// 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:
//
// <pre>http://localhost:9200/twitter/tweet</pre>
//
// 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):
//
// <pre>
// elasticsearch_url
// webstore_url
// url
// </pre>
//
// This should point to the ES type url. E.G. for ES running on
// localhost:9200 with index twitter and type tweet it would be
//
// <pre>http://localhost:9200/twitter/tweet</pre>
my.ElasticSearch = my.Base.extend({
__type__: 'elasticsearch',
readonly: false,
_getESUrl: function(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;
},
sync: function(method, model, options) {
var self = this;
if (method === "read") {
if (model.__type__ == 'Dataset') {
var base = self._getESUrl(model);
var schemaUrl = base + '/_mapping';
var jqxhr = $.ajax({
var schemaUrl = self._getESUrl(model) + '/_mapping';
var jqxhr = this._makeRequest({
url: schemaUrl,
dataType: 'jsonp'
});
@ -57,36 +59,75 @@ this.recline.Backend = this.recline.Backend || {};
return dfd.promise();
} else if (model.__type__ == 'Document') {
var base = this._getESUrl(model.dataset) + '/' + model.id;
return $.ajax({
return this._makeRequest({
url: base,
dataType: 'json'
});
}
} else if (method === 'update') {
if (model.__type__ == 'Document') {
var data = JSON.stringify(model.toJSON());
var base = this._getESUrl(model.dataset);
if (model.id) {
base += '/' + model.id;
}
return $.ajax({
url: base,
type: 'POST',
data: data,
dataType: 'json'
});
return this.upsert(model.toJSON(), this._getESUrl(model.dataset));
}
} else if (method === 'delete') {
if (model.__type__ == 'Document') {
var base = this._getESUrl(model.dataset) + '/' + model.id;
return $.ajax({
url: base,
type: 'DELETE',
dataType: 'json'
});
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() === '') {
@ -123,7 +164,7 @@ this.recline.Backend = this.recline.Backend || {};
var queryNormalized = this._normalizeQuery(queryObj);
var data = {source: JSON.stringify(queryNormalized)};
var base = this._getESUrl(model);
var jqxhr = $.ajax({
var jqxhr = this._makeRequest({
url: base + '/_search',
data: data,
dataType: 'jsonp'