Browse Source

Merge pull request #60 from pelias/search-bbox

search with optional bbox
pull/64/head
Harish Krishna 10 years ago
parent
commit
772ec686b7
  1. 2
      package.json
  2. 4
      query/search.js
  3. 83
      sanitiser/_geo.js
  4. 47
      sanitiser/_latlonzoom.js
  5. 2
      sanitiser/reverse.js
  6. 2
      sanitiser/suggest.js
  7. 56
      test/unit/query/search.js
  8. 60
      test/unit/sanitiser/suggest.js

2
package.json

@ -36,7 +36,7 @@
"express": "^4.8.8",
"geojson": "^0.2.0",
"geojson-extent": "^0.3.1",
"geopipes-elasticsearch-backend": "0.0.8",
"geopipes-elasticsearch-backend": "git://github.com/geopipes/elasticsearch-backend#geo_bbox",
"is-object": "^1.0.1",
"pelias-esclient": "0.0.25",
"toobusy": "^0.2.4"

4
query/search.js

@ -11,6 +11,10 @@ function generate( params ){
var query = queries.distance( centroid, { size: params.size } );
if (params.bbox) {
query = queries.bbox ( centroid, { size: params.size, bbox: params.bbox } );
}
// add search condition to distance query
query.query.filtered.query = {
query_string : {

83
sanitiser/_geo.js

@ -0,0 +1,83 @@
// validate inputs, convert types and apply defaults
function sanitize( req ){
var clean = req.clean || {};
var params= req.query;
// ensure the input params are a valid object
if( Object.prototype.toString.call( params ) !== '[object Object]' ){
params = {};
}
var is_invalid_lat = function(lat) {
return isNaN( lat ) || lat < -90 || lat > 90;
};
var is_invalid_lon = function(lon) {
return isNaN( lon ) || lon < -180 || lon > 180;
};
// lat
var lat = parseFloat( params.lat, 10 );
if( is_invalid_lat(lat) ){
return {
'error': true,
'message': 'invalid param \'lat\': must be >-90 and <90'
};
}
clean.lat = lat;
// lon
var lon = parseFloat( params.lon, 10 );
if( is_invalid_lon(lon) ){
return {
'error': true,
'message': 'invalid param \'lon\': must be >-180 and <180'
};
}
clean.lon = lon;
// zoom level
var zoom = parseInt( params.zoom, 10 );
if( !isNaN( zoom ) ){
clean.zoom = Math.min( Math.max( zoom, 1 ), 18 ); // max
} else {
clean.zoom = 10; // default
}
// bbox
// bbox = bottom_left lat, bottom_left lon, top_right lat, top_right lon
// bbox = left,bottom,right,top
// bbox = min Longitude , min Latitude , max Longitude , max Latitude
if (params.bbox) {
var bbox = [];
var bboxArr = params.bbox.split(',');
if( Array.isArray(bboxArr) && bboxArr.length === 4 ) {
bbox = bboxArr.filter(function(latlon, index) {
latlon = parseFloat(latlon, 10);
return !(index % 2 === 0 ? is_invalid_lat(latlon) : is_invalid_lon(latlon));
});
if (bbox.length === 4) {
clean.bbox = {
top : Math.max(bbox[0], bbox[2]),
right : Math.max(bbox[1], bbox[3]),
bottom: Math.min(bbox[0], bbox[2]),
left : Math.min(bbox[1], bbox[3])
};
} else {
return {
'error': true,
'message': 'invalid bbox'
};
}
}
}
req.clean = clean;
return { 'error': false };
}
// export function
module.exports = sanitize;

47
sanitiser/_latlonzoom.js

@ -1,47 +0,0 @@
// validate inputs, convert types and apply defaults
function sanitize( req ){
var clean = req.clean || {};
var params= req.query;
// ensure the input params are a valid object
if( Object.prototype.toString.call( params ) !== '[object Object]' ){
params = {};
}
// lat
var lat = parseFloat( params.lat, 10 );
if( isNaN( lat ) || lat < -90 || lat > 90 ){
return {
'error': true,
'message': 'invalid param \'lat\': must be >-90 and <90'
};
}
clean.lat = lat;
// lon
var lon = parseFloat( params.lon, 10 );
if( isNaN( lon ) || lon < -180 || lon > 180 ){
return {
'error': true,
'message': 'invalid param \'lon\': must be >-180 and <180'
};
}
clean.lon = lon;
// zoom level
var zoom = parseInt( params.zoom, 10 );
if( !isNaN( zoom ) ){
clean.zoom = Math.min( Math.max( zoom, 1 ), 18 ); // max
} else {
clean.zoom = 10; // default
}
req.clean = clean;
return { 'error': false };
}
// export function
module.exports = sanitize;

2
sanitiser/reverse.js

@ -2,7 +2,7 @@
var logger = require('../src/logger'),
_sanitize = require('../sanitiser/_sanitize'),
sanitiser = {
latlonzoom: require('../sanitiser/_latlonzoom'),
latlonzoom: require('../sanitiser/_geo'),
size: function( req ) {
var size = require('../sanitiser/_size');
return size(req, 1);

2
sanitiser/suggest.js

@ -5,7 +5,7 @@ var logger = require('../src/logger'),
input: require('../sanitiser/_input'),
size: require('../sanitiser/_size'),
layers: require('../sanitiser/_layers'),
latlonzoom: require('../sanitiser/_latlonzoom')
latlonzoom: require('../sanitiser/_geo')
};
var sanitize = function(req, cb) { _sanitize(req, sanitizers, cb); };

56
test/unit/query/search.js

@ -16,15 +16,57 @@ module.exports.tests.query = function(test, common) {
input: 'test', size: 10,
lat: 29.49136, lon: -82.50622,
bbox: {
bottom_left: {
lat: 11.51053655297385,
lon: -103.16362455862279
},
top_right: {
lat: 47.472183447026154,
lon: -61.84881544137721
top: 47.47,
right: -61.84,
bottom: 11.51,
left: -103.16
},
layers: ['test']
});
var expected = {
'query': {
'filtered': {
'query': {
'query_string': {
'query': 'test',
'fields': [
'name.default'
],
'default_operator': 'OR'
}
},
'filter': {
'bool': {
'must': [
{
'geo_bounding_box': {
'center_point': {
'top': '47.47',
'right': '-61.84',
'bottom':'11.51',
'left': '-103.16'
},
'_cache': true
}
}
]
}
}
}
},
'sort': [],
'size': 10
};
t.deepEqual(query, expected, 'valid search query');
t.end();
});
test('valid query without bbox', function(t) {
var query = generate({
input: 'test', size: 10,
lat: 29.49136, lon: -82.50622,
layers: ['test']
});

60
test/unit/sanitiser/suggest.js

@ -109,6 +109,66 @@ module.exports.tests.sanitize_lon = function(test, common) {
});
};
module.exports.tests.sanitize_bbox = function(test, common) {
var bboxes = {
invalid_coordinates: [
'90,-181,-180,34', // invalid top_right lon, bottom_left lat
'91,-170,45,-181', // invalid top_right lat, bottom_left lon
'91,-181,-91,181', // invalid top_right lat/lon, bottom_left lat/lon
'91, -181,-91,181',// invalid - spaces between coordinates
],
invalid: [
'91;-181,-91,181', // invalid - semicolon between coordinates
'91, -181, -91', // invalid - missing a coordinate
'123,12', // invalid - missing coordinates
'' // invalid - empty param
],
valid: [
'90,-179,-80,34', // valid top_right lat/lon, bottom_left lat/lon
'0,0,0,0' // valid top_right lat/lon, bottom_left lat/lon
]
};
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.equal(clean, undefined, 'clean not set');
});
});
t.end();
});
test('invalid bbox', function(t) {
bboxes.invalid.forEach( function( bbox ){
sanitize({ input: 'test', lat: 0, lon: 0, bbox: bbox }, function( err, clean ){
var expected = JSON.parse(JSON.stringify( defaultClean ));
t.equal(err, undefined, 'no error');
t.deepEqual(clean, expected, 'falling back on 50km distance from centroid');
});
});
t.end();
});
test('valid bbox', function(t) {
bboxes.valid.forEach( function( bbox ){
sanitize({ input: 'test', lat: 0, lon: 0, bbox: bbox }, function( err, clean ){
var expected = JSON.parse(JSON.stringify( defaultClean ));
var bboxArray = bbox.split(',').map(function(i) {
return parseInt(i);
});
expected.bbox = {
top : Math.max(bboxArray[0], bboxArray[2]),
right : Math.max(bboxArray[1], bboxArray[3]),
bottom: Math.min(bboxArray[0], bboxArray[2]),
left : Math.min(bboxArray[1], bboxArray[3])
};
t.equal(err, undefined, 'no error');
t.deepEqual(clean, expected, 'clean set correctly (' + bbox + ')');
});
});
t.end();
});
};
module.exports.tests.sanitize_zoom = function(test, common) {
test('invalid zoom value', function(t) {
sanitize({ zoom: 'a', input: 'test', lat: 0, lon: 0 }, function( err, clean ){

Loading…
Cancel
Save