From a9c1b2ae07182b111e21e8992824b3df67ca196a Mon Sep 17 00:00:00 2001 From: Rufus Pollock Date: Fri, 24 Feb 2012 22:32:30 +0000 Subject: [PATCH] [#54,backend/es][m]: ElasticSearch backend (readonly atm) - fixes #54. --- src/backend/elasticsearch.js | 75 +++++++++++++++++ test/backend.elasticsearch.test.js | 124 +++++++++++++++++++++++++++++ test/index.html | 2 + 3 files changed, 201 insertions(+) create mode 100644 src/backend/elasticsearch.js create mode 100644 test/backend.elasticsearch.test.js diff --git a/src/backend/elasticsearch.js b/src/backend/elasticsearch.js new file mode 100644 index 00000000..36a7c1a4 --- /dev/null +++ b/src/backend/elasticsearch.js @@ -0,0 +1,75 @@ +this.recline = this.recline || {}; +this.recline.Backend = this.recline.Backend || {}; + +(function($, my) { + // ## ElasticSearch Backend + // + // Connecting to [ElasticSearch](http://www.elasticsearch.org/) + // + // To use this backend ensure your Dataset has a elasticsearch_url, + // webstore_url or url attribute (used in that order) + my.ElasticSearch = Backbone.Model.extend({ + _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({ + url: schemaUrl, + dataType: 'jsonp' + }); + var dfd = $.Deferred(); + my.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 { + alert('This backend currently only supports read operations'); + } + }, + query: function(model, queryObj) { + var base = this._getESUrl(model); + var data = _.extend({}, queryObj); + var jqxhr = $.ajax({ + url: base + '/_search', + data: data, + dataType: 'jsonp' + }); + var dfd = $.Deferred(); + // TODO: fail case + jqxhr.done(function(results) { + model.docCount = results.hits.total; + var docs = _.map(results.hits.hits, function(result) { + var _out = result._source; + _out.id = result._id; + return _out; + }); + dfd.resolve(docs); + }); + return dfd.promise(); + } + }); + recline.Model.backends['elasticsearch'] = new my.ElasticSearch(); + +}(jQuery, this.recline.Backend)); + diff --git a/test/backend.elasticsearch.test.js b/test/backend.elasticsearch.test.js new file mode 100644 index 00000000..d3822c16 --- /dev/null +++ b/test/backend.elasticsearch.test.js @@ -0,0 +1,124 @@ +(function ($) { +module("Backend ElasticSearch"); + +var mapping_data = { + "note": { + "properties": { + "_created": { + "format": "dateOptionalTime", + "type": "date" + }, + "_last_modified": { + "format": "dateOptionalTime", + "type": "date" + }, + "end": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "start": { + "type": "string" + }, + "title": { + "type": "string" + } + } + } +}; + +var sample_data = { + "_shards": { + "failed": 0, + "successful": 5, + "total": 5 + }, + "hits": { + "hits": [ + { + "_id": "u3rpLyuFS3yLNXrtxWkMwg", + "_index": "hypernotes", + "_score": 1.0, + "_source": { + "_created": "2012-02-24T17:53:57.286Z", + "_last_modified": "2012-02-24T17:53:57.286Z", + "owner": "tester", + "title": "Note 1" + }, + "_type": "note" + }, + { + "_id": "n7JMkFOHSASJCVTXgcpqkA", + "_index": "hypernotes", + "_score": 1.0, + "_source": { + "_created": "2012-02-24T17:53:57.290Z", + "_last_modified": "2012-02-24T17:53:57.290Z", + "owner": "tester", + "title": "Note 3" + }, + "_type": "note" + }, + { + "_id": "g7UMA55gTJijvsB3dFitzw", + "_index": "hypernotes", + "_score": 1.0, + "_source": { + "_created": "2012-02-24T17:53:57.289Z", + "_last_modified": "2012-02-24T17:53:57.289Z", + "owner": "tester", + "title": "Note 2" + }, + "_type": "note" + } + ], + "max_score": 1.0, + "total": 3 + }, + "timed_out": false, + "took": 2 +}; + +test("ElasticSearch", function() { + var dataset = new recline.Model.Dataset({ + url: 'https://localhost:9200/my-es-db/my-es-type' + }, + 'elasticsearch' + ); + + var stub = sinon.stub($, 'ajax', function(options) { + if (options.url.indexOf('_mapping') != -1) { + return { + done: function(callback) { + callback(mapping_data); + return this; + }, + fail: function() { + return this; + } + } + } else { + return { + done: function(callback) { + callback(sample_data); + }, + fail: function() { + } + } + } + }); + + dataset.fetch().then(function(dataset) { + deepEqual(['_created', '_last_modified', 'end', 'owner', 'start', 'title'], _.pluck(dataset.fields.toJSON(), 'id')); + dataset.query().then(function(docList) { + equal(3, dataset.docCount); + equal(3, docList.length); + equal('Note 1', docList.models[0].get('title')); + start(); + }); + }); + $.ajax.restore(); +}); + +})(this.jQuery); diff --git a/test/index.html b/test/index.html index 0f16894d..fb96f9cf 100644 --- a/test/index.html +++ b/test/index.html @@ -23,7 +23,9 @@ + +