Browse Source

Merge pull request #115 from pelias/reverse-categories

Reverse categories
pull/93/merge
Diana Shkolnikov 10 years ago
parent
commit
e146829618
  1. 5
      .gitignore
  2. 2
      .jshintignore
  3. 6
      README.md
  4. 4
      package.json
  5. 15
      query/reverse.js
  6. 38
      sanitiser/_categories.js
  7. 7
      sanitiser/reverse.js
  8. 65
      test/unit/query/reverse.js
  9. 42
      test/unit/sanitiser/reverse.js

5
.gitignore vendored

@ -1,2 +1,5 @@
node_modules node_modules
*.log coverage
.idea
*.log
reports

2
.jshintignore

@ -1 +1,3 @@
node_modules node_modules
coverage
reports

6
README.md

@ -58,6 +58,12 @@ $ npm test
$ npm run docs $ npm run docs
``` ```
### Code Coverage
```bash
$ npm run coverage
```
### Continuous Integration ### Continuous Integration
Travis tests every release against node version `0.10` Travis tests every release against node version `0.10`

4
package.json

@ -8,9 +8,10 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "node index.js", "start": "node index.js",
"test": "npm run unit", "test": "npm run unit && npm run coverage",
"unit": "node test/unit/run.js | tap-spec", "unit": "node test/unit/run.js | tap-spec",
"ciao": "node node_modules/ciao/bin/ciao -c test/ciao.json test/ciao", "ciao": "node node_modules/ciao/bin/ciao -c test/ciao.json test/ciao",
"coverage": "node_modules/.bin/istanbul cover test/unit/run.js",
"audit": "npm shrinkwrap; node node_modules/nsp/bin/nspCLI.js audit-shrinkwrap; rm npm-shrinkwrap.json;", "audit": "npm shrinkwrap; node node_modules/nsp/bin/nspCLI.js audit-shrinkwrap; rm npm-shrinkwrap.json;",
"docs": "rm -r docs; cd test/ciao; node ../../node_modules/ciao/bin/ciao -c ../ciao.json . -d ../../docs" "docs": "rm -r docs; cd test/ciao; node ../../node_modules/ciao/bin/ciao -c ../ciao.json . -d ../../docs"
}, },
@ -44,6 +45,7 @@
}, },
"devDependencies": { "devDependencies": {
"ciao": "^0.3.4", "ciao": "^0.3.4",
"istanbul": "^0.3.13",
"jshint": "^2.5.6", "jshint": "^2.5.6",
"nsp": "^0.3.0", "nsp": "^0.3.0",
"precommit-hook": "^1.0.7", "precommit-hook": "^1.0.7",

15
query/reverse.js

@ -1,7 +1,6 @@
var logger = require('../src/logger'), var queries = require('geopipes-elasticsearch-backend').queries,
queries = require('geopipes-elasticsearch-backend').queries, sort = require('./sort');
sort = require('../query/sort');
function generate( params ){ function generate( params ){
@ -13,7 +12,17 @@ function generate( params ){
var query = queries.distance( centroid, { size: params.size || 1 } ); var query = queries.distance( centroid, { size: params.size || 1 } );
query.sort = query.sort.concat(sort); query.sort = query.sort.concat(sort);
if ( params.categories && params.categories.length > 0 ) {
addCategoriesFilter( query, params.categories );
}
return query; return query;
} }
function addCategoriesFilter( query, categories ) {
query.query.filtered.filter.bool.must.push({
terms: { category: categories }
});
}
module.exports = generate; module.exports = generate;

38
sanitiser/_categories.js

@ -0,0 +1,38 @@
var isObject = require('is-object');
// 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( !isObject( params ) ){
params = {};
}
// default case (no categories specified in GET params)
if('string' !== typeof params.categories || !params.categories.length){
clean.categories = [];
}
else {
// parse GET params
clean.categories = params.categories.split(',')
.map(function (cat) {
return cat.toLowerCase().trim(); // lowercase inputs
})
.filter( function( cat ) {
return ( cat.length > 0 );
});
}
// pass validated params to next middleware
req.clean = clean;
return { 'error': false };
}
// export function
module.exports = sanitize;

7
sanitiser/reverse.js

@ -1,6 +1,5 @@
var logger = require('../src/logger'), var _sanitize = require('../sanitiser/_sanitize'),
_sanitize = require('../sanitiser/_sanitize'),
sanitiser = { sanitiser = {
latlonzoom: function( req ) { latlonzoom: function( req ) {
var geo = require('../sanitiser/_geo'); var geo = require('../sanitiser/_geo');
@ -10,6 +9,10 @@ var logger = require('../src/logger'),
size: function( req ) { size: function( req ) {
var size = require('../sanitiser/_size'); var size = require('../sanitiser/_size');
return size(req, 1); return size(req, 1);
},
categories: function ( req ) {
var categories = require('../sanitiser/_categories');
return categories(req);
} }
}; };

65
test/unit/query/reverse.js

@ -111,6 +111,71 @@ module.exports.tests.query = function(test, common) {
}); });
t.end(); t.end();
}); });
test('valid query with categories', function(t) {
var params = { lat: 29.49136, lon: -82.50622, categories: ['food', 'education', 'entertainment'] };
var query = generate(params);
var expected = {
'query': {
'filtered': {
'query': {
'match_all': {}
},
'filter': {
'bool': {
'must': [
{
'geo_distance': {
'distance': '50km',
'distance_type': 'plane',
'optimize_bbox': 'indexed',
'_cache': true,
'center_point': {
'lat': '29.49',
'lon': '-82.51'
}
}
},
{
'terms': {
'category': params.categories
}
}
]
}
}
}
},
'sort': [
'_score',
{
'_geo_distance': {
'center_point': {
'lat': 29.49136,
'lon': -82.50622
},
'order': 'asc',
'unit': 'km'
}
}
].concat(sort.slice(1)),
'size': 1,
'track_scores': true
};
t.deepEqual(query, expected, 'valid reverse query with categories');
// test different sizes
var sizes = [1,2,10,undefined,null];
sizes.forEach( function(size) {
params.size = size;
query = generate(params);
expected.size = size ? size : 1;
t.deepEqual(query, expected, 'valid reverse query for size: '+ size);
});
t.end();
});
}; };
module.exports.all = function (tape, common) { module.exports.all = function (tape, common) {

42
test/unit/sanitiser/reverse.js

@ -8,7 +8,8 @@ var suggest = require('../../../sanitiser/reverse'),
layers: [ 'geoname', 'osmnode', 'osmway', 'admin0', 'admin1', 'admin2', 'neighborhood', layers: [ 'geoname', 'osmnode', 'osmway', 'admin0', 'admin1', 'admin2', 'neighborhood',
'locality', 'local_admin', 'osmaddress', 'openaddresses' ], 'locality', 'local_admin', 'osmaddress', 'openaddresses' ],
lon: 0, lon: 0,
size: 1 size: 1,
categories: []
}, },
sanitize = function(query, cb) { _sanitize({'query':query}, cb); }; sanitize = function(query, cb) { _sanitize({'query':query}, cb); };
@ -200,6 +201,45 @@ module.exports.tests.sanitize_layers = function(test, common) {
}); });
}; };
module.exports.tests.sanitize_categories = function(test, common) {
var queryParams = { input: 'test', lat: 0, lon: 0 };
test('unspecified', function(t) {
queryParams.categories = undefined;
sanitize(queryParams, function( err, clean ){
t.deepEqual(clean.categories, defaultClean.categories, 'default to empty categories array');
t.end();
});
});
test('single category', function(t) {
queryParams.categories = 'food';
sanitize(queryParams, function( err, clean ){
t.deepEqual(clean.categories, ['food'], 'category set');
t.end();
});
});
test('multiple categories', function(t) {
queryParams.categories = 'food,education,nightlife';
sanitize(queryParams, function( err, clean ){
t.deepEqual(clean.categories, ['food', 'education', 'nightlife'], 'categories set');
t.end();
});
});
test('whitespace and empty strings', function(t) {
queryParams.categories = 'food, , nightlife ,';
sanitize(queryParams, function( err, clean ){
t.deepEqual(clean.categories, ['food', 'nightlife'], 'categories set');
t.end();
});
});
test('all empty strings', function(t) {
queryParams.categories = ', , ,';
sanitize(queryParams, function( err, clean ){
t.deepEqual(clean.categories, defaultClean.categories, 'empty strings filtered out');
t.end();
});
});
};
module.exports.tests.middleware_failure = function(test, common) { module.exports.tests.middleware_failure = function(test, common) {
test('middleware failure', function(t) { test('middleware failure', function(t) {
var res = { status: function( code ){ var res = { status: function( code ){

Loading…
Cancel
Save