Browse Source

Merge pull request #211 from pelias/master

Merge master into __BETA__
__BETA__
Diana Shkolnikov 9 years ago
parent
commit
ba3feca4ee
  1. 9
      DOCS.md
  2. 8
      app.js
  3. 6
      controller/place.js
  4. 12
      query/search.js
  5. 37
      sanitiser/_geo.js
  6. 1
      sanitiser/place.js
  7. 4
      test/ciao/place/msuccess.coffee
  8. 4
      test/ciao/place/success.coffee
  9. 5
      test/unit/controller/place.js
  10. 88
      test/unit/query/search.js
  11. 4
      test/unit/run.js
  12. 8
      test/unit/sanitiser/place.js
  13. 25
      test/unit/sanitiser/reverse.js
  14. 39
      test/unit/sanitiser/search.js
  15. 29
      test/unit/sanitiser/suggest.js

9
DOCS.md

@ -100,14 +100,15 @@ The reverse geocoding endpoint; matches a point on the planet to the name of tha
* `details` (default: `true`)
## /doc
## /place
The endpoint for retrieving one or more elasticsearch documents with specific ids.
The endpoint for retrieving one or more places with specific ids. These correspond
directly with Elasticsearch documents.
#### Required Parameters
* one of `id` or `ids`
* `id`:
* unique id of the document to be retrieved
* unique id of the places to be retrieved
* should be in the form of type:id, for example: `geoname:4163334`
* `ids`:
* if multiple docs are to be fetched in bulk, an array of ids
* if multiple places are to be fetched in bulk, an array of ids

8
app.js

@ -15,7 +15,7 @@ app.use( require('./middleware/jsonp') );
/** ----------------------- sanitisers ----------------------- **/
var sanitisers = {};
sanitisers.doc = require('./sanitiser/doc');
sanitisers.place = require('./sanitiser/place');
sanitisers.suggest = require('./sanitiser/suggest');
sanitisers.search = require('./sanitiser/search');
sanitisers.coarse = require('./sanitiser/coarse');
@ -25,7 +25,7 @@ sanitisers.reverse = require('./sanitiser/reverse');
var controllers = {};
controllers.index = require('./controller/index');
controllers.doc = require('./controller/doc');
controllers.place = require('./controller/place');
controllers.search = require('./controller/search');
/** ----------------------- routes ----------------------- **/
@ -33,8 +33,8 @@ controllers.search = require('./controller/search');
// api root
app.get( '/', controllers.index() );
// doc API
app.get( '/doc', sanitisers.doc.middleware, controllers.doc() );
// place API
app.get( '/place', sanitisers.place.middleware, controllers.place() );
// suggest APIs
app.get( '/suggest', sanitisers.search.middleware, controllers.search() );

6
controller/doc.js → controller/place.js

@ -1,14 +1,11 @@
var service = { mget: require('../service/mget') };
var geojsonify = require('../helper/geojsonify').search;
function setup( backend ){
// allow overriding of dependencies
backend = backend || require('../src/backend');
function controller( req, res, next ){
var query = req.clean.ids.map( function(id) {
return {
_index: 'pelias',
@ -18,7 +15,6 @@ function setup( backend ){
});
service.mget( backend, query, function( err, docs ){
// error handler
if( err ){ return next( err ); }
@ -30,9 +26,7 @@ function setup( backend ){
// respond
return res.status(200).json( geojson );
});
}
return controller;

12
query/search.js

@ -43,7 +43,10 @@ function generate( params ){
// add search condition to distance query
query.query.filtered.query.bool.must.push({
'match': {
'name.default': input
'name.default': {
'query': input,
'analyzer': 'peliasOneEdgeGram'
}
}
});
@ -51,7 +54,12 @@ function generate( params ){
// note: this is required for shingle/phrase matching
query.query.filtered.query.bool.should.push({
'match': {
'phrase.default': input
'phrase.default': {
'query': input,
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2
}
}
});

37
sanitiser/_geo.js

@ -1,3 +1,4 @@
var util = require( 'util' );
var isObject = require('is-object');
@ -46,26 +47,21 @@ function sanitize_bbox( clean, param ) {
return;
}
var bbox = [];
var bboxArr = param.split( ',' );
if( Array.isArray( bboxArr ) && bboxArr.length === 4 ) {
var bbox = bboxArr.map(parseFloat);
bbox = bboxArr.filter( function( latlon, index ) {
latlon = parseFloat( latlon, 10 );
return !(lat_lon_checks[(index % 2 === 0 ? 'lon' : 'lat')].is_invalid( latlon ));
});
if (bbox.some(isNaN)) {
return;
}
if( bbox.length === 4 ) {
clean.bbox = {
right: Math.max( bbox[0], bbox[2] ),
top: Math.max( bbox[1], bbox[3] ),
left: Math.min( bbox[0], bbox[2] ),
bottom: Math.min( bbox[1], bbox[3] )
};
} else {
throw new Error('invalid bbox');
}
}
}
@ -78,15 +74,12 @@ function sanitize_bbox( clean, param ) {
* @param {bool} latlon_is_required
*/
function sanitize_coord( coord, clean, param, latlon_is_required ) {
var value = parseFloat( param, 10 );
var value = parseFloat( param );
if ( !isNaN( value ) ) {
if( lat_lon_checks[coord].is_invalid( value ) ){
throw new Error( 'invalid ' + lat_lon_checks[coord].error_msg );
}
clean[coord] = value;
}
else if (latlon_is_required) {
throw new Error('missing ' + lat_lon_checks[coord].error_msg);
throw new Error( util.format( 'missing param \'%s\'', coord ) );
}
}
@ -96,19 +89,3 @@ function sanitize_zoom_level( clean, param ) {
clean.zoom = Math.min( Math.max( zoom, 1 ), 18 ); // max
}
}
var lat_lon_checks = {
lat: {
is_invalid: function is_invalid_lat(lat) {
return isNaN( lat ) || lat < -90 || lat > 90;
},
error_msg: 'param \'lat\': must be >-90 and <90'
},
lon: {
is_invalid: function is_invalid_lon(lon) {
return isNaN(lon) || lon < -180 || lon > 180;
},
error_msg: 'param \'lon\': must be >-180 and <180'
}
};

1
sanitiser/doc.js → sanitiser/place.js

@ -1,4 +1,3 @@
var _sanitize = require('../sanitiser/_sanitize'),
sanitizers = {
id: require('../sanitiser/_id'),

4
test/ciao/doc/msuccess.coffee → test/ciao/place/msuccess.coffee

@ -1,6 +1,6 @@
#> valid doc query
path: '/doc?id=geoname:4221195&id=geoname:4193595'
#> valid place query
path: '/place?id=geoname:4221195&id=geoname:4193595'
#? 200 ok
response.statusCode.should.equal 200

4
test/ciao/doc/success.coffee → test/ciao/place/success.coffee

@ -1,6 +1,6 @@
#> valid doc query
path: '/doc?id=geoname:4221195'
#> valid place query
path: '/place?id=geoname:4221195'
#? 200 ok
response.statusCode.should.equal 200

5
test/unit/controller/doc.js → test/unit/controller/place.js

@ -1,5 +1,4 @@
var setup = require('../../../controller/doc'),
var setup = require('../../../controller/place'),
mockBackend = require('../mock/backend');
module.exports.tests = {};
@ -15,7 +14,7 @@ module.exports.tests.interface = function(test, common) {
// functionally test controller (backend success)
module.exports.tests.functional_success = function(test, common) {
// expected geojson features for 'client/doc/ok/1' fixture
// expected geojson features for 'client/place/ok/1' fixture
var expected = [{
type: 'Feature',
geometry: {

88
test/unit/query/search.js

@ -82,12 +82,20 @@ var expected = {
'bool': {
'must': [{
'match': {
'name.default': 'test'
'name.default': {
'query': 'test',
'analyzer': 'peliasOneEdgeGram'
}
}
}],
'should': [{
'match': {
'phrase.default': 'test'
'phrase.default': {
'query': 'test',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2
}
}
}]
}
@ -164,12 +172,20 @@ module.exports.tests.query = function(test, common) {
'bool': {
'must': [{
'match': {
'name.default': 'test'
'name.default': {
'query': 'test',
'analyzer': 'peliasOneEdgeGram'
}
}
}],
'should': [{
'match': {
'phrase.default': 'test'
'phrase.default': {
'query': 'test',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2
}
}
}]
}
@ -204,12 +220,20 @@ module.exports.tests.query = function(test, common) {
'bool': {
'must': [{
'match': {
'name.default': 'test'
'name.default': {
'query': 'test',
'analyzer': 'peliasOneEdgeGram'
}
}
}],
'should': [{
'match': {
'phrase.default': 'test'
'phrase.default': {
'query': 'test',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2
}
}
}]
}
@ -262,7 +286,10 @@ module.exports.tests.query = function(test, common) {
'must': [
{
'match': {
'name.default': '123 main st'
'name.default': {
'query': '123 main st',
'analyzer': 'peliasOneEdgeGram'
}
}
}
],
@ -339,7 +366,12 @@ module.exports.tests.query = function(test, common) {
},
{
'match': {
'phrase.default': '123 main st'
'phrase.default': {
'query': '123 main st',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2
}
}
}
]
@ -456,7 +488,10 @@ module.exports.tests.query = function(test, common) {
'must': [
{
'match': {
'name.default': 'soho grand'
'name.default': {
'query': 'soho grand',
'analyzer': 'peliasOneEdgeGram'
}
}
}
],
@ -498,7 +533,12 @@ module.exports.tests.query = function(test, common) {
},
{
'match': {
'phrase.default': 'soho grand'
'phrase.default': {
'query': 'soho grand',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2
}
}
}
]
@ -616,13 +656,16 @@ module.exports.tests.query = function(test, common) {
'must': [
{
'match': {
'name.default': '1 water st'
'name.default': {
'query': '1 water st',
'analyzer': 'peliasOneEdgeGram'
}
}
}
],
'should': [
{
match: {
'match': {
'address.number': {
'query': 1,
'boost': address_weights.number
@ -630,7 +673,7 @@ module.exports.tests.query = function(test, common) {
}
},
{
match: {
'match': {
'address.street': {
'query': 'water st',
'boost': address_weights.street
@ -661,23 +704,28 @@ module.exports.tests.query = function(test, common) {
}
},
{
match: {
local_admin: 'manhattan'
'match': {
'local_admin': 'manhattan'
}
},
{
match: {
locality: 'manhattan'
'match': {
'locality': 'manhattan'
}
},
{
match: {
neighborhood: 'manhattan'
'match': {
'neighborhood': 'manhattan'
}
},
{
'match': {
'phrase.default': '1 water st'
'phrase.default': {
'query': '1 water st',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2
}
}
}
]

4
test/unit/run.js

@ -4,14 +4,14 @@ var common = {};
var tests = [
require('./controller/index'),
require('./controller/doc'),
require('./controller/place'),
require('./controller/search'),
require('./service/mget'),
require('./service/search'),
require('./sanitiser/suggest'),
require('./sanitiser/search'),
require('./sanitiser/reverse'),
require('./sanitiser/doc'),
require('./sanitiser/place'),
require('./sanitiser/coarse'),
require('./query/indeces'),
require('./query/sort'),

8
test/unit/sanitiser/doc.js → test/unit/sanitiser/place.js

@ -1,7 +1,7 @@
var doc = require('../../../sanitiser/doc'),
_sanitize = doc.sanitize,
middleware = doc.middleware,
var place = require('../../../sanitiser/place'),
_sanitize = place.sanitize,
middleware = place.middleware,
indeces = require('../../../query/indeces'),
delimiter = ':',
defaultLengthError = function(input) { return 'invalid param \''+ input + '\': text length, must be >0'; },
@ -190,7 +190,7 @@ module.exports.tests.middleware_success = function(test, common) {
module.exports.all = function (tape, common) {
function test(name, testFunction) {
return tape('SANTIZE /doc ' + name, testFunction);
return tape('SANTIZE /place ' + name, testFunction);
}
for( var testCase in module.exports.tests ){

25
test/unit/sanitiser/reverse.js

@ -3,7 +3,7 @@ var suggest = require('../../../sanitiser/reverse'),
_sanitize = suggest.sanitize,
middleware = suggest.middleware,
delim = ',',
defaultError = 'missing param \'lat\': must be >-90 and <90',
defaultError = 'missing param \'lat\'',
defaultClean = { lat:0,
layers: [ 'geoname', 'osmnode', 'osmway', 'admin0', 'admin1', 'admin2', 'neighborhood',
'locality', 'local_admin', 'osmaddress', 'openaddresses' ],
@ -32,8 +32,8 @@ module.exports.tests.interface = function(test, common) {
module.exports.tests.sanitize_lat = function(test, common) {
var lats = {
invalid: [ -181, -120, -91, 91, 120, 181 ],
valid: [ 0, 45, 90, -0, '0', '45', '90' ],
invalid: [],
valid: [ 0, 45, 90, -0, '0', '45', '90', -181, -120, -91, 91, 120, 181 ],
missing: ['', undefined, null]
};
test('invalid lat', function(t) {
@ -59,7 +59,7 @@ module.exports.tests.sanitize_lat = function(test, common) {
test('missing lat', function(t) {
lats.missing.forEach( function( lat ){
sanitize({ lat: lat, lon: 0 }, function( err, clean ){
t.equal(err, 'missing param \'lat\': must be >-90 and <90', 'latitude is a required field');
t.equal(err, 'missing param \'lat\'', 'latitude is a required field');
t.equal(clean, undefined, 'clean not set');
});
});
@ -69,20 +69,9 @@ module.exports.tests.sanitize_lat = function(test, common) {
module.exports.tests.sanitize_lon = function(test, common) {
var lons = {
invalid: [ -360, -181, 181, 360 ],
valid: [ -180, -1, -0, 0, 45, 90, '-180', '0', '180' ],
valid: [ -360, -181, 181, -180, -1, -0, 0, 45, 90, '-180', '0', '180' ],
missing: ['', undefined, null]
};
test('invalid lon', function(t) {
lons.invalid.forEach( function( lon ){
sanitize({ input: 'test', lat: 0, lon: lon }, function( err, clean ){
t.equal(err, 'invalid param \'lon\': must be >-180 and <180', lon + ' is an invalid longitude');
t.equal(clean, undefined, 'clean not set');
});
});
t.end();
});
test('valid lon', function(t) {
lons.valid.forEach( function( lon ){
sanitize({ input: 'test', lat: 0, lon: lon }, function( err, clean ){
@ -97,7 +86,7 @@ module.exports.tests.sanitize_lon = function(test, common) {
test('missing lon', function(t) {
lons.missing.forEach( function( lon ){
sanitize({ lat: 0, lon: lon }, function( err, clean ){
t.equal(err, 'missing param \'lon\': must be >-180 and <180', 'longitude is a required field');
t.equal(err, 'missing param \'lon\'', 'longitude is a required field');
t.equal(clean, undefined, 'clean not set');
});
});
@ -287,7 +276,7 @@ module.exports.tests.middleware_failure = function(test, common) {
t.equal(code, 400, 'status set');
}};
var next = function( message ){
t.equal(message, defaultError);
t.equals(message, defaultError);
t.end();
};
middleware( {}, res, next );

39
test/unit/sanitiser/search.js

@ -84,8 +84,8 @@ module.exports.tests.sanitize_input_with_delim = function(test, common) {
module.exports.tests.sanitize_lat = function(test, common) {
var lats = {
invalid: [ -181, -120, -91, 91, 120, 181 ],
valid: [ 0, 45, 90, -0, '0', '45', '90' ]
invalid: [],
valid: [ 0, 45, 90, -0, '0', '45', '90', -181, -120, -91, 91, 120, 181 ]
};
test('invalid lat', function(t) {
lats.invalid.forEach( function( lat ){
@ -113,19 +113,8 @@ module.exports.tests.sanitize_lat = function(test, common) {
module.exports.tests.sanitize_lon = function(test, common) {
var lons = {
invalid: [ -360, -181, 181, 360 ],
valid: [ -180, -1, -0, 0, 45, 90, '-180', '0', '180' ]
valid: [ -381, -181, -180, -1, -0, 0, 45, 90, '-180', '0', '180', 181 ]
};
test('invalid lon', function(t) {
lons.invalid.forEach( function( lon ){
sanitize({ input: 'test', lat: 0, lon: lon }, function( err, clean ){
t.equal(err, 'invalid param \'lon\': must be >-180 and <180', lon + ' is an invalid longitude');
t.equal(clean, undefined, 'clean not set');
});
});
t.end();
});
test('valid lon', function(t) {
lons.valid.forEach( function( lon ){
sanitize({ input: 'test', lat: 0, lon: lon }, function( err, clean ){
@ -178,27 +167,37 @@ module.exports.tests.sanitize_optional_geo = function(test, common) {
module.exports.tests.sanitize_bbox = function(test, common) {
var bboxes = {
invalid_coordinates: [
'-181,90,34,-180', // invalid top_right lon, bottom_left lat
'-170,91,-181,45', // invalid top_right lat, bottom_left lon
'-181,91,181,-91', // invalid top_right lon/lat, bottom_left lon/lat
'91, -181,-91,181',// invalid - spaces between coordinates
],
invalid: [
'91;-181,-91,181', // invalid - semicolon between coordinates
'these,are,not,numbers',
'0,0,0,notANumber',
',,,',
'91, -181, -91', // invalid - missing a coordinate
'123,12', // invalid - missing coordinates
'' // invalid - empty param
],
valid: [
'-179,90,34,-80', // valid top_right lon/lat, bottom_left lon/lat
'0,0,0,0' // valid top_right lat/lon, bottom_left lat/lon
'0,0,0,0',
'10,20,30,40',
'-40,-20,10,30',
'-40,-20,10,30',
'-1200,20,1000,20',
'-1400,90,1400,-90',
// wrapped latitude coordinates
'-181,90,34,-180',
'-170,91,-181,45',
'-181,91,181,-91',
'91, -181,-91,11',
'91, -11,-91,181'
]
};
test('invalid bbox coordinates', function(t) {
bboxes.invalid_coordinates.forEach( function( bbox ){
sanitize({ input: 'test', bbox: bbox }, function( err, clean ){
t.equal(err, 'invalid bbox', bbox + ' is invalid');
t.ok(err.match(/Invalid (lat|lon)/), bbox + ' is invalid');
t.equal(clean, undefined, 'clean not set');
});
});

29
test/unit/sanitiser/suggest.js

@ -82,8 +82,8 @@ module.exports.tests.sanitize_input_with_delim = function(test, common) {
module.exports.tests.sanitize_lat = function(test, common) {
var lats = {
invalid: [ -181, -120, -91, 91, 120, 181 ],
valid: [ 0, 45, 90, -0, '0', '45', '90' ]
invalid: [],
valid: [ 0, 45, 90, -0, '0', '45', '90', -181, -120, -91, 91, 120, 181 ]
};
test('invalid lat', function(t) {
lats.invalid.forEach( function( lat ){
@ -110,19 +110,8 @@ module.exports.tests.sanitize_lat = function(test, common) {
module.exports.tests.sanitize_lon = function(test, common) {
var lons = {
invalid: [ -360, -181, 181, 360 ],
valid: [ -180, -1, -0, 0, 45, 90, '-180', '0', '180' ]
valid: [ -381, -181, -180, -1, -0, 0, 45, 90, '-180', '0', '180', 181 ]
};
test('invalid lon', function(t) {
lons.invalid.forEach( function( lon ){
sanitize({ input: 'test', lat: 0, lon: lon }, function( err, clean ){
t.equal(err, 'invalid param \'lon\': must be >-180 and <180', lon + ' is an invalid longitude');
t.equal(clean, undefined, 'clean not set');
});
});
t.end();
});
test('valid lon', function(t) {
lons.valid.forEach( function( lon ){
sanitize({ input: 'test', lat: 0, lon: lon }, function( err, clean ){
@ -140,10 +129,6 @@ module.exports.tests.sanitize_lon = function(test, common) {
module.exports.tests.sanitize_bbox = function(test, common) {
var bboxes = {
invalid_coordinates: [
'-181,90,34,-180', // invalid top_right lon, bottom_left lat
'-170,91,-181,45', // invalid top_right lat, bottom_left lon
'-181,91,181,-91', // invalid top_right lon/lat, bottom_left lon/lat
'91, -181,-91,181',// invalid - spaces between coordinates
],
invalid: [
'91;-181,-91,181', // invalid - semicolon between coordinates
@ -153,14 +138,18 @@ module.exports.tests.sanitize_bbox = function(test, common) {
],
valid: [
'-179,90,34,-80', // valid top_right lon/lat, bottom_left lon/lat
'0,0,0,0' // valid top_right lat/lon, bottom_left lat/lon
'0,0,0,0', // valid top_right lat/lon, bottom_left lat/lon
'-181,90,34,-180', // wrapped top_right lon, bottom_left lat
'-170,91,-181,45', // wrapped top_right lat, bottom_left lon
'-181,91,181,-91', // wrapped top_right lon/lat, bottom_left lon/lat
'91, -181,-91,181',// valid - spaces between coordinates
]
};
test('invalid bbox coordinates', function(t) {
bboxes.invalid_coordinates.forEach( function( bbox ){
sanitize({ input: 'test', lat: 0, lon: 0, bbox: bbox }, function( err, clean ){
t.equal(err, 'invalid bbox', bbox + ' is invalid');
t.ok(err.match(/Invalid (lat|lon)/), bbox + ' is invalid');
t.equal(clean, undefined, 'clean not set');
});
});

Loading…
Cancel
Save