From b8bd6c1252afedd51d952c4b53b4057f350baaa1 Mon Sep 17 00:00:00 2001 From: Stephen Hess Date: Fri, 21 Oct 2016 15:58:23 -0400 Subject: [PATCH 1/7] enabled city/state only inputs for fallback queries --- query/search.js | 18 ++++++++++-- test/unit/query/search.js | 58 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/query/search.js b/query/search.js index bed5a63c..3e2a591a 100644 --- a/query/search.js +++ b/query/search.js @@ -1,7 +1,8 @@ var peliasQuery = require('pelias-query'), defaults = require('./search_defaults'), textParser = require('./text_parser'), - check = require('check-types'); + check = require('check-types'), + _ = require('lodash'); //------------------------------ // general-purpose search query @@ -120,7 +121,7 @@ function generateQuery( clean ){ } function getQuery(vs) { - if (hasStreet(vs)) { + if (hasStreet(vs) || isCityStateOnly(vs)) { return { type: 'fallback', body: fallbackQuery.render(vs) @@ -137,4 +138,17 @@ function hasStreet(vs) { return vs.isset('input:street'); } +function isCityStateOnly(vs) { + var isSet = function(layer) { + return vs.isset('input:' + layer); + }; + + var allowedFields = ['locality', 'region']; + var disallowedFields = ['query', 'category', 'housenumber', 'street', + 'neighbourhood', 'borough', 'postcode', 'county', 'country']; + + return allowedFields.every(isSet) && !disallowedFields.some(isSet); + +} + module.exports = generateQuery; diff --git a/test/unit/query/search.js b/test/unit/query/search.js index 36fbd17d..eae7e40a 100644 --- a/test/unit/query/search.js +++ b/test/unit/query/search.js @@ -270,6 +270,64 @@ 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('city- OR state-only should return undefined', function(t) { + ['city', 'state'].forEach(function(placeType) { + var clean = { + parsed_text: {} + }; + + clean.parsed_text[placeType] = placeType + ' value'; + + var query = generate(clean); + + t.equals(query, undefined, 'should have returned undefined'); + + }); + + t.end(); + + }); + + test('city and state with at least one other input: field should return undefined', function(t) { + ['query', 'category', 'number', 'neighbourhood', 'borough', 'postalcode', 'county', 'country'].forEach(function(placeType) { + var clean = { + parsed_text: { + 'city': 'city value', + 'state': 'state value' + } + }; + + clean.parsed_text[placeType] = placeType + ' value'; + + var query = generate(clean); + + t.equals(query, undefined, 'should have returned undefined'); + + }); + + t.end(); + + }); + }; module.exports.all = function (tape, common) { From 54b70c7d3c5286d89d623e0f11dc06dafebaf99c Mon Sep 17 00:00:00 2001 From: Stephen Hess Date: Fri, 21 Oct 2016 16:18:12 -0400 Subject: [PATCH 2/7] removed lodash requirement --- query/search.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/query/search.js b/query/search.js index 3e2a591a..c62b04d7 100644 --- a/query/search.js +++ b/query/search.js @@ -1,8 +1,7 @@ var peliasQuery = require('pelias-query'), defaults = require('./search_defaults'), textParser = require('./text_parser'), - check = require('check-types'), - _ = require('lodash'); + check = require('check-types'); //------------------------------ // general-purpose search query From c3451bbec0a8931d9d687c05cbd3a14c946444d2 Mon Sep 17 00:00:00 2001 From: Stephen Hess Date: Tue, 25 Oct 2016 12:13:09 -0400 Subject: [PATCH 3/7] added city input/results support just uses the original scoring, which is open to debate --- middleware/confidenceScoreFallback.js | 11 +-- .../middleware/confidenceScoreFallback.js | 71 +++++++++++++++++++ 2 files changed, 78 insertions(+), 4 deletions(-) diff --git a/middleware/confidenceScoreFallback.js b/middleware/confidenceScoreFallback.js index 8593ed60..d10d3ade 100644 --- a/middleware/confidenceScoreFallback.js +++ b/middleware/confidenceScoreFallback.js @@ -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'); } 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/test/unit/middleware/confidenceScoreFallback.js b/test/unit/middleware/confidenceScoreFallback.js index 5fcd03de..1adf77da 100644 --- a/test/unit/middleware/confidenceScoreFallback.js +++ b/test/unit/middleware/confidenceScoreFallback.js @@ -237,6 +237,77 @@ 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 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) { From acb669b7b3871dfa85784a86f9c4f7f62316fdc7 Mon Sep 17 00:00:00 2001 From: Stephen Hess Date: Tue, 25 Oct 2016 12:36:48 -0400 Subject: [PATCH 4/7] added support for optional country --- query/search.js | 2 +- test/unit/query/search.js | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/query/search.js b/query/search.js index c62b04d7..4f9b1c73 100644 --- a/query/search.js +++ b/query/search.js @@ -144,7 +144,7 @@ function isCityStateOnly(vs) { var allowedFields = ['locality', 'region']; var disallowedFields = ['query', 'category', 'housenumber', 'street', - 'neighbourhood', 'borough', 'postcode', 'county', 'country']; + 'neighbourhood', 'borough', 'postcode', 'county']; return allowedFields.every(isSet) && !disallowedFields.some(isSet); diff --git a/test/unit/query/search.js b/test/unit/query/search.js index eae7e40a..1aae8df6 100644 --- a/test/unit/query/search.js +++ b/test/unit/query/search.js @@ -289,6 +289,22 @@ module.exports.tests.city_state = function(test, common) { }); + 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- OR state-only should return undefined', function(t) { ['city', 'state'].forEach(function(placeType) { var clean = { @@ -308,7 +324,7 @@ module.exports.tests.city_state = function(test, common) { }); test('city and state with at least one other input: field should return undefined', function(t) { - ['query', 'category', 'number', 'neighbourhood', 'borough', 'postalcode', 'county', 'country'].forEach(function(placeType) { + ['query', 'category', 'number', 'neighbourhood', 'borough', 'postalcode', 'county'].forEach(function(placeType) { var clean = { parsed_text: { 'city': 'city value', From 5dedcf503252678f413449bd46259e58fc515483 Mon Sep 17 00:00:00 2001 From: Stephen Hess Date: Tue, 25 Oct 2016 12:37:52 -0400 Subject: [PATCH 5/7] changed method name --- query/search.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/query/search.js b/query/search.js index 4f9b1c73..945276e9 100644 --- a/query/search.js +++ b/query/search.js @@ -120,7 +120,7 @@ function generateQuery( clean ){ } function getQuery(vs) { - if (hasStreet(vs) || isCityStateOnly(vs)) { + if (hasStreet(vs) || isCityStateOnlyWithOptionalCountry(vs)) { return { type: 'fallback', body: fallbackQuery.render(vs) @@ -137,7 +137,7 @@ function hasStreet(vs) { return vs.isset('input:street'); } -function isCityStateOnly(vs) { +function isCityStateOnlyWithOptionalCountry(vs) { var isSet = function(layer) { return vs.isset('input:' + layer); }; From 916e7d673617bc299b125ec875bb72dccdfe1553 Mon Sep 17 00:00:00 2001 From: Stephen Hess Date: Wed, 26 Oct 2016 11:52:35 -0400 Subject: [PATCH 6/7] made `locality` and `localadmin` equivalent in fallback scoring --- middleware/confidenceScoreFallback.js | 4 +- .../middleware/confidenceScoreFallback.js | 57 +++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/middleware/confidenceScoreFallback.js b/middleware/confidenceScoreFallback.js index d10d3ade..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; @@ -99,7 +99,7 @@ function checkFallbackLevel(req, hit) { function checkFallbackOccurred(req, hit) { return (requestedAddress(req) && hit.layer !== 'address') || (requestedStreet(req) && hit.layer !== 'street') || - (requestedCity(req) && hit.layer !== 'locality'); + (requestedCity(req) && hit.layer !== 'locality' && hit.layer !== 'localadmin'); } function requestedAddress(req) { diff --git a/test/unit/middleware/confidenceScoreFallback.js b/test/unit/middleware/confidenceScoreFallback.js index 1adf77da..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: { @@ -261,6 +295,29 @@ module.exports.tests.confidenceScore = function(test, common) { 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: { From df6bd50645adb90a4308f1851f78886cf0989cb0 Mon Sep 17 00:00:00 2001 From: Stephen Hess Date: Wed, 26 Oct 2016 11:58:37 -0400 Subject: [PATCH 7/7] unrolled loop tests --- test/unit/query/search.js | 143 ++++++++++++++++++++++++++++++++------ 1 file changed, 122 insertions(+), 21 deletions(-) diff --git a/test/unit/query/search.js b/test/unit/query/search.js index 1aae8df6..f16a79ae 100644 --- a/test/unit/query/search.js +++ b/test/unit/query/search.js @@ -305,41 +305,142 @@ module.exports.tests.city_state = function(test, common) { }); - test('city- OR state-only should return undefined', function(t) { - ['city', 'state'].forEach(function(placeType) { - var clean = { - parsed_text: {} - }; + 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); - clean.parsed_text[placeType] = placeType + ' value'; + t.equals(query, undefined, 'should have returned undefined'); + t.end(); - var query = generate(clean); + }); - t.equals(query, undefined, 'should have returned undefined'); + 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 and state with at least one other input: field should return undefined', function(t) { - ['query', 'category', 'number', 'neighbourhood', 'borough', 'postalcode', 'county'].forEach(function(placeType) { - var clean = { - parsed_text: { - 'city': 'city value', - 'state': 'state value' - } - }; + test('city/state with number should return undefined', function(t) { + var clean = { + parsed_text: { + city: 'city value', + state: 'state value', + number: 'number value' + } + }; - clean.parsed_text[placeType] = placeType + ' value'; + var query = generate(clean); - var query = generate(clean); + t.equals(query, undefined, 'should have returned undefined'); + t.end(); - t.equals(query, undefined, 'should have returned undefined'); + }); - }); + 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(); });