diff --git a/middleware/confidenceScoreFallback.js b/middleware/confidenceScoreFallback.js index 8593ed60..d208d02f 100644 --- a/middleware/confidenceScoreFallback.js +++ b/middleware/confidenceScoreFallback.js @@ -72,13 +72,13 @@ function checkFallbackLevel(req, hit) { return 0.8; case 'street': return 0.8; + case 'localadmin': case 'locality': case 'borough': case 'neighbourhood': return 0.6; case 'macrocounty': case 'county': - case 'localadmin': return 0.4; case 'region': return 0.3; @@ -89,6 +89,7 @@ function checkFallbackLevel(req, hit) { default: return 0.1; } + } hit.match_type = 'exact'; @@ -96,11 +97,9 @@ function checkFallbackLevel(req, hit) { } function checkFallbackOccurred(req, hit) { - // at this time we only do this for address queries, so keep this simple - // TODO: add other layer checks once we start handling disambiguation - return (requestedAddress(req) && hit.layer !== 'address') || - (requestedStreet(req) && hit.layer !== 'street'); + (requestedStreet(req) && hit.layer !== 'street') || + (requestedCity(req) && hit.layer !== 'locality' && hit.layer !== 'localadmin'); } function requestedAddress(req) { @@ -115,4 +114,8 @@ function requestedStreet(req) { req.clean.parsed_text.hasOwnProperty('street'); } +function requestedCity(req) { + return req.clean.parsed_text.hasOwnProperty('city'); +} + module.exports = setup; diff --git a/package.json b/package.json index 3511e9aa..c7da591e 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "morgan": "1.7.0", "pelias-categories": "1.1.0", "pelias-config": "2.3.0", - "pelias-labels": "1.4.0", + "pelias-labels": "1.5.0", "pelias-logger": "0.1.0", "pelias-model": "4.3.0", "pelias-query": "8.8.0", diff --git a/query/search.js b/query/search.js index bed5a63c..945276e9 100644 --- a/query/search.js +++ b/query/search.js @@ -120,7 +120,7 @@ function generateQuery( clean ){ } function getQuery(vs) { - if (hasStreet(vs)) { + if (hasStreet(vs) || isCityStateOnlyWithOptionalCountry(vs)) { return { type: 'fallback', body: fallbackQuery.render(vs) @@ -137,4 +137,17 @@ function hasStreet(vs) { return vs.isset('input:street'); } +function isCityStateOnlyWithOptionalCountry(vs) { + var isSet = function(layer) { + return vs.isset('input:' + layer); + }; + + var allowedFields = ['locality', 'region']; + var disallowedFields = ['query', 'category', 'housenumber', 'street', + 'neighbourhood', 'borough', 'postcode', 'county']; + + return allowedFields.every(isSet) && !disallowedFields.some(isSet); + +} + module.exports = generateQuery; diff --git a/test/unit/middleware/confidenceScoreFallback.js b/test/unit/middleware/confidenceScoreFallback.js index 5fcd03de..138bee8c 100644 --- a/test/unit/middleware/confidenceScoreFallback.js +++ b/test/unit/middleware/confidenceScoreFallback.js @@ -203,6 +203,40 @@ module.exports.tests.confidenceScore = function(test, common) { t.end(); }); + test('fallback to localadmin should have score deduction', function(t) { + var req = { + clean: { + text: '123 Main St, City, NM', + parsed_text: { + number: 123, + street: 'Main St', + state: 'NM' + } + } + }; + var res = { + data: [{ + _score: 10, + found: true, + value: 1, + layer: 'localadmin', + 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, 0.6, 'score was set'); + t.end(); + }); + test('fallback to country should have score deduction', function(t) { var req = { clean: { @@ -237,6 +271,100 @@ module.exports.tests.confidenceScore = function(test, common) { t.equal(res.data[0].confidence, 0.1, 'score was set'); t.end(); }); + + test('city input granularity with locality result should set score to 1.0', function(t) { + var req = { + clean: { + parsed_text: { + city: 'city name', + state: 'state name' + } + } + }; + var res = { + data: [{ + layer: 'locality' + }], + meta: { + query_type: 'fallback' + } + }; + + confidenceScore(req, res, function() {}); + t.equal(res.data[0].confidence, 1.0, 'score was set'); + t.end(); + }); + + test('city input granularity with localadmin result should set score to 1.0', function(t) { + var req = { + clean: { + parsed_text: { + city: 'city name', + state: 'state name' + } + } + }; + var res = { + data: [{ + layer: 'localadmin' + }], + meta: { + query_type: 'fallback' + } + }; + + confidenceScore(req, res, function() {}); + t.equal(res.data[0].confidence, 1.0, 'score was set'); + t.end(); + }); + + test('city input granularity with region fallback should set score to 0.3', function(t) { + var req = { + clean: { + parsed_text: { + city: 'city name', + state: 'state name' + } + } + }; + var res = { + data: [{ + layer: 'region' + }], + meta: { + query_type: 'fallback' + } + }; + + confidenceScore(req, res, function() {}); + t.equal(res.data[0].confidence, 0.3, 'score was set'); + t.end(); + }); + + test('city input granularity with country fallback should set score to 0.1', function(t) { + var req = { + clean: { + parsed_text: { + city: 'city name', + state: 'state name', + country: 'country name' + } + } + }; + var res = { + data: [{ + layer: 'country' + }], + meta: { + query_type: 'fallback' + } + }; + + confidenceScore(req, res, function() {}); + t.equal(res.data[0].confidence, 0.1, 'score was set'); + t.end(); + }); + }; module.exports.all = function (tape, common) { diff --git a/test/unit/query/search.js b/test/unit/query/search.js index 36fbd17d..f16a79ae 100644 --- a/test/unit/query/search.js +++ b/test/unit/query/search.js @@ -270,6 +270,181 @@ module.exports.tests.query = function(test, common) { t.deepEqual(compiled.body, expected, 'valid search query with category filtering'); t.end(); }); + +}; + +module.exports.tests.city_state = function(test, common) { + test('only city and state set should return query', function(t) { + var clean = { + parsed_text: { + 'city': 'city value', + 'state': 'state value' + } + }; + + var query = generate(clean); + + t.notEqual(query, undefined, 'should not have returned undefined'); + t.end(); + + }); + + test('only city, state, and country set should return query', function(t) { + var clean = { + parsed_text: { + 'city': 'city value', + 'state': 'state value', + 'country': 'country value' + } + }; + + var query = generate(clean); + + t.notEqual(query, undefined, 'should not have returned undefined'); + t.end(); + + }); + + test('city-only should return undefined', function(t) { + var clean = { + parsed_text: { + city: 'city value' + } + }; + + var query = generate(clean); + + t.equals(query, undefined, 'should have returned undefined'); + t.end(); + + }); + + test('state-only should return undefined', function(t) { + var clean = { + parsed_text: { + state: 'state value' + } + }; + + var query = generate(clean); + + t.equals(query, undefined, 'should have returned undefined'); + t.end(); + + }); + + test('city/state with query should return undefined', function(t) { + var clean = { + parsed_text: { + city: 'city value', + state: 'state value', + query: 'query value' + } + }; + + var query = generate(clean); + + t.equals(query, undefined, 'should have returned undefined'); + t.end(); + + }); + + test('city/state with category should return undefined', function(t) { + var clean = { + parsed_text: { + city: 'city value', + state: 'state value', + category: 'category value' + } + }; + + var query = generate(clean); + + t.equals(query, undefined, 'should have returned undefined'); + t.end(); + + }); + + test('city/state with number should return undefined', function(t) { + var clean = { + parsed_text: { + city: 'city value', + state: 'state value', + number: 'number value' + } + }; + + var query = generate(clean); + + t.equals(query, undefined, 'should have returned undefined'); + t.end(); + + }); + + test('city/state with neighbourhood should return undefined', function(t) { + var clean = { + parsed_text: { + city: 'city value', + state: 'state value', + neighbourhood: 'neighbourhood value' + } + }; + + var query = generate(clean); + + t.equals(query, undefined, 'should have returned undefined'); + t.end(); + + }); + + test('city/state with borough should return undefined', function(t) { + var clean = { + parsed_text: { + city: 'city value', + state: 'state value', + borough: 'borough value' + } + }; + + var query = generate(clean); + + t.equals(query, undefined, 'should have returned undefined'); + t.end(); + + }); + + test('city/state with postalcode should return undefined', function(t) { + var clean = { + parsed_text: { + city: 'city value', + state: 'state value', + postalcode: 'postalcode value' + } + }; + + var query = generate(clean); + + t.equals(query, undefined, 'should have returned undefined'); + t.end(); + + }); + + test('city/state with county should return undefined', function(t) { + var clean = { + parsed_text: { + city: 'city value', + state: 'state value', + county: 'county value' + } + }; + + var query = generate(clean); + + t.equals(query, undefined, 'should have returned undefined'); + t.end(); + + }); + }; module.exports.all = function (tape, common) {