Browse Source

added middleware component to trim by result granularity

purpose in comments, cleans up from overly-verbose FallbackQuery
pull/666/head
Stephen Hess 9 years ago
parent
commit
7495cad465
  1. 55
      middleware/trimByGranularity.js
  2. 184
      test/unit/middleware/trimByGranularity.js

55
middleware/trimByGranularity.js

@ -1,34 +1,61 @@
var _ = require('lodash'); var _ = require('lodash');
// This middleware component trims the results array by granularity when
// FallbackQuery was used. FallbackQuery is used for inputs like
// `1090 N Charlotte St, Lancaster, PA` where the address may not exist and
// we must fall back to trying `Lancaster, PA`. If the address does exist then
// FallbackQuery will return results for:
// - address+city+state
// - city+state
// - state
//
// Because the address matched, we're not interested in city+state or state, so
// this component removes that aren't the most granular.
// layers in increasing order of granularity // layers in increasing order of granularity
var layers = [ var layers = [
['venue'], 'venue',
['address'], 'address',
['neighbourhood'], 'neighbourhood',
['locality', 'localadmin'], 'borough',
['county', 'macrocounty'], 'locality',
['region', 'macroregion'], 'county',
['country'] 'region',
'country'
]; ];
function hasRecordsAtLayers(results, layers) { // this helper method returns `true` if every result has a matched_query
return _.some(results, function(result) { // starting with `fallback.`
return layers.indexOf(result.layer) !== -1; function isFallbackQuery(results) {
return results.every(function(result) {
return result.hasOwnProperty('_matched_queries') &&
!_.isEmpty(result._matched_queries) &&
_.startsWith(result._matched_queries[0], 'fallback.');
});
}
function hasRecordsAtLayers(results, layer) {
return results.some(function(result) {
return result._matched_queries[0] === 'fallback.' + layer;
}); });
} }
function retainRecordsAtLayers(results, layers) { function retainRecordsAtLayers(results, layer) {
return _.filter(results, function(result) { return results.filter(function(result) {
return layers.indexOf(result.layer) !== -1; return result._matched_queries[0] === 'fallback.' + layer;
}); });
} }
function setup() { function setup() {
return function trim(req, res, next) { return function trim(req, res, next) {
if (_.isUndefined(req.clean)) { // don't do anything if there are no results or there are non-fallback.* named queries
// there should never be a mixture of fallback.* and non-fallback.* named queries
if (_.isUndefined(res.data) || !isFallbackQuery(res.data)) {
return next(); return next();
} }
// start at the most granular possible layer. if there are results at a layer
// then remove everything not at that layer.
layers.forEach(function(layer) { layers.forEach(function(layer) {
if (hasRecordsAtLayers(res.data, layer )) { if (hasRecordsAtLayers(res.data, layer )) {
res.data = retainRecordsAtLayers(res.data, layer); res.data = retainRecordsAtLayers(res.data, layer);

184
test/unit/middleware/trimByGranularity.js

@ -12,28 +12,26 @@ module.exports.tests.trimByGranularity = function(test, common) {
t.end(); t.end();
}); });
test('when venue records are most granular, only they should be retained', function(t) { test('all records with fallback.* matched_queries name should retain only venues when they are most granular', function(t) {
var req = { clean: {} }; var req = { clean: {} };
var res = { var res = {
data: [ data: [
{ name: 'venue 1', layer: 'venue' }, { name: 'venue 1', _matched_queries: ['fallback.venue'] },
{ name: 'venue 2', layer: 'venue' }, { name: 'venue 2', _matched_queries: ['fallback.venue'] },
{ name: 'address 1', layer: 'address' }, { name: 'address 1', _matched_queries: ['fallback.address'] },
{ name: 'neighbourhood 1', layer: 'neighbourhood' }, { name: 'neighbourhood 1', _matched_queries: ['fallback.neighbourhood'] },
{ name: 'locality 1', layer: 'locality' }, { name: 'locality 1', _matched_queries: ['fallback.locality'] },
{ name: 'localadmin 1', layer: 'localadmin' }, { name: 'county 1', _matched_queries: ['fallback.county'] },
{ name: 'county 1', layer: 'county' }, { name: 'region 1', _matched_queries: ['fallback.region'] },
{ name: 'macrocounty 1', layer: 'macrocounty' }, { name: 'country 1', _matched_queries: ['fallback.country'] },
{ name: 'region 1', layer: 'region' }, { name: 'unknown', _matched_queries: ['fallback.unknown'] }
{ name: 'macroregion 1', layer: 'macroregion' },
{ name: 'country 1', layer: 'country' }
] ]
}; };
var expected_data = [ var expected_data = [
{ name: 'venue 1', layer: 'venue' }, { name: 'venue 1', _matched_queries: ['fallback.venue'] },
{ name: 'venue 2', layer: 'venue' }, { name: 'venue 2', _matched_queries: ['fallback.venue'] },
]; ];
function testIt() { function testIt() {
@ -44,30 +42,27 @@ module.exports.tests.trimByGranularity = function(test, common) {
} }
testIt(); testIt();
}); });
test('when address records are most granular, only they should be retained', function(t) { test('all records with fallback.* matched_queries name should retain only addresses when they are most granular', function(t) {
var req = { clean: {} }; var req = { clean: {} };
var res = { var res = {
data: [ data: [
{ name: 'address 1', layer: 'address' }, { name: 'address 1', _matched_queries: ['fallback.address'] },
{ name: 'address 2', layer: 'address' }, { name: 'address 2', _matched_queries: ['fallback.address'] },
{ name: 'neighbourhood 1', layer: 'neighbourhood' }, { name: 'neighbourhood 1', _matched_queries: ['fallback.neighbourhood'] },
{ name: 'locality 1', layer: 'locality' }, { name: 'locality 1', _matched_queries: ['fallback.locality'] },
{ name: 'localadmin 1', layer: 'localadmin' }, { name: 'county 1', _matched_queries: ['fallback.county'] },
{ name: 'county 1', layer: 'county' }, { name: 'region 1', _matched_queries: ['fallback.region'] },
{ name: 'macrocounty 1', layer: 'macrocounty' }, { name: 'country 1', _matched_queries: ['fallback.country'] },
{ name: 'region 1', layer: 'region' }, { name: 'unknown', _matched_queries: ['fallback.unknown'] }
{ name: 'macroregion 1', layer: 'macroregion' },
{ name: 'country 1', layer: 'country' },
] ]
}; };
var expected_data = [ var expected_data = [
{ name: 'address 1', layer: 'address' }, { name: 'address 1', _matched_queries: ['fallback.address'] },
{ name: 'address 2', layer: 'address' } { name: 'address 2', _matched_queries: ['fallback.address'] },
]; ];
function testIt() { function testIt() {
@ -78,31 +73,26 @@ module.exports.tests.trimByGranularity = function(test, common) {
} }
testIt(); testIt();
}); });
test('when neighbourhood records are most granular, only they should be retained', function(t) { test('all records with fallback.* matched_queries name should retain only neighbourhoods when they are most granular', function(t) {
var req = { clean: {} }; var req = { clean: {} };
var res = { var res = {
data: [ data: [
{ name: 'neighbourhood 1', layer: 'neighbourhood' }, { name: 'neighbourhood 1', _matched_queries: ['fallback.neighbourhood'] },
{ name: 'neighbourhood 2', layer: 'neighbourhood' }, { name: 'neighbourhood 2', _matched_queries: ['fallback.neighbourhood'] },
{ name: 'locality 1', layer: 'locality' }, { name: 'locality 1', _matched_queries: ['fallback.locality'] },
{ name: 'locality 2', layer: 'locality' }, { name: 'county 1', _matched_queries: ['fallback.county'] },
{ name: 'localadmin 1', layer: 'localadmin' }, { name: 'region 1', _matched_queries: ['fallback.region'] },
{ name: 'localadmin 2', layer: 'localadmin' }, { name: 'country 1', _matched_queries: ['fallback.country'] },
{ name: 'county 1', layer: 'county' }, { name: 'unknown', _matched_queries: ['fallback.unknown'] }
{ name: 'macrocounty 1', layer: 'macrocounty' },
{ name: 'region 1', layer: 'region' },
{ name: 'macroregion 1', layer: 'macroregion' },
{ name: 'country 1', layer: 'country' }
] ]
}; };
var expected_data = [ var expected_data = [
{ name: 'neighbourhood 1', layer: 'neighbourhood' }, { name: 'neighbourhood 1', _matched_queries: ['fallback.neighbourhood'] },
{ name: 'neighbourhood 2', layer: 'neighbourhood' } { name: 'neighbourhood 2', _matched_queries: ['fallback.neighbourhood'] },
]; ];
function testIt() { function testIt() {
@ -113,121 +103,106 @@ module.exports.tests.trimByGranularity = function(test, common) {
} }
testIt(); testIt();
}); });
test('when locality/localadmin records are most granular, only they should be retained', function(t) { test('all records with fallback.* matched_queries name should retain only localities when they are most granular', function(t) {
var req = { clean: {} }; var req = { clean: {} };
var res = { var res = {
data: [ data: [
{ name: 'locality 1', layer: 'locality' }, { name: 'locality 1', _matched_queries: ['fallback.locality'] },
{ name: 'locality 2', layer: 'locality' }, { name: 'locality 2', _matched_queries: ['fallback.locality'] },
{ name: 'localadmin 1', layer: 'localadmin' }, { name: 'county 1', _matched_queries: ['fallback.county'] },
{ name: 'localadmin 2', layer: 'localadmin' }, { name: 'region 1', _matched_queries: ['fallback.region'] },
{ name: 'county 1', layer: 'county' }, { name: 'country 1', _matched_queries: ['fallback.country'] },
{ name: 'macrocounty 1', layer: 'macrocounty' }, { name: 'unknown', _matched_queries: ['fallback.unknown'] }
{ name: 'region 1', layer: 'region' },
{ name: 'macroregion 1', layer: 'macroregion' },
{ name: 'country 1', layer: 'country' },
] ]
}; };
var expected_data = [ var expected_data = [
{ name: 'locality 1', layer: 'locality' }, { name: 'locality 1', _matched_queries: ['fallback.locality'] },
{ name: 'locality 2', layer: 'locality' }, { name: 'locality 2', _matched_queries: ['fallback.locality'] },
{ name: 'localadmin 1', layer: 'localadmin' },
{ name: 'localadmin 2', layer: 'localadmin' }
]; ];
function testIt() { function testIt() {
trimByGranularity(req, res, function() { trimByGranularity(req, res, function() {
t.deepEquals(res.data, expected_data, 'only locality/localadmin records should be here'); t.deepEquals(res.data, expected_data, 'only locality records should be here');
t.end(); t.end();
}); });
} }
testIt(); testIt();
}); });
test('when county/macrocounty records are most granular, only they should be retained', function(t) { test('all records with fallback.* matched_queries name should retain only venues when they are most granular', function(t) {
var req = { clean: {} }; var req = { clean: {} };
var res = { var res = {
data: [ data: [
{ name: 'county 1', layer: 'county' }, { name: 'county 1', _matched_queries: ['fallback.county'] },
{ name: 'county 2', layer: 'county' }, { name: 'county 2', _matched_queries: ['fallback.county'] },
{ name: 'macrocounty 1', layer: 'macrocounty' }, { name: 'region 1', _matched_queries: ['fallback.region'] },
{ name: 'macrocounty 2', layer: 'macrocounty' }, { name: 'country 1', _matched_queries: ['fallback.country'] },
{ name: 'region 1', layer: 'region' }, { name: 'unknown', _matched_queries: ['fallback.unknown'] }
{ name: 'macroregion 1', layer: 'macroregion' },
{ name: 'country 1', layer: 'country' },
] ]
}; };
var expected_data = [ var expected_data = [
{ name: 'county 1', layer: 'county' }, { name: 'county 1', _matched_queries: ['fallback.county'] },
{ name: 'county 2', layer: 'county' }, { name: 'county 2', _matched_queries: ['fallback.county'] },
{ name: 'macrocounty 1', layer: 'macrocounty' },
{ name: 'macrocounty 2', layer: 'macrocounty' },
]; ];
function testIt() { function testIt() {
trimByGranularity(req, res, function() { trimByGranularity(req, res, function() {
t.deepEquals(res.data, expected_data, 'only county/macrocounty records should be here'); t.deepEquals(res.data, expected_data, 'only county records should be here');
t.end(); t.end();
}); });
} }
testIt(); testIt();
}); });
test('when region/macroregion records are most granular, only they should be retained', function(t) { test('all records with fallback.* matched_queries name should retain only venues when they are most granular', function(t) {
var req = { clean: {} }; var req = { clean: {} };
var res = { var res = {
data: [ data: [
{ name: 'region 1', layer: 'region' }, { name: 'region 1', _matched_queries: ['fallback.region'] },
{ name: 'region 2', layer: 'region' }, { name: 'region 2', _matched_queries: ['fallback.region'] },
{ name: 'macroregion 1', layer: 'macroregion' }, { name: 'country 1', _matched_queries: ['fallback.country'] },
{ name: 'macroregion 2', layer: 'macroregion' }, { name: 'unknown', _matched_queries: ['fallback.unknown'] }
{ name: 'country 1', layer: 'country' },
] ]
}; };
var expected_data = [ var expected_data = [
{ name: 'region 1', layer: 'region' }, { name: 'region 1', _matched_queries: ['fallback.region'] },
{ name: 'region 2', layer: 'region' }, { name: 'region 2', _matched_queries: ['fallback.region'] },
{ name: 'macroregion 1', layer: 'macroregion' },
{ name: 'macroregion 2', layer: 'macroregion' }
]; ];
function testIt() { function testIt() {
trimByGranularity(req, res, function() { trimByGranularity(req, res, function() {
t.deepEquals(res.data, expected_data, 'only region/macroregion records should be here'); t.deepEquals(res.data, expected_data, 'only region records should be here');
t.end(); t.end();
}); });
} }
testIt(); testIt();
}); });
test('when country records are most granular, only they should be retained', function(t) { test('all records with fallback.* matched_queries name should retain only countries when they are most granular', function(t) {
var req = { clean: {} }; var req = { clean: {} };
var res = { var res = {
data: [ data: [
{ name: 'country 1', layer: 'country' }, { name: 'country 1', _matched_queries: ['fallback.country'] },
{ name: 'country 2', layer: 'country' } { name: 'country 2', _matched_queries: ['fallback.country'] },
{ name: 'unknown', _matched_queries: ['fallback.unknown'] }
] ]
}; };
var expected_data = [ var expected_data = [
{ name: 'country 1', layer: 'country' }, { name: 'country 1', _matched_queries: ['fallback.country'] },
{ name: 'country 2', layer: 'country' } { name: 'country 2', _matched_queries: ['fallback.country'] },
]; ];
function testIt() { function testIt() {
@ -237,6 +212,33 @@ module.exports.tests.trimByGranularity = function(test, common) {
}); });
} }
testIt();
});
test('presence of any non-fallback.* named queries should not trim', function(t) {
var req = { clean: {} };
var res = {
data: [
{ name: 'region', _matched_queries: ['fallback.region'] },
{ name: 'country', _matched_queries: ['fallback.country'] },
{ name: 'result with non-named query' }
]
};
var expected_data = [
{ name: 'region', _matched_queries: ['fallback.region'] },
{ name: 'country', _matched_queries: ['fallback.country'] },
{ name: 'result with non-named query' }
];
function testIt() {
trimByGranularity(req, res, function() {
t.deepEquals(res.data, expected_data, 'all should results should have been retained');
t.end();
});
}
testIt(); testIt();
}); });

Loading…
Cancel
Save