mirror of https://github.com/pelias/api.git
Harish Krishna
10 years ago
14 changed files with 289 additions and 324 deletions
@ -1,72 +0,0 @@ |
|||||||
|
|
||||||
var service = { |
|
||||||
suggest: require('../service/suggest'), |
|
||||||
mget: require('../service/mget') |
|
||||||
}; |
|
||||||
var geojsonify = require('../helper/geojsonify').search; |
|
||||||
|
|
||||||
function setup( backend, query ){ |
|
||||||
|
|
||||||
// allow overriding of dependencies
|
|
||||||
backend = backend || require('../src/backend'); |
|
||||||
query = query || require('../query/suggest'); |
|
||||||
|
|
||||||
function controller( req, res, next ){ |
|
||||||
|
|
||||||
// backend command
|
|
||||||
var cmd = { |
|
||||||
index: 'pelias', |
|
||||||
body: query( req.clean ) |
|
||||||
}; |
|
||||||
|
|
||||||
// responder
|
|
||||||
function reply( docs ){ |
|
||||||
|
|
||||||
// convert docs to geojson
|
|
||||||
var geojson = geojsonify( docs ); |
|
||||||
|
|
||||||
// response envelope
|
|
||||||
geojson.date = new Date().getTime(); |
|
||||||
|
|
||||||
// respond
|
|
||||||
return res.status(200).json( geojson ); |
|
||||||
} |
|
||||||
|
|
||||||
// query backend
|
|
||||||
service.suggest( backend, cmd, function( err, suggested ){ |
|
||||||
|
|
||||||
// error handler
|
|
||||||
if( err ){ return next( err ); } |
|
||||||
|
|
||||||
// no documents suggested, return empty array to avoid ActionRequestValidationException
|
|
||||||
if( !Array.isArray( suggested ) || !suggested.length ){ |
|
||||||
return reply([]); |
|
||||||
} |
|
||||||
|
|
||||||
// map suggester output to mget query
|
|
||||||
var query = suggested.map( function( doc ) { |
|
||||||
var idParts = doc.text.split(':'); |
|
||||||
return { |
|
||||||
_index: 'pelias', |
|
||||||
_type: idParts[0], |
|
||||||
_id: idParts.slice(1).join(':') |
|
||||||
}; |
|
||||||
}); |
|
||||||
|
|
||||||
service.mget( backend, query, function( err, docs ){ |
|
||||||
|
|
||||||
// error handler
|
|
||||||
if( err ){ return next( err ); } |
|
||||||
|
|
||||||
// reply
|
|
||||||
return reply( docs ); |
|
||||||
|
|
||||||
}); |
|
||||||
}); |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
return controller; |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = setup; |
|
@ -0,0 +1,28 @@ |
|||||||
|
{ |
||||||
|
"suggest": [ |
||||||
|
{ |
||||||
|
"layers": ["geoname","osmnode","osmway","admin0","admin1","admin2","neighborhood"], |
||||||
|
"precision": [5, 3, 1] |
||||||
|
}, |
||||||
|
{ |
||||||
|
"layers": ["admin0","admin1","admin2","neighborhood"], |
||||||
|
"precision": [] |
||||||
|
}, |
||||||
|
{ |
||||||
|
"layers": ["geoname","osmnode","osmway","admin0","admin1","admin2","neighborhood"], |
||||||
|
"precision": [3], |
||||||
|
"fuzzy": "AUTO" |
||||||
|
} |
||||||
|
], |
||||||
|
"suggest_nearby": [ |
||||||
|
{ |
||||||
|
"layers": ["geoname","osmnode","osmway","admin0","admin1","admin2","neighborhood"], |
||||||
|
"precision": [] |
||||||
|
}, |
||||||
|
{ |
||||||
|
"layers": ["geoname","osmnode","osmway","admin0","admin1","admin2","neighborhood"], |
||||||
|
"precision": [], |
||||||
|
"fuzzy": "AUTO" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
|
||||||
|
var picker = function( results, size ){ |
||||||
|
var combined = []; |
||||||
|
var num_results = 0; |
||||||
|
|
||||||
|
for (var i=0; i<results.length && num_results<size; i++) { |
||||||
|
if (results[i] && results[i].length) { |
||||||
|
combined[i] = combined[i] || []; |
||||||
|
combined[i].push(results[i][0]); |
||||||
|
results[i].splice(0,1); |
||||||
|
num_results++; |
||||||
|
} else { |
||||||
|
results.splice(i,1); |
||||||
|
i--; |
||||||
|
} |
||||||
|
|
||||||
|
if (i === results.length-1) { |
||||||
|
i=0; |
||||||
|
} |
||||||
|
} |
||||||
|
return (combined.length > 0) ? sort_by_score(combined) : combined; |
||||||
|
}; |
||||||
|
|
||||||
|
var dedup = function(arr) { |
||||||
|
var unique_ids = []; |
||||||
|
return arr.filter(function(item, pos) { |
||||||
|
if (unique_ids.indexOf(item.name.default) === -1) { |
||||||
|
unique_ids.push(item.name.default); |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
var sort_by_score = function(arr) { |
||||||
|
return arr.map(function(doc) { |
||||||
|
return doc.sort(function(a,b) { |
||||||
|
return b.score - a.score; |
||||||
|
}); |
||||||
|
}).reduce(function(a,b) { //flatten
|
||||||
|
return a.concat(b); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
picker: picker, |
||||||
|
dedup: dedup |
||||||
|
}; |
@ -1,112 +0,0 @@ |
|||||||
|
|
||||||
var setup = require('../../../controller/suggest'), |
|
||||||
mockBackend = require('../mock/backend'), |
|
||||||
mockQuery = require('../mock/query'); |
|
||||||
|
|
||||||
module.exports.tests = {}; |
|
||||||
|
|
||||||
module.exports.tests.interface = function(test, common) { |
|
||||||
test('valid interface', function(t) { |
|
||||||
t.equal(typeof setup, 'function', 'setup is a function'); |
|
||||||
t.equal(typeof setup(), 'function', 'setup returns a controller'); |
|
||||||
t.end(); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
// functionally test controller (backend success)
|
|
||||||
module.exports.tests.functional_success = function(test, common) { |
|
||||||
|
|
||||||
// expected geojson features for 'client/mget/ok/1' fixture
|
|
||||||
var expected = [{ |
|
||||||
type: 'Feature', |
|
||||||
geometry: { |
|
||||||
type: 'Point', |
|
||||||
coordinates: [ -50.5, 100.1 ] |
|
||||||
}, |
|
||||||
properties: { |
|
||||||
id: 'myid1', |
|
||||||
type: 'mytype1', |
|
||||||
layer: 'mytype1', |
|
||||||
name: 'test name1', |
|
||||||
admin0: 'country1', |
|
||||||
admin1: 'state1', |
|
||||||
admin2: 'city1', |
|
||||||
text: 'test name1, city1, state1' |
|
||||||
} |
|
||||||
}, { |
|
||||||
type: 'Feature', |
|
||||||
geometry: { |
|
||||||
type: 'Point', |
|
||||||
coordinates: [ -51.5, 100.2 ] |
|
||||||
}, |
|
||||||
properties: { |
|
||||||
id: 'myid2', |
|
||||||
type: 'mytype2', |
|
||||||
layer: 'mytype2', |
|
||||||
name: 'test name2', |
|
||||||
admin0: 'country2', |
|
||||||
admin1: 'state2', |
|
||||||
admin2: 'city2', |
|
||||||
text: 'test name2, city2, state2' |
|
||||||
} |
|
||||||
}]; |
|
||||||
|
|
||||||
test('functional success', function(t) { |
|
||||||
var i = 0; |
|
||||||
var backend = mockBackend( 'client/suggest/ok/1', function( cmd ){ |
|
||||||
// the backend executes 2 commands, so we check them both
|
|
||||||
if( ++i === 1 ){ |
|
||||||
t.deepEqual(cmd, { body: { a: 'b' }, index: 'pelias' }, 'correct suggest command'); |
|
||||||
} else { |
|
||||||
t.deepEqual(cmd, {
|
|
||||||
body: { docs: [
|
|
||||||
{ _id: 'mockid1', _index: 'pelias', _type: 'mocktype' },
|
|
||||||
{ _id: 'mockid2', _index: 'pelias', _type: 'mocktype' } ]
|
|
||||||
}
|
|
||||||
}, 'correct mget command'); |
|
||||||
} |
|
||||||
}); |
|
||||||
var controller = setup( backend, mockQuery() ); |
|
||||||
var res = { |
|
||||||
status: function( code ){ |
|
||||||
t.equal(code, 200, 'status set'); |
|
||||||
return res; |
|
||||||
}, |
|
||||||
json: function( json ){ |
|
||||||
t.equal(typeof json, 'object', 'returns json'); |
|
||||||
t.equal(typeof json.date, 'number', 'date set'); |
|
||||||
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(); |
|
||||||
} |
|
||||||
}; |
|
||||||
controller( { clean: { a: 'b' } }, res ); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
// functionally test controller (backend failure)
|
|
||||||
module.exports.tests.functional_failure = function(test, common) { |
|
||||||
test('functional failure', function(t) { |
|
||||||
var backend = mockBackend( 'client/suggest/fail/1', function( cmd ){ |
|
||||||
t.deepEqual(cmd, { body: { a: 'b' }, index: 'pelias' }, 'correct backend command'); |
|
||||||
}); |
|
||||||
var controller = setup( backend, mockQuery() ); |
|
||||||
var next = function( message ){ |
|
||||||
t.equal(message,'a backend error occurred','error passed to errorHandler'); |
|
||||||
t.end(); |
|
||||||
}; |
|
||||||
controller( { clean: { a: 'b' } }, undefined, next ); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
module.exports.all = function (tape, common) { |
|
||||||
|
|
||||||
function test(name, testFunction) { |
|
||||||
return tape('GET /suggest/nearby ' + name, testFunction); |
|
||||||
} |
|
||||||
|
|
||||||
for( var testCase in module.exports.tests ){ |
|
||||||
module.exports.tests[testCase](test, common); |
|
||||||
} |
|
||||||
}; |
|
@ -0,0 +1,80 @@ |
|||||||
|
|
||||||
|
var query_mixer = require('../../../helper/queryMixer.json'); |
||||||
|
var indeces = require('../../../query/indeces'); |
||||||
|
|
||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
module.exports.tests.interface = function(test, common) { |
||||||
|
test('interface', function(t) { |
||||||
|
t.equal(typeof query_mixer, 'object', 'valid object'); |
||||||
|
t.equal(query_mixer.hasOwnProperty('suggest'), true, 'has suggest defined'); |
||||||
|
t.equal(query_mixer.hasOwnProperty('suggest_nearby'), true, 'has suggest_nearby defined'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.valid = function(test, common) { |
||||||
|
var valid_keys = ['layers', 'precision', 'fuzzy']; |
||||||
|
var valid_fuzzy_vals = ['AUTO', 0, 1, 2]; |
||||||
|
var valid_layer_vals = indeces; |
||||||
|
|
||||||
|
var isValidPrecision = function(t, precisionArr) { |
||||||
|
precisionArr.forEach(function(precision) { |
||||||
|
t.notEqual(isNaN(precision), true, precision + ' is a valid precision value'); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
var isValidLayer = function(t, layerArr) { |
||||||
|
layerArr.forEach(function(this_layer) { |
||||||
|
t.notEqual(valid_layer_vals.indexOf(this_layer), -1, 'layer value ' + this_layer + ' is valid');
|
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
var isValid = function(key, mix) { |
||||||
|
test('valid mix (' + key + ')' , function(t) { |
||||||
|
t.equal(keys.length > 0, true, 'valid key');
|
||||||
|
t.equal(Array.isArray( mix ), true, 'is an array'); |
||||||
|
t.equal(mix.length > 0, true, 'is not an empty array'); |
||||||
|
mix.forEach( function(this_mix) { |
||||||
|
t.notEqual(Object.getOwnPropertyNames(this_mix).length, 0, 'object not empty'); |
||||||
|
for (var keys in this_mix) { |
||||||
|
t.notEqual(valid_keys.indexOf(keys), -1, keys + ' is valid'); |
||||||
|
switch(keys) { |
||||||
|
case 'fuzzy': |
||||||
|
t.notEqual(valid_fuzzy_vals.indexOf(this_mix[keys]), -1, 'fuzzy value ' + this_mix[keys] + ' is valid'); |
||||||
|
break; |
||||||
|
case 'layers': |
||||||
|
t.equal(Array.isArray(this_mix[keys]), true, 'layers is an array'); |
||||||
|
t.equal(this_mix[keys].length > 0, true, 'layers is not an empty array'); |
||||||
|
isValidLayer(t, this_mix[keys]); |
||||||
|
break; |
||||||
|
case 'precision': |
||||||
|
t.equal(Array.isArray( this_mix[keys] ), true, keys + ' is an array'); |
||||||
|
if (this_mix[keys].length > 0) { |
||||||
|
isValidPrecision(t, this_mix[keys]); |
||||||
|
} |
||||||
|
break; |
||||||
|
default:
|
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
for (var keys in query_mixer) {
|
||||||
|
isValid(keys, query_mixer[keys]); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = function (tape, common) { |
||||||
|
|
||||||
|
function test(name, testFunction) { |
||||||
|
return tape('query_mixer: ' + name, testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( var testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
Loading…
Reference in new issue