diff --git a/controller/suggest.js b/controller/suggest.js index 956363b8..e5f87084 100644 --- a/controller/suggest.js +++ b/controller/suggest.js @@ -1,6 +1,7 @@ var service = { suggest: require('../service/suggest') }; var geojsonify = require('../helper/geojsonify').suggest; +var async = require('async'); function setup( backend, query ){ @@ -10,28 +11,108 @@ function setup( backend, query ){ function controller( req, res, next ){ - // backend command var cmd = { index: 'pelias', body: query( req.clean ) }; - // query backend - service.suggest( backend, cmd, function( err, docs ){ + var SIZE = req.clean.size || 10; - // error handler - if( err ){ return next( err ); } + var query_backend = function(cmd, callback) { + // query backend + service.suggest( backend, cmd, function( err, docs ){ + + // error handler + if( err ){ return next( err ); } + + callback(null, docs); + }); + }; + + var dedup = function(combined) { + var unique_ids = []; + return combined.filter(function(item, pos) { + if (unique_ids.indexOf(item.payload.id) == -1) { + unique_ids.push(item.payload.id); + return true; + } + return false; + }); + }; + + var respond = function(data) { // convert docs to geojson - var geojson = geojsonify( docs ); + var geojson = geojsonify( data ); // response envelope geojson.date = new Date().getTime(); // respond return res.status(200).json( geojson ); - }); + }; + + if (req.clean.input) { + var async_query; + + // admin only + req.admin = {}; + for (k in req.clean) { req.admin[k] = req.clean[k] } + req.admin.layers = ['admin0','admin1','admin2']; + if (req.clean.input.length < 4 && isNaN(parseInt(req.clean.input, 10))) { + async_query = { + a: function(callback){ + cmd.body = query( req.admin, 3 ); + query_backend(cmd, callback); + }, + b: function(callback){ + cmd.body = query( req.admin, 1 ); + query_backend(cmd, callback); + }, + c: function(callback) { + cmd.body = query( req.clean, 3 ); + query_backend(cmd, callback); + } + } + } else { + async_query = { + a: function(callback){ + cmd.body = query( req.clean, 5); + query_backend(cmd, callback); + }, + b: function(callback){ + cmd.body = query( req.clean, 3); + query_backend(cmd, callback); + }, + c: function(callback){ + cmd.body = query( req.clean, 1 ); + query_backend(cmd, callback); + }, + d: function(callback){ + cmd.body = query( req.admin ); + query_backend(cmd, callback); + } + } + } + + async.parallel(async_query, function(err, results) { + var splice_length = parseInt((SIZE / Object.keys(results).length), 10); + // results is equal to: {a: docs, b: docs, c: docs} + var combined = []; + for (keys in results) { + combined = combined.concat(results[keys].splice(0,splice_length)); + } + + combined = dedup(combined); + respond(combined); + }); + } else { + query_backend(cmd, function(err, results) { + respond(results); + }); + } + } return controller; diff --git a/package.json b/package.json index 4d0bfce9..aacccf89 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ "geojson": "^0.2.0", "geopipes-elasticsearch-backend": "0.0.8", "pelias-esclient": "0.0.25", - "toobusy": "^0.2.4" + "toobusy": "^0.2.4", + "async": "^0.9.0" }, "devDependencies": { "ciao": "^0.3.4", diff --git a/query/suggest.js b/query/suggest.js index ba086f88..29f2f0b3 100644 --- a/query/suggest.js +++ b/query/suggest.js @@ -2,7 +2,7 @@ var logger = require('../src/logger'); // Build pelias suggest query -function generate( params ){ +function generate( params, precision ){ var getPrecision = function(zoom) { switch (true) { @@ -29,7 +29,7 @@ function generate( params ){ 'dataset': params.layers, 'location': { 'value': [ params.lon, params.lat ], - 'precision': getPrecision(params.zoom) + 'precision': precision || getPrecision(params.zoom) } } } diff --git a/test/unit/controller/suggest.js b/test/unit/controller/suggest.js index f67f5da7..57051964 100644 --- a/test/unit/controller/suggest.js +++ b/test/unit/controller/suggest.js @@ -24,7 +24,7 @@ module.exports.tests.functional_success = function(test, common) { coordinates: [ 101, -10.1 ] }, properties: { - id: 'mockid', + id: 'mockid1', type: 'mocktype', value: 1 } @@ -35,7 +35,7 @@ module.exports.tests.functional_success = function(test, common) { coordinates: [ 101, -10.1 ] }, properties: { - id: 'mockid', + id: 'mockid2', type: 'mocktype', value: 2 } @@ -43,7 +43,12 @@ module.exports.tests.functional_success = function(test, common) { test('functional success', function(t) { var backend = mockBackend( 'client/suggest/ok/1', function( cmd ){ - t.deepEqual(cmd, { body: { a: 'b' }, index: 'pelias' }, 'correct backend command'); + if (cmd.body.layers) { + // layers are set exclusively for admin: test for admin-only layers + t.deepEqual(cmd, { body: { input: 'b', layers: [ 'admin0', 'admin1', 'admin2' ] }, index: 'pelias' }, 'correct backend command'); + } else { + t.deepEqual(cmd, { body: { input: 'b' }, index: 'pelias' }, 'correct backend command'); + } }); var controller = setup( backend, mockQuery() ); var res = { @@ -60,7 +65,7 @@ module.exports.tests.functional_success = function(test, common) { t.end(); } }; - controller( { clean: { a: 'b' } }, res ); + controller( { clean: { input: 'b' } }, res ); }); }; diff --git a/test/unit/mock/backend.js b/test/unit/mock/backend.js index 1d0c3aaf..8c4e7baa 100644 --- a/test/unit/mock/backend.js +++ b/test/unit/mock/backend.js @@ -1,12 +1,14 @@ -var mockPayload = { - id: 'mocktype/mockid', - geo: '101,-10.1' +var mockPayload = function(id){ + return { + id: 'mocktype/mockid'+id, + geo: '101,-10.1' + } }; var responses = {}; responses['client/suggest/ok/1'] = function( cmd, cb ){ - return cb( undefined, suggestEnvelope([ { value: 1, payload: mockPayload }, { value: 2, payload: mockPayload } ]) ); + return cb( undefined, suggestEnvelope([ { value: 1, payload: mockPayload(1) }, { value: 2, payload: mockPayload(2) } ]) ); }; responses['client/suggest/fail/1'] = function( cmd, cb ){ return cb( 'a backend error occurred' ); diff --git a/test/unit/service/suggest.js b/test/unit/service/suggest.js index 0f57e02c..c74a72c1 100644 --- a/test/unit/service/suggest.js +++ b/test/unit/service/suggest.js @@ -16,14 +16,16 @@ module.exports.tests.interface = function(test, common) { // functionally test service module.exports.tests.functional_success = function(test, common) { - var mockPayload = { - id: 'mocktype/mockid', - geo: '101,-10.1' + var mockPayload = function(id){ + return { + id: 'mocktype/mockid'+id, + geo: '101,-10.1' + } }; var expected = [ - { value: 1, payload: mockPayload }, - { value: 2, payload: mockPayload } + { value: 1, payload: mockPayload(1) }, + { value: 2, payload: mockPayload(2) } ]; test('valid ES query', function(t) {