Browse Source

fix: match_type and confidence score were not correct for certain queries

pull/780/head
Diana Shkolnikov 8 years ago
parent
commit
30590a41d3
  1. 127
      middleware/confidenceScoreFallback.js
  2. 80
      test/unit/middleware/confidenceScoreFallback.js

127
middleware/confidenceScoreFallback.js

@ -1,3 +1,5 @@
'use strict';
/**
*
* Basic confidence score should be computed and returned for each item in the results.
@ -11,6 +13,7 @@
var check = require('check-types');
var logger = require('pelias-logger').get('api');
const _ = require('lodash');
function setup() {
return computeScores;
@ -67,9 +70,10 @@ function checkFallbackLevel(req, hit) {
// if we know a fallback occurred, deduct points based on layer granularity
switch (hit.layer) {
case 'venue':
case 'address':
logger.warn('Fallback scenarios should not result in address or venue records!', req.clean.parsed_text);
return 0.8;
case 'address':
return 0.8;
case 'street':
return 0.8;
case 'localadmin':
@ -96,26 +100,121 @@ function checkFallbackLevel(req, hit) {
return 1.0;
}
/**
* In parsed_text we might find any of the following properties:
* query
* number
* street
* neighbourhood
* borough
* city
* county
* state
* postalcode
* country
*
* They do not map 1:1 to our layers so the following somewhat complicated
* mapping structure is needed to set clear rules for comparing what was requested
* by the query and what has been received as a result to determine if a fallback occurred.
*/
const fallbackRules = [
{
name: 'venue',
notSet: [],
set: ['query'],
expectedLayers: ['venue']
},
{
name: 'address',
notSet: ['query'],
set: ['number', 'street'],
expectedLayers: ['address']
},
{
name: 'street',
notSet: ['query', 'number'],
set: ['street'],
expectedLayers: ['street']
},
{
name: 'neighbourhood',
notSet: ['query', 'number', 'street'],
set: ['neighbourhood'],
expectedLayers: ['neighbourhood']
},
{
name: 'borough',
notSet: ['query', 'number', 'street', 'neighbourhood'],
set: ['borough'],
expectedLayers: ['borough']
},
{
name: 'city',
notSet: ['query', 'number', 'street', 'neighbourhood', 'borough'],
set: ['city'],
expectedLayers: ['borough', 'locality', 'localadmin']
},
{
name: 'county',
notSet: ['query', 'number', 'street', 'neighbourhood', 'borough', 'city'],
set: ['county'],
expectedLayers: ['county']
},
{
name: 'state',
notSet: ['query', 'number', 'street', 'neighbourhood', 'borough', 'city', 'county'],
set: ['state'],
expectedLayers: ['region']
},
{
name: 'country',
notSet: ['query', 'number', 'street', 'neighbourhood', 'borough', 'city', 'county', 'state'],
set: ['country'],
expectedLayers: ['country']
}
];
function checkFallbackOccurred(req, hit) {
return (requestedAddress(req) && hit.layer !== 'address') ||
(requestedStreet(req) && hit.layer !== 'street') ||
(requestedCity(req) && hit.layer !== 'locality' && hit.layer !== 'localadmin');
// short-circuit after finding the first fallback scenario
const res = _.find(fallbackRules, (rule) => {
return (
// verify that more granular properties are not set
notSet(req.clean.parsed_text, rule.notSet) &&
// verify that expected property is set
areSet(req.clean.parsed_text, rule.set) &&
// verify that expected layer(s) was not returned
rule.expectedLayers.indexOf(hit.layer) === -1
);
});
return !!res;
}
function notSet(parsed_text, notSet) {
if (notSet.length === 0) {
return true;
}
function requestedAddress(req) {
// house number and street name were specified
return req.clean.parsed_text.hasOwnProperty('number') &&
req.clean.parsed_text.hasOwnProperty('street');
return (
_.every(notSet, (prop) => {
return !_.get(parsed_text, prop, false);
})
);
}
function requestedStreet(req) {
// only street name was specified
return !req.clean.parsed_text.hasOwnProperty('number') &&
req.clean.parsed_text.hasOwnProperty('street');
function areSet(parsed_text, areSet) {
if (areSet.length === 0) {
logger.warn('Expected properties in fallbackRules should never be empty');
return true;
}
function requestedCity(req) {
return req.clean.parsed_text.hasOwnProperty('city');
return (
_.every(areSet, (prop) => {
return _.get(parsed_text, prop, false);
})
);
}
module.exports = setup;

80
test/unit/middleware/confidenceScoreFallback.js

@ -95,6 +95,7 @@ module.exports.tests.confidenceScore = function(test, common) {
confidenceScore(req, res, function() {});
t.equal(res.data[0].confidence, 0.1, 'score was set');
t.equal(res.data[0].match_type, 'unknown', 'exact match indicated');
t.end();
});
@ -105,6 +106,7 @@ module.exports.tests.confidenceScore = function(test, common) {
parsed_text: {
number: 123,
street: 'Main St',
city: 'City',
state: 'NM'
}
}
@ -131,6 +133,7 @@ module.exports.tests.confidenceScore = function(test, common) {
confidenceScore(req, res, function() {});
t.equal(res.data[0].confidence, 1.0, 'max score was set');
t.equal(res.data[0].match_type, 'exact', 'exact match indicated');
t.end();
});
@ -166,10 +169,78 @@ module.exports.tests.confidenceScore = function(test, common) {
confidenceScore(req, res, function() {});
t.equal(res.data[0].confidence, 1.0, 'max score was set');
t.equal(res.data[0].match_type, 'exact', 'exact match indicated');
t.end();
});
test('fallback to locality should have score deduction', function(t) {
test('no fallback state query should have max score', function(t) {
var req = {
clean: {
text: 'Region Name, Country',
parsed_text: {
state: 'Region Name',
country: 'Country'
}
}
};
var res = {
data: [{
_score: 10,
found: true,
value: 1,
layer: 'region',
center_point: { lat: 100.1, lon: -50.5 },
name: { default: 'Region Name' },
parent: {
country: ['country1']
}
}],
meta: {
scores: [10],
query_type: 'fallback'
}
};
confidenceScore(req, res, function() {});
t.equal(res.data[0].confidence, 1.0, 'max score was set');
t.equal(res.data[0].match_type, 'exact', 'exact match indicated');
t.end();
});
test('no fallback country query should have max score', function(t) {
var req = {
clean: {
text: 'Country Name',
parsed_text: {
country: 'Country Name'
}
}
};
var res = {
data: [{
_score: 10,
found: true,
value: 1,
layer: 'country',
center_point: { lat: 100.1, lon: -50.5 },
name: { default: 'test name1' },
parent: {
country: ['country1']
}
}],
meta: {
scores: [10],
query_type: 'fallback'
}
};
confidenceScore(req, res, function() {});
t.equal(res.data[0].confidence, 1.0, 'max score was set');
t.equal(res.data[0].match_type, 'exact', 'exact match indicated');
t.end();
});
test('fallback to locality when searching for address should have score deduction', function(t) {
var req = {
clean: {
text: '123 Main St, City, NM',
@ -200,6 +271,7 @@ module.exports.tests.confidenceScore = function(test, common) {
confidenceScore(req, res, function() {});
t.equal(res.data[0].confidence, 0.6, 'score was set');
t.equal(res.data[0].match_type, 'fallback', 'fallback match indicated');
t.end();
});
@ -234,6 +306,7 @@ module.exports.tests.confidenceScore = function(test, common) {
confidenceScore(req, res, function() {});
t.equal(res.data[0].confidence, 0.6, 'score was set');
t.equal(res.data[0].match_type, 'fallback', 'fallback match indicated');
t.end();
});
@ -269,6 +342,7 @@ module.exports.tests.confidenceScore = function(test, common) {
confidenceScore(req, res, function() {});
t.equal(res.data[0].confidence, 0.1, 'score was set');
t.equal(res.data[0].match_type, 'fallback', 'fallback match indicated');
t.end();
});
@ -292,6 +366,7 @@ module.exports.tests.confidenceScore = function(test, common) {
confidenceScore(req, res, function() {});
t.equal(res.data[0].confidence, 1.0, 'score was set');
t.equal(res.data[0].match_type, 'exact', 'exact match indicated');
t.end();
});
@ -315,6 +390,7 @@ module.exports.tests.confidenceScore = function(test, common) {
confidenceScore(req, res, function() {});
t.equal(res.data[0].confidence, 1.0, 'score was set');
t.equal(res.data[0].match_type, 'exact', 'exact match indicated');
t.end();
});
@ -338,6 +414,7 @@ module.exports.tests.confidenceScore = function(test, common) {
confidenceScore(req, res, function() {});
t.equal(res.data[0].confidence, 0.3, 'score was set');
t.equal(res.data[0].match_type, 'fallback', 'fallback match indicated');
t.end();
});
@ -362,6 +439,7 @@ module.exports.tests.confidenceScore = function(test, common) {
confidenceScore(req, res, function() {});
t.equal(res.data[0].confidence, 0.1, 'score was set');
t.equal(res.data[0].match_type, 'fallback', 'fallback match indicated');
t.end();
});

Loading…
Cancel
Save