From 157d77ebb1226f9b6f259d48d8f0284872454a8f Mon Sep 17 00:00:00 2001 From: Rufus Pollock Date: Sun, 1 Apr 2012 11:45:42 +0100 Subject: [PATCH] [#57,backend,model][m]: switch to having backend query methods return a QueryResult object - fixes #57. --- src/backend/base.js | 47 +++++++++++++++++++++++++++++++++--- src/backend/dataproxy.js | 3 ++- src/backend/elasticsearch.js | 13 +++++----- src/backend/gdocs.js | 2 +- src/backend/memory.js | 6 +++-- src/model.js | 7 +++--- 6 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/backend/base.js b/src/backend/base.js index 49eda889..37d81d33 100644 --- a/src/backend/base.js +++ b/src/backend/base.js @@ -40,18 +40,59 @@ this.recline.Backend = this.recline.Backend || {}; // ### query // - // Query the backend for documents returning them in bulk. This method will be used by the Dataset.query method to search the backend for documents, retrieving the results in bulk. This method should also set the docCount attribute on the dataset. + // Query the backend for documents returning them in bulk. This method will + // be used by the Dataset.query method to search the backend for documents, + // retrieving the results in bulk. // - // queryObj should be either a recline.Model.Query - // object or a Hash. The structure of data in the Query object or + // @param {recline.model.Dataset} model: Dataset model. + // + // @param {Object} queryObj: object describing a query (usually produced by + // using recline.Model.Query and calling toJSON on it). + // + // The structure of data in the Query object or // Hash should follow that defined in issue 34. // (Of course, if you are writing your own backend, and hence // have control over the interpretation of the query object, you // can use whatever structure you like). + // + // @returns {Promise} promise API object. The promise resolve method will + // be called on query completion with a QueryResult object. + // + // A QueryResult has the following structure (modelled closely on + // ElasticSearch - see this issue for more + // details): + // + //
+    // {
+    //   total: // (required) total number of results (can be null)
+    //   hits: [ // (required) one entry for each result document
+    //     {
+    //        _score:   // (optional) match score for document
+    //        _type: // (optional) document type
+    //        _source: // (required) document/row object
+    //     } 
+    //   ],
+    //   facets: { // (optional) 
+    //     // facet results (as per )
+    //   }
+    // }
+    // 
query: function(model, queryObj) { }, + // convenience method to convert simple set of documents / rows to a QueryResult + _docsToQueryResult: function(rows) { + var hits = _.map(rows, function(row) { + return { _source: row }; + }); + return { + total: null, + hits: hits + }; + }, + // ## _wrapInTimeout // // Convenience method providing a crude way to catch backend errors on JSONP calls. diff --git a/src/backend/dataproxy.js b/src/backend/dataproxy.js index e2ddb767..cfbfa342 100644 --- a/src/backend/dataproxy.js +++ b/src/backend/dataproxy.js @@ -35,6 +35,7 @@ this.recline.Backend = this.recline.Backend || {}; } }, query: function(dataset, queryObj) { + var self = this; var base = this.get('dataproxy_url'); var data = { url: dataset.get('url') @@ -62,7 +63,7 @@ this.recline.Backend = this.recline.Backend || {}; }); return tmp; }); - dfd.resolve(_out); + dfd.resolve(self._docsToQueryResult(_out)); }) .fail(function(arguments) { dfd.reject(arguments); diff --git a/src/backend/elasticsearch.js b/src/backend/elasticsearch.js index 5668202c..a0f960e6 100644 --- a/src/backend/elasticsearch.js +++ b/src/backend/elasticsearch.js @@ -93,13 +93,12 @@ this.recline.Backend = this.recline.Backend || {}; 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); + _.each(results.hits.hits, function(hit) { + if (!'id' in hit._source && hit._id) { + hit._source.id = hit._id; + } + }) + dfd.resolve(results.hits); }); return dfd.promise(); } diff --git a/src/backend/gdocs.js b/src/backend/gdocs.js index 63ce2eb7..3a7a6aaf 100644 --- a/src/backend/gdocs.js +++ b/src/backend/gdocs.js @@ -67,7 +67,7 @@ this.recline.Backend = this.recline.Backend || {}; _.each(_.zip(fields, d), function (x) { obj[x[0]] = x[1]; }) return obj; }); - dfd.resolve(objs); + dfd.resolve(this._docsToQueryResult(objs)); return dfd; }, gdocsToJavascript: function(gdocsSpreadsheet) { diff --git a/src/backend/memory.js b/src/backend/memory.js index 97b585e0..e1a891cd 100644 --- a/src/backend/memory.js +++ b/src/backend/memory.js @@ -127,8 +127,10 @@ this.recline.Backend = this.recline.Backend || {}; return (sortObj[fieldName].order == 'asc') ? _out : -1*_out; }); }); - var results = results.slice(start, start+numRows); - dfd.resolve(results); + var total = results.length; + var out = this._docsToQueryResult(results.slice(start, start+numRows)); + out.total = total; + dfd.resolve(out); return dfd.promise(); } }); diff --git a/src/model.js b/src/model.js index b3743381..e102f932 100644 --- a/src/model.js +++ b/src/model.js @@ -43,9 +43,10 @@ my.Dataset = Backbone.Model.extend({ var self = this; this.queryState.set(queryObj); var dfd = $.Deferred(); - this.backend.query(this, this.queryState.toJSON()).done(function(rows) { - var docs = _.map(rows, function(row) { - var _doc = new my.Document(row); + this.backend.query(this, this.queryState.toJSON()).done(function(queryResult) { + self.docCount = queryResult.total; + var docs = _.map(queryResult.hits, function(hit) { + var _doc = new my.Document(hit._source); _doc.backend = self.backend; _doc.dataset = self; return _doc;