Browse Source

current dev branch

pull/2/head
Peter Johnson 10 years ago
parent
commit
f48239a7d4
  1. 3
      .gitignore
  2. 32
      controller/suggest.js
  3. 7
      express.js
  4. 5
      index.js
  5. 4
      package.json
  6. 12
      query/indeces.js
  7. 31
      query/suggest.js
  8. 77
      sanitiser/suggest.js
  9. 21
      src/backend.js
  10. 5
      src/logger.js
  11. 33
      src/responder.js

3
.gitignore vendored

@ -1 +1,2 @@
node_modules node_modules
*.log

32
controller/suggest.js

@ -0,0 +1,32 @@
var logger = require('../src/logger'),
responder = require('../src/responder'),
query = require('../query/suggest'),
backend = require('../src/backend');
module.exports = function( req, res, next ){
var reply = {
date: new Date().getTime(),
body: []
};
var cmd = {
index: 'pelias',
body: query( req.clean ) // generate query from clean params
};
// Proxy request to ES backend & map response to a valid FeatureCollection
backend().client.suggest( cmd, function( err, data ){
if( err ){ return responder.error( req, res, next, err ); }
if( data && data.pelias && data.pelias.length ){
// map options to reply body
reply.body = data['pelias'][0].options;
}
return responder.cors( req, res, reply );
});
};

7
express.js

@ -1,6 +1,9 @@
var express = require('express'); var express = require('express'),
var app = express(); app = express();
// middleware modules
// app.use( require('cookie-parser')() );
// enable client-side caching of 60s by default // enable client-side caching of 60s by default
app.use(function(req, res, next){ app.use(function(req, res, next){

5
index.js

@ -2,6 +2,9 @@
var app = require('./express'); var app = require('./express');
// api root // api root
app.get( '/', require('./controller/index' ) ); app.get( '/', require('./controller/index') );
// suggest API
app.get( '/suggest', require('./sanitiser/suggest'), require('./controller/suggest') );
app.listen( process.env.PORT || 3100 ); app.listen( process.env.PORT || 3100 );

4
package.json

@ -30,7 +30,9 @@
"elasticsearch": ">=1.2.1" "elasticsearch": ">=1.2.1"
}, },
"dependencies": { "dependencies": {
"express": "^4.8.8" "express": "^4.8.8",
"geopipes-elasticsearch-backend": "0.0.7",
"pelias-esclient": "0.0.25"
}, },
"devDependencies": { "devDependencies": {
"ciao": "^0.3.4", "ciao": "^0.3.4",

12
query/indeces.js

@ -0,0 +1,12 @@
// querable indeces
module.exports = [
'geoname',
'osmnode',
'osmway',
'admin0',
'admin1',
'admin2',
'neighborhood'
];

31
query/suggest.js

@ -0,0 +1,31 @@
var logger = require('../src/logger');
// Build pelias suggest query
function generate( params ){
var cmd = {
'pelias' : {
'text' : params.input,
'completion' : {
'size' : params.size,
'field' : 'suggest',
'context': {
'dataset': params.layers,
'location': {
'value': [ params.lon, params.lat ],
// // commented out until they fix the precision bug in ES 1.3.3
'precision': 2 // params.zoom > 9 ? 3 : (params.zoom > 7 ? 2 : 1)
}
}
}
}
};
logger.log( 'cmd', JSON.stringify( cmd, null, 2 ) );
return cmd;
}
module.exports = generate;

77
sanitiser/suggest.js

@ -0,0 +1,77 @@
var logger = require('../src/logger'),
indeces = require('../query/indeces');
// validate inputs, convert types and apply defaults
function sanitize( params, cb ){
var clean = {};
// ensure the input params are a valid object
if( Object.prototype.toString.call( params ) !== '[object Object]' ){
params = {};
}
// input text
if('string' !== typeof params.input || !params.input.length){
return cb( 'invalid input text length, must be >0' );
}
clean.input = params.input;
// total results
var size = parseInt( params.size, 10 );
if( !isNaN( size ) ){
clean.size = Math.min( size, 40 ); // max
} else {
clean.size = 10; // default
}
// which layers to query
if('string' === typeof params.layers && params.layers.length){
var layers = params.layers.split(',').map( function( layer ){
return layer.toLowerCase(); // lowercase inputs
});
for( var x=0; x<layers.length; x++ ){
if( -1 === indeces.indexOf( layers[x] ) ){
return cb( 'invalid layer, must be one or more of ' + layers.join(',') );
}
}
clean.layers = layers;
}
else {
clean.layers = indeces; // default (all layers)
}
// lat
var lat = parseFloat( params.lat, 10 );
if( isNaN( lat ) || lat < 0 || lat > 90 ){
return cb( 'invalid lat, must be >0 and <90' );
}
clean.lat = lat;
// lon
var lon = parseFloat( params.lon, 10 );
if( isNaN( lon ) || lon < -180 || lon > 180 ){
return cb( 'invalid lon, must be >-180 and <180' );
}
clean.lon = lon;
// zoom level
var zoom = parseInt( params.zoom, 10 );
if( !isNaN( zoom ) ){
clean.zoom = Math.min( zoom, 18 ); // max
} else {
clean.zoom = 10; // default
}
return cb( undefined, clean );
}
module.exports = function( req, res, next ){
sanitize( req.query, function( err, clean ){
if( err ){ next( err ); }
req.clean = clean;
next();
});
};

21
src/backend.js

@ -0,0 +1,21 @@
var Backend = require('geopipes-elasticsearch-backend'),
backends = {},
client;
// set env specific client
if( process.env.NODE_ENV === 'test' ){
client = require('./pelias-mockclient');
} else {
client = require('pelias-esclient')();
}
function getBackend( index, type ){
var key = ( index + ':' + type );
if( !backends[key] ){
backends[key] = new Backend( client, index, type );
}
return backends[key];
}
module.exports = getBackend;

5
src/logger.js

@ -0,0 +1,5 @@
module.exports = {
log: console.log.bind( console ),
warn: console.warn.bind( console ),
error: console.error.bind( console )
};

33
src/responder.js

@ -0,0 +1,33 @@
// send a reply that is capable of JSON, CORS and JSONP
function cors( req, res, obj ){
res.header('Charset','utf8');
res.header('Cache-Control','public,max-age=60');
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET');
res.header('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
res.header('Access-Control-Allow-Credentials', true);
res.header('X-Powered-By', 'pelias');
// jsonp
if( req.query && req.query.callback ){
res.header('Content-type','application/javascript');
return res.send( req.query.callback + '('+ JSON.stringify( obj ) + ');' );
}
// regular json
res.header('Content-type','application/json');
return res.json( obj );
}
// send an error
function error( req, res, next, err ){
console.error( 'application error:', err );
// mask error from user (contains paths)
return cors( req, res, { error: 'application error' } );
}
module.exports = {
cors: cors,
error: error
};
Loading…
Cancel
Save