diff --git a/controller/search.js b/controller/search.js index ff38a3a6..fcb12831 100644 --- a/controller/search.js +++ b/controller/search.js @@ -1,4 +1,6 @@ +var geojsonify = require('../helper/geojsonify').search; + function setup( backend, query ){ // allow overriding of dependencies @@ -27,11 +29,14 @@ function setup( backend, query ){ }); } + // convert docs to geojson + var geojson = geojsonify( docs ); + + // response envelope + geojson.date = new Date().getTime(); + // respond - return res.status(200).json({ - date: new Date().getTime(), - body: docs - }); + return res.status(200).json( geojson ); }); } diff --git a/helper/geojsonify.js b/helper/geojsonify.js index 1154fd8a..2dd81ea2 100644 --- a/helper/geojsonify.js +++ b/helper/geojsonify.js @@ -42,4 +42,48 @@ function suggest( docs ){ } -module.exports.suggest = suggest; \ No newline at end of file +function search( docs ){ + + // emit a warning if the doc format is invalid + // @note: if you see this error, fix it ASAP! + function warning(){ + console.error( 'error: invalid doc', __filename ); + return false; // remove offending doc from results + } + + // flatten & expand data for geojson conversion + var geodata = docs.map( function( doc ){ + + var output = {}; + + // something went very wrong + if( !doc ) return warning(); + + // map center_point + if( !doc.center_point ) return warning(); + output.lat = parseFloat( doc.center_point.lat ); + output.lng = parseFloat( doc.center_point.lon ); + + // map name + if( !doc.name || !doc.name.default ) return warning(); + output.name = doc.name.default; + + // map admin values + if( doc.admin0 ){ output.admin0 = doc.admin0; } + if( doc.admin1 ){ output.admin1 = doc.admin1; } + if( doc.admin2 ){ output.admin2 = doc.admin2; } + + return output; + + // filter-out invalid entries + }).filter( function( doc ){ + return doc; + }); + + // convert to geojson + return GeoJSON.parse( geodata, { Point: ['lat', 'lng'] } ); + +} + +module.exports.suggest = suggest; +module.exports.search = search; \ No newline at end of file diff --git a/test/ciao/reverse/success.coffee b/test/ciao/reverse/success.coffee index c77a3f06..3f936fbd 100644 --- a/test/ciao/reverse/success.coffee +++ b/test/ciao/reverse/success.coffee @@ -8,7 +8,8 @@ response.statusCode.should.equal 200 now = new Date().getTime() should.exist json should.not.exist json.error -should.exist json.date -json.date.should.be.within now-2000, now+2000 -should.exist json.body -json.body.should.be.instanceof Array \ No newline at end of file +json.date.should.be.within now-5000, now+5000 + +#? valid geojson +json.type.should.equal 'FeatureCollection' +json.features.should.be.instanceof Array \ No newline at end of file diff --git a/test/ciao/search/success.coffee b/test/ciao/search/success.coffee index b55899cd..79ab9bbc 100644 --- a/test/ciao/search/success.coffee +++ b/test/ciao/search/success.coffee @@ -8,7 +8,8 @@ response.statusCode.should.equal 200 now = new Date().getTime() should.exist json should.not.exist json.error -should.exist json.date -json.date.should.be.within now-2000, now+2000 -should.exist json.body -json.body.should.be.instanceof Array \ No newline at end of file +json.date.should.be.within now-5000, now+5000 + +#? valid geojson +json.type.should.equal 'FeatureCollection' +json.features.should.be.instanceof Array \ No newline at end of file diff --git a/test/unit/controller/search.js b/test/unit/controller/search.js index e5bfbd06..72dadada 100644 --- a/test/unit/controller/search.js +++ b/test/unit/controller/search.js @@ -15,7 +15,35 @@ module.exports.tests.interface = function(test, common) { // functionally test controller (backend success) module.exports.tests.functional_success = function(test, common) { - test('functional test', function(t) { + + // expected geojson features for 'client/suggest/ok/1' fixture + var expected = [{ + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [ -50.5, 100.1 ] + }, + properties: { + name: 'test name1', + admin0: 'country1', + admin1: 'state1', + admin2: 'city1' + } + }, { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [ -51.5, 100.2 ] + }, + properties: { + name: 'test name2', + admin0: 'country2', + admin1: 'state2', + admin2: 'city2' + } + }]; + + test('functional success', function(t) { var backend = mockBackend( 'client/search/ok/1', function( cmd ){ t.deepEqual(cmd, { body: { a: 'b' }, index: 'pelias' }, 'correct backend command'); }); @@ -28,8 +56,9 @@ module.exports.tests.functional_success = function(test, common) { json: function( json ){ t.equal(typeof json, 'object', 'returns json'); t.equal(typeof json.date, 'number', 'date set'); - t.true(Array.isArray(json.body), 'body is array'); - // t.deepEqual(json.body, [ { value: 1 }, { value: 2 } ], 'values correctly mapped'); + t.equal(json.type, 'FeatureCollection', 'valid geojson'); + t.true(Array.isArray(json.features), 'features is array'); + t.deepEqual(json.features, expected, 'values correctly mapped'); t.end(); } }; @@ -39,7 +68,7 @@ module.exports.tests.functional_success = function(test, common) { // functionally test controller (backend failure) module.exports.tests.functional_failure = function(test, common) { - test('functional test', function(t) { + test('functional failure', function(t) { var backend = mockBackend( 'client/search/fail/1', function( cmd ){ t.deepEqual(cmd, { body: { a: 'b' }, index: 'pelias' }, 'correct backend command'); }); diff --git a/test/unit/helper/geojsonify.js b/test/unit/helper/geojsonify.js index 5a3c66d1..3e802837 100644 --- a/test/unit/helper/geojsonify.js +++ b/test/unit/helper/geojsonify.js @@ -70,6 +70,108 @@ module.exports.tests.suggest = function(test, common) { }); }; + +module.exports.tests.search = function(test, common) { + + var input = [ + { + "center_point": { + "lat": 51.5337144, + "lon": -0.1069716 + }, + "name": { + "default": "'Round Midnight Jazz and Blues Bar" + }, + "type": "node", + "address": { + "number": "13", + "street": "Liverpool Road", + "zip": "N1 0RW" + }, + "admin0": "United Kingdom", + "admin1": "Islington", + "admin2": "Angel", + "suggest": { + "input": [ + "'round midnight jazz and blues bar" + ], + "payload": { + "id": "osmnode/2208150035", + "geo": "-0.10697160000000001,51.53371440000001" + }, + "output": "'Round Midnight Jazz and Blues Bar, Angel, United Kingdom" + } + }, + { + "type": "way", + "name": { + "default": "Blues Cafe" + }, + "center_point": { + "lat": "51.517806", + "lon": "-0.101795" + }, + "admin0": "United Kingdom", + "admin1": "City And County Of The City Of London", + "admin2": "Smithfield", + "suggest": { + "input": [ + "blues cafe" + ], + "payload": { + "id": "osmway/147495160", + "geo": "-0.101795,51.517806" + }, + "output": "Blues Cafe, Smithfield, United Kingdom" + } + } + ]; + + var expected = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + -0.1069716, + 51.5337144 + ] + }, + "properties": { + "name": "'Round Midnight Jazz and Blues Bar", + "admin0": "United Kingdom", + "admin1": "Islington", + "admin2": "Angel" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + -0.101795, + 51.517806 + ] + }, + "properties": { + "name": "Blues Cafe", + "admin0": "United Kingdom", + "admin1": "City And County Of The City Of London", + "admin2": "Smithfield" + } + } + ] + }; + + test('geojsonify.search()', function(t) { + var json = geojsonify.search( input ); + t.deepEqual(json, expected, 'all docs mapped'); + t.end(); + }); +}; + module.exports.all = function (tape, common) { function test(name, testFunction) { diff --git a/test/unit/mock/backend.js b/test/unit/mock/backend.js index 32a69228..63378423 100644 --- a/test/unit/mock/backend.js +++ b/test/unit/mock/backend.js @@ -12,7 +12,21 @@ responses['client/suggest/fail/1'] = function( cmd, cb ){ return cb( 'a backend error occurred' ); }; responses['client/search/ok/1'] = function( cmd, cb ){ - return cb( undefined, searchEnvelope([ { value: 1 }, { value: 2 } ]) ); + return cb( undefined, searchEnvelope([{ + _source: { + value: 1, + center_point: { lat: 100.1, lon: -50.5 }, + name: { default: 'test name1' }, + admin0: 'country1', admin1: 'state1', admin2: 'city1' + } + }, { + _source: { + value: 2, + center_point: { lat: 100.2, lon: -51.5 }, + name: { default: 'test name2' }, + admin0: 'country2', admin1: 'state2', admin2: 'city2' + } + }])); }; responses['client/search/fail/1'] = function( cmd, cb ){ return cb( 'a backend error occurred' ); @@ -41,7 +55,7 @@ function suggestEnvelope( options ){ } function searchEnvelope( options ){ - return { pelias: [{ options: options }]}; + return { hits: { total: options.length, hits: options } }; } module.exports = setup; \ No newline at end of file