diff --git a/README.md b/README.md index 73e48d0d..4253d15e 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ The API recognizes the following properties under the top-level `api` key in you |`indexName`|*no*|*pelias*|name of the Elasticsearch index to be used when building queries| |`relativeScores`|*no*|true|if set to true, confidence scores will be normalized, realistically at this point setting this to false is not tested or desirable |`accessLog`|*no*||name of the format to use for access logs; may be any one of the [predefined values](https://github.com/expressjs/morgan#predefined-formats) in the `morgan` package. Defaults to `"common"`; if set to `false`, or an otherwise falsy value, disables access-logging entirely.| -|`services`|*no*||service definitions for [point-in-polygon](https://github.com/pelias/pip-service) and [placholder](https://github.com/pelias/placeholder) services. If missing (which is not recommended), the point-in-polygon and placeholder services will not be called.| +|`services`|*no*||service definitions for [point-in-polygon](https://github.com/pelias/pip-service), and [placeholder](https://github.com/pelias/placeholder), and [interpolation](https://github.com/pelias/interpolation) services. If missing (which is not recommended), the services will not be called.| |`defaultParameters.focus.point.lon`
`defaultParameters.focus.point.lat`|no | |default coordinates for focus point Example configuration file would look something like this: @@ -73,6 +73,10 @@ Example configuration file would look something like this: }, "placeholder": { "url": "http://myplaceholderservice.com:5000" + }, + "interpolation": { + "url": "http://myinterpolationservice.com:3000", + "timeout": 2500 } } "defaultParameters": { @@ -80,12 +84,6 @@ Example configuration file would look something like this: "focus.point.lon": 21.212121 } }, - "interpolation": { - "client": { - "adapter": "http", - "host": "internal-pelias-interpolation-dev-130430937.us-east-1.elb.amazonaws.com" - } - }, "logger": { "level": "debug" } diff --git a/controller/libpostal.js b/controller/libpostal.js index 8f275dd9..23c82f85 100644 --- a/controller/libpostal.js +++ b/controller/libpostal.js @@ -20,6 +20,7 @@ function setup(should_execute) { parsed_text.country = iso3166.to3(_.toUpper(parsed_text.country)); } + req.clean.parser = 'libpostal'; req.clean.parsed_text = parsed_text; debugLog.push(req, {parsed_text: req.clean.parsed_text}); } diff --git a/controller/place.js b/controller/place.js index 2b5490aa..51ce58d9 100644 --- a/controller/place.js +++ b/controller/place.js @@ -38,7 +38,7 @@ function setup( apiConfig, esclient ){ const cmd = req.clean.ids.map( function(id) { return { _index: apiConfig.indexName, - _type: id.layers, + _type: id.layer, _id: id.id }; }); diff --git a/index.js b/index.js index 2bd4dcf5..f541c0cc 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,10 @@ -var app = require('./app'), - port = ( process.env.PORT || 3100 ); +const app = require('./app'), + port = ( process.env.PORT || 3100 ), + host = ( process.env.HOST || undefined ); -/** run server on the default setup (single core) **/ -console.log( 'pelias is now running on port ' + port ); -app.listen( port ); +const server = app.listen( port, host, () => { + // ask server for the actual address and port its listening on + const listenAddress = server.address(); + console.log( `pelias is now running on ${listenAddress.address}:${listenAddress.port}` ); +}); diff --git a/package.json b/package.json index fd567c54..147c87e4 100644 --- a/package.json +++ b/package.json @@ -49,21 +49,21 @@ "geolib": "^2.0.18", "iso-639-3": "^1.0.0", "iso3166-1": "^0.3.0", - "joi": "^11.0.1", + "joi": "^12.0.0", "locale": "^0.1.0", "lodash": "^4.17.4", "markdown": "0.5.0", "morgan": "^1.8.2", "pelias-categories": "1.2.0", - "pelias-config": "2.12.1", + "pelias-config": "2.13.0", "pelias-labels": "1.7.0", - "pelias-logger": "0.2.0", + "pelias-logger": "0.3.0", "pelias-microservice-wrapper": "1.2.1", - "pelias-model": "5.1.0", + "pelias-model": "5.2.0", "pelias-query": "9.1.0", "pelias-sorting": "1.0.1", - "pelias-text-analyzer": "1.9.2", - "predicates": "^1.0.1", + "pelias-text-analyzer": "1.10.2", + "predicates": "^2.0.0", "retry": "^0.10.1", "stats-lite": "^2.0.4", "through2": "^2.0.3" diff --git a/query/structured_geocoding.js b/query/structured_geocoding.js index a69f42bb..317caba1 100644 --- a/query/structured_geocoding.js +++ b/query/structured_geocoding.js @@ -1,12 +1,12 @@ -var peliasQuery = require('pelias-query'), - defaults = require('./search_defaults'), - textParser = require('./text_parser'), - check = require('check-types'); +const peliasQuery = require('pelias-query'), + defaults = require('./search_defaults'), + textParser = require('./text_parser'), + check = require('check-types'); //------------------------------ // general-purpose search query //------------------------------ -var structuredQuery = new peliasQuery.layout.StructuredFallbackQuery(); +const structuredQuery = new peliasQuery.layout.StructuredFallbackQuery(); // scoring boost structuredQuery.score( peliasQuery.view.focus_only_function( peliasQuery.view.phrase ) ); @@ -29,7 +29,7 @@ structuredQuery.filter( peliasQuery.view.categories ); **/ function generateQuery( clean ){ - var vs = new peliasQuery.Vars( defaults ); + const vs = new peliasQuery.Vars( defaults ); // input text vs.var( 'input:name', clean.text ); @@ -95,7 +95,7 @@ function generateQuery( clean ){ textParser( clean.parsed_text, vs ); } - var q = getQuery(vs); + const q = getQuery(vs); // console.log(JSON.stringify(q.body, null, 2)); diff --git a/sanitizer/_geonames_deprecation.js b/sanitizer/_geonames_deprecation.js index b3cc8185..2cdef1d1 100644 --- a/sanitizer/_geonames_deprecation.js +++ b/sanitizer/_geonames_deprecation.js @@ -1,21 +1,32 @@ const _ = require('lodash'); /** -with the release of coarse reverse as a separate thing and ES reverse only -handling venues, addresses, and streets, geonames make no sense in the reverse context + * Now that we have the pip-service, we have stopped supporting returning Geonames for coarse reverse. + * + * However, until the `/nearby` endpoint is finalized, we still want to support Geonames for + * _non-coarse_ reverse. **/ +const coarse_reverse_message ='coarse /reverse does not support geonames. See https://github.com/pelias/pelias/issues/675 for more info'; + function _sanitize( raw, clean, opts ) { // error & warning messages const messages = { errors: [], warnings: [] }; + // return taking no action unless this is a coarse-only reverse request + const non_coarse_layers = ['address', 'street', 'venue']; + const is_coarse_reverse = !_.isEmpty(clean.layers) && + _.isEmpty(_.intersection(clean.layers, non_coarse_layers)); + if (!is_coarse_reverse) { + return messages; + } + if (_.isEqual(clean.sources, ['geonames']) || _.isEqual(clean.sources, ['gn'])) { - messages.errors.push('/reverse does not support geonames'); + messages.errors.push(coarse_reverse_message); } else if (_.includes(clean.sources, 'geonames') || _.includes(clean.sources, 'gn')) { clean.sources = _.without(clean.sources, 'geonames', 'gn'); - messages.warnings.push('/reverse does not support geonames'); - + messages.warnings.push(coarse_reverse_message); } return messages; diff --git a/test/unit/controller/libpostal.js b/test/unit/controller/libpostal.js index e0d012a9..f117007d 100644 --- a/test/unit/controller/libpostal.js +++ b/test/unit/controller/libpostal.js @@ -149,6 +149,7 @@ module.exports.tests.parse_is_called = (test, common) => { controller(req, res, () => { t.deepEquals(req, { clean: { + parser: 'libpostal', parsed_text: 'replacement parsed_text' } }); @@ -184,6 +185,7 @@ module.exports.tests.iso2_conversion = (test, common) => { controller(req, res, () => { t.deepEquals(req, { clean: { + parser: 'libpostal', parsed_text: { locality: 'this is the locality' } @@ -223,6 +225,7 @@ module.exports.tests.iso2_conversion = (test, common) => { controller(req, res, () => { t.deepEquals(req, { clean: { + parser: 'libpostal', parsed_text: { country: 'unknown country code' } @@ -265,6 +268,7 @@ module.exports.tests.iso2_conversion = (test, common) => { controller(req, res, () => { t.deepEquals(req, { clean: { + parser: 'libpostal', parsed_text: { country: 'ISO3 COUNTRY CODE' } diff --git a/test/unit/controller/place.js b/test/unit/controller/place.js index 25d99463..b76d69c9 100644 --- a/test/unit/controller/place.js +++ b/test/unit/controller/place.js @@ -51,11 +51,11 @@ module.exports.tests.success = (test, common) => { ids: [ { id: 'id1', - layers: 'layer1' + layer: 'layer1' }, { id: 'id2', - layers: 'layer2' + layer: 'layer2' } ] }, diff --git a/test/unit/fixture/reverse_null_island.js b/test/unit/fixture/reverse_null_island.js deleted file mode 100644 index 874518d4..00000000 --- a/test/unit/fixture/reverse_null_island.js +++ /dev/null @@ -1,39 +0,0 @@ -var vs = require('../../../query/reverse_defaults'); - -module.exports = { - 'query': { - 'bool': { - 'filter': [{ - 'geo_distance': { - 'distance': '3km', - 'distance_type': 'plane', - 'optimize_bbox': 'indexed', - 'center_point': { - 'lat': 0, - 'lon': 0 - } - } - }, - { - 'terms': { - 'layer': ['venue', 'address', 'street'] - } - }] - } - }, - 'sort': [ - '_score', - { - '_geo_distance': { - 'center_point': { - 'lat': 0, - 'lon': 0 - }, - 'order': 'asc', - 'distance_type': 'plane' - } - } - ], - 'size': vs.size, - 'track_scores': true -}; diff --git a/test/unit/fixture/reverse_standard.js b/test/unit/fixture/reverse_standard.js deleted file mode 100644 index 4a477ce1..00000000 --- a/test/unit/fixture/reverse_standard.js +++ /dev/null @@ -1,39 +0,0 @@ -var vs = require('../../../query/reverse_defaults'); - -module.exports = { - 'query': { - 'bool': { - 'filter': [{ - 'geo_distance': { - 'distance': '3km', - 'distance_type': 'plane', - 'optimize_bbox': 'indexed', - 'center_point': { - 'lat': 29.49136, - 'lon': -82.50622 - } - } - }, - { - 'terms': { - 'layer': ['venue', 'address', 'street'] - } - }] - } - }, - 'sort': [ - '_score', - { - '_geo_distance': { - 'center_point': { - 'lat': 29.49136, - 'lon': -82.50622 - }, - 'order': 'asc', - 'distance_type': 'plane' - } - } - ], - 'size': vs.size, - 'track_scores': true -}; diff --git a/test/unit/fixture/reverse_with_boundary_country.js b/test/unit/fixture/reverse_with_boundary_country.js deleted file mode 100644 index 6677247a..00000000 --- a/test/unit/fixture/reverse_with_boundary_country.js +++ /dev/null @@ -1,49 +0,0 @@ -var vs = require('../../../query/reverse_defaults'); - -module.exports = { - 'query': { - 'bool': { - 'must': [ - { - 'match': { - 'parent.country_a': { - 'analyzer': 'standard', - 'query': 'ABC' - } - } - } - ], - 'filter': [{ - 'geo_distance': { - 'distance': '3km', - 'distance_type': 'plane', - 'optimize_bbox': 'indexed', - 'center_point': { - 'lat': 29.49136, - 'lon': -82.50622 - } - } - }, - { - 'terms': { - 'layer': ['venue', 'address', 'street'] - } - }] - } - }, - 'sort': [ - '_score', - { - '_geo_distance': { - 'center_point': { - 'lat': 29.49136, - 'lon': -82.50622 - }, - 'order': 'asc', - 'distance_type': 'plane' - } - } - ], - 'size': vs.size, - 'track_scores': true -}; diff --git a/test/unit/fixture/reverse_with_layer_filtering.js b/test/unit/fixture/reverse_with_layer_filtering.js deleted file mode 100644 index 5d6c53b5..00000000 --- a/test/unit/fixture/reverse_with_layer_filtering.js +++ /dev/null @@ -1,41 +0,0 @@ -var vs = require('../../../query/reverse_defaults'); - -module.exports = { - 'query': { - 'bool': { - 'filter': [ - { - 'geo_distance': { - 'distance': '3km', - 'distance_type': 'plane', - 'optimize_bbox': 'indexed', - 'center_point': { - 'lat': 29.49136, - 'lon': -82.50622 - } - } - }, - { - 'terms': { - 'layer': ['venue', 'address', 'street'] - } - } - ] - } - }, - 'sort': [ - '_score', - { - '_geo_distance': { - 'center_point': { - 'lat': 29.49136, - 'lon': -82.50622 - }, - 'order': 'asc', - 'distance_type': 'plane' - } - } - ], - 'size': vs.size, - 'track_scores': true -}; diff --git a/test/unit/fixture/reverse_with_layer_filtering_non_coarse_subset.js b/test/unit/fixture/reverse_with_layer_filtering_non_coarse_subset.js deleted file mode 100644 index aba18b08..00000000 --- a/test/unit/fixture/reverse_with_layer_filtering_non_coarse_subset.js +++ /dev/null @@ -1,41 +0,0 @@ -var vs = require('../../../query/reverse_defaults'); - -module.exports = { - 'query': { - 'bool': { - 'filter': [ - { - 'geo_distance': { - 'distance': '3km', - 'distance_type': 'plane', - 'optimize_bbox': 'indexed', - 'center_point': { - 'lat': 29.49136, - 'lon': -82.50622 - } - } - }, - { - 'terms': { - 'layer': ['venue', 'street'] - } - } - ] - } - }, - 'sort': [ - '_score', - { - '_geo_distance': { - 'center_point': { - 'lat': 29.49136, - 'lon': -82.50622 - }, - 'order': 'asc', - 'distance_type': 'plane' - } - } - ], - 'size': vs.size, - 'track_scores': true -}; diff --git a/test/unit/fixture/reverse_with_source_filtering.js b/test/unit/fixture/reverse_with_source_filtering.js deleted file mode 100644 index b2bcb0d9..00000000 --- a/test/unit/fixture/reverse_with_source_filtering.js +++ /dev/null @@ -1,46 +0,0 @@ -var vs = require('../../../query/reverse_defaults'); - -module.exports = { - 'query': { - 'bool': { - 'filter': [ - { - 'geo_distance': { - 'distance': '3km', - 'distance_type': 'plane', - 'optimize_bbox': 'indexed', - 'center_point': { - 'lat': 29.49136, - 'lon': -82.50622 - } - } - }, - { - 'terms': { - 'source': ['test'] - } - }, - { - 'terms': { - 'layer': ['venue', 'address', 'street'] - } - } - ] - } - }, - 'sort': [ - '_score', - { - '_geo_distance': { - 'center_point': { - 'lat': 29.49136, - 'lon': -82.50622 - }, - 'order': 'asc', - 'distance_type': 'plane' - } - } - ], - 'size': vs.size, - 'track_scores': true -}; diff --git a/test/unit/fixture/search_linguistic_viewport.js b/test/unit/fixture/search_linguistic_viewport.js deleted file mode 100644 index e30751ea..00000000 --- a/test/unit/fixture/search_linguistic_viewport.js +++ /dev/null @@ -1,71 +0,0 @@ -module.exports = { - 'query': { - 'function_score': { - 'query': { - 'bool': { - 'minimum_should_match': 1, - 'should': [ - { - 'bool': { - '_name': 'fallback.street', - 'boost': 5, - 'must': [ - { - 'match_phrase': { - 'address_parts.street': 'street value' - } - } - ], - 'should': [], - 'filter': { - 'term': { - 'layer': 'street' - } - } - } - } - ], - 'filter': { - 'bool': { - 'must': [ - { - 'terms': { - 'layer': [ - 'test' - ] - } - } - ] - } - } - } - }, - 'max_boost': 20, - 'functions': [ - { - 'field_value_factor': { - 'modifier': 'log1p', - 'field': 'popularity', - 'missing': 1 - }, - 'weight': 1 - }, - { - 'field_value_factor': { - 'modifier': 'log1p', - 'field': 'population', - 'missing': 1 - }, - 'weight': 2 - } - ], - 'score_mode': 'avg', - 'boost_mode': 'multiply' - } - }, - 'size': 10, - 'track_scores': true, - 'sort': [ - '_score' - ] -}; diff --git a/test/unit/fixture/search_linguistic_viewport_min_diagonal.js b/test/unit/fixture/search_linguistic_viewport_min_diagonal.js deleted file mode 100644 index e30751ea..00000000 --- a/test/unit/fixture/search_linguistic_viewport_min_diagonal.js +++ /dev/null @@ -1,71 +0,0 @@ -module.exports = { - 'query': { - 'function_score': { - 'query': { - 'bool': { - 'minimum_should_match': 1, - 'should': [ - { - 'bool': { - '_name': 'fallback.street', - 'boost': 5, - 'must': [ - { - 'match_phrase': { - 'address_parts.street': 'street value' - } - } - ], - 'should': [], - 'filter': { - 'term': { - 'layer': 'street' - } - } - } - } - ], - 'filter': { - 'bool': { - 'must': [ - { - 'terms': { - 'layer': [ - 'test' - ] - } - } - ] - } - } - } - }, - 'max_boost': 20, - 'functions': [ - { - 'field_value_factor': { - 'modifier': 'log1p', - 'field': 'popularity', - 'missing': 1 - }, - 'weight': 1 - }, - { - 'field_value_factor': { - 'modifier': 'log1p', - 'field': 'population', - 'missing': 1 - }, - 'weight': 2 - } - ], - 'score_mode': 'avg', - 'boost_mode': 'multiply' - } - }, - 'size': 10, - 'track_scores': true, - 'sort': [ - '_score' - ] -}; diff --git a/test/unit/fixture/structured_geocoding/boundary_country.json b/test/unit/fixture/structured_geocoding/boundary_country.json deleted file mode 100644 index edff5d63..00000000 --- a/test/unit/fixture/structured_geocoding/boundary_country.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "query": { - "function_score": { - "query": { - "bool": { - "minimum_should_match": 1, - "should": [], - "filter": { - "bool": { - "must": [ - { - "match": { - "parent.country_a": { - "analyzer": "standard", - "query": "ABC" - } - } - }, - { - "terms": { - "layer": [ - "test" - ] - } - } - ] - } - } - } - }, - "max_boost": 20, - "functions": [ - { - "field_value_factor": { - "modifier": "log1p", - "field": "popularity", - "missing": 1 - }, - "weight": 1 - }, - { - "field_value_factor": { - "modifier": "log1p", - "field": "population", - "missing": 1 - }, - "weight": 2 - } - ], - "score_mode": "avg", - "boost_mode": "multiply" - } - }, - "sort": [ - "_score" - ], - "size": 10, - "track_scores": true -} diff --git a/test/unit/fixture/structured_geocoding/fallback.json b/test/unit/fixture/structured_geocoding/fallback.json deleted file mode 100644 index 7b737ffd..00000000 --- a/test/unit/fixture/structured_geocoding/fallback.json +++ /dev/null @@ -1,859 +0,0 @@ -{ - "query": { - "function_score": { - "query": { - "bool": { - "minimum_should_match": 1, - "should": [ - { - "bool": { - "_name": "fallback.venue", - "must": [ - { - "multi_match": { - "query": "query value", - "type": "phrase", - "fields": [ - "phrase.default", - "category" - ] - } - }, - { - "multi_match": { - "query": "neighbourhood value", - "type": "phrase", - "fields": [ - "parent.neighbourhood", - "parent.neighbourhood_a" - ] - } - }, - { - "multi_match": { - "query": "borough value", - "type": "phrase", - "fields": [ - "parent.borough", - "parent.borough_a" - ] - } - }, - { - "multi_match": { - "query": "city value", - "type": "phrase", - "fields": [ - "parent.locality", - "parent.locality_a", - "parent.localadmin", - "parent.localadmin_a" - ] - } - }, - { - "multi_match": { - "query": "county value", - "type": "phrase", - "fields": [ - "parent.county", - "parent.county_a", - "parent.macrocounty", - "parent.macrocounty_a" - ] - } - }, - { - "multi_match": { - "query": "state value", - "type": "phrase", - "fields": [ - "parent.region", - "parent.region_a", - "parent.macroregion", - "parent.macroregion_a" - ] - } - }, - { - "multi_match": { - "query": "country value", - "type": "phrase", - "fields": [ - "parent.country", - "parent.country_a", - "parent.dependency", - "parent.dependency_a" - ] - } - } - ], - "filter": { - "term": { - "layer": "venue" - } - } - } - }, - { - "bool": { - "_name": "fallback.address", - "must": [ - { - "match_phrase": { - "address_parts.number": "number value" - } - }, - { - "match_phrase": { - "address_parts.street": "street value" - } - }, - { - "multi_match": { - "query": "neighbourhood value", - "type": "phrase", - "fields": [ - "parent.neighbourhood", - "parent.neighbourhood_a" - ] - } - }, - { - "multi_match": { - "query": "borough value", - "type": "phrase", - "fields": [ - "parent.borough", - "parent.borough_a" - ] - } - }, - { - "multi_match": { - "query": "city value", - "type": "phrase", - "fields": [ - "parent.locality", - "parent.locality_a", - "parent.localadmin", - "parent.localadmin_a" - ] - } - }, - { - "multi_match": { - "query": "county value", - "type": "phrase", - "fields": [ - "parent.county", - "parent.county_a", - "parent.macrocounty", - "parent.macrocounty_a" - ] - } - }, - { - "multi_match": { - "query": "state value", - "type": "phrase", - "fields": [ - "parent.region", - "parent.region_a", - "parent.macroregion", - "parent.macroregion_a" - ] - } - }, - { - "multi_match": { - "query": "country value", - "type": "phrase", - "fields": [ - "parent.country", - "parent.country_a", - "parent.dependency", - "parent.dependency_a" - ] - } - } - ], - "should": [ - { - "match_phrase": { - "address_parts.zip": "postalcode value" - } - } - ], - "filter": { - "term": { - "layer": "address" - } - }, - "boost": 10 - } - }, - { - "bool": { - "_name": "fallback.street", - "must": [ - { - "match_phrase": { - "address_parts.street": "street value" - } - }, - { - "multi_match": { - "query": "neighbourhood value", - "type": "phrase", - "fields": [ - "parent.neighbourhood", - "parent.neighbourhood_a" - ] - } - }, - { - "multi_match": { - "query": "borough value", - "type": "phrase", - "fields": [ - "parent.borough", - "parent.borough_a" - ] - } - }, - { - "multi_match": { - "query": "city value", - "type": "phrase", - "fields": [ - "parent.locality", - "parent.locality_a", - "parent.localadmin", - "parent.localadmin_a" - ] - } - }, - { - "multi_match": { - "query": "county value", - "type": "phrase", - "fields": [ - "parent.county", - "parent.county_a", - "parent.macrocounty", - "parent.macrocounty_a" - ] - } - }, - { - "multi_match": { - "query": "state value", - "type": "phrase", - "fields": [ - "parent.region", - "parent.region_a", - "parent.macroregion", - "parent.macroregion_a" - ] - } - }, - { - "multi_match": { - "query": "country value", - "type": "phrase", - "fields": [ - "parent.country", - "parent.country_a", - "parent.dependency", - "parent.dependency_a" - ] - } - } - ], - "should": [ - { - "match_phrase": { - "address_parts.zip": "postalcode value" - } - } - ], - "filter": { - "term": { - "layer": "street" - } - }, - "boost": 5 - } - }, - { - "bool": { - "_name": "fallback.postalcode", - "must": [ - { - "multi_match": { - "query": "postalcode value", - "type": "phrase", - "fields": [ - "parent.postalcode" - ] - } - }, - { - "multi_match": { - "query": "city value", - "type": "phrase", - "fields": [ - "parent.locality", - "parent.locality_a", - "parent.localadmin", - "parent.localadmin_a" - ] - } - }, - { - "multi_match": { - "query": "county value", - "type": "phrase", - "fields": [ - "parent.county", - "parent.county_a", - "parent.macrocounty", - "parent.macrocounty_a" - ] - } - }, - { - "multi_match": { - "query": "state value", - "type": "phrase", - "fields": [ - "parent.region", - "parent.region_a", - "parent.macroregion", - "parent.macroregion_a" - ] - } - }, - { - "multi_match": { - "query": "country value", - "type": "phrase", - "fields": [ - "parent.country", - "parent.country_a", - "parent.dependency", - "parent.dependency_a" - ] - } - } - ], - "filter": { - "term": { - "layer": "postalcode" - } - } - } - }, - { - "bool": { - "_name": "fallback.neighbourhood", - "must": [ - { - "multi_match": { - "query": "neighbourhood value", - "type": "phrase", - "fields": [ - "parent.neighbourhood", - "parent.neighbourhood_a" - ] - } - }, - { - "multi_match": { - "query": "borough value", - "type": "phrase", - "fields": [ - "parent.borough", - "parent.borough_a" - ] - } - }, - { - "multi_match": { - "query": "city value", - "type": "phrase", - "fields": [ - "parent.locality", - "parent.locality_a", - "parent.localadmin", - "parent.localadmin_a" - ] - } - }, - { - "multi_match": { - "query": "county value", - "type": "phrase", - "fields": [ - "parent.county", - "parent.county_a", - "parent.macrocounty", - "parent.macrocounty_a" - ] - } - }, - { - "multi_match": { - "query": "state value", - "type": "phrase", - "fields": [ - "parent.region", - "parent.region_a", - "parent.macroregion", - "parent.macroregion_a" - ] - } - }, - { - "multi_match": { - "query": "country value", - "type": "phrase", - "fields": [ - "parent.country", - "parent.country_a", - "parent.dependency", - "parent.dependency_a" - ] - } - } - ], - "filter": { - "term": { - "layer": "neighbourhood" - } - } - } - }, - { - "bool": { - "_name": "fallback.borough", - "must": [ - { - "multi_match": { - "query": "borough value", - "type": "phrase", - "fields": [ - "parent.borough", - "parent.borough_a" - ] - } - }, - { - "multi_match": { - "query": "city value", - "type": "phrase", - "fields": [ - "parent.locality", - "parent.locality_a", - "parent.localadmin", - "parent.localadmin_a" - ] - } - }, - { - "multi_match": { - "query": "county value", - "type": "phrase", - "fields": [ - "parent.county", - "parent.county_a", - "parent.macrocounty", - "parent.macrocounty_a" - ] - } - }, - { - "multi_match": { - "query": "state value", - "type": "phrase", - "fields": [ - "parent.region", - "parent.region_a", - "parent.macroregion", - "parent.macroregion_a" - ] - } - }, - { - "multi_match": { - "query": "country value", - "type": "phrase", - "fields": [ - "parent.country", - "parent.country_a", - "parent.dependency", - "parent.dependency_a" - ] - } - } - ], - "filter": { - "term": { - "layer": "borough" - } - } - } - }, - { - "bool": { - "_name": "fallback.locality", - "must": [ - { - "multi_match": { - "query": "city value", - "type": "phrase", - "fields": [ - "parent.locality", - "parent.locality_a" - ] - } - }, - { - "multi_match": { - "query": "county value", - "type": "phrase", - "fields": [ - "parent.county", - "parent.county_a", - "parent.macrocounty", - "parent.macrocounty_a" - ] - } - }, - { - "multi_match": { - "query": "state value", - "type": "phrase", - "fields": [ - "parent.region", - "parent.region_a", - "parent.macroregion", - "parent.macroregion_a" - ] - } - }, - { - "multi_match": { - "query": "country value", - "type": "phrase", - "fields": [ - "parent.country", - "parent.country_a", - "parent.dependency", - "parent.dependency_a" - ] - } - } - ], - "filter": { - "term": { - "layer": "locality" - } - } - } - }, - { - "bool": { - "_name": "fallback.localadmin", - "must": [ - { - "multi_match": { - "query": "city value", - "type": "phrase", - "fields": [ - "parent.localadmin", - "parent.localadmin_a" - ] - } - }, - { - "multi_match": { - "query": "county value", - "type": "phrase", - "fields": [ - "parent.county", - "parent.county_a", - "parent.macrocounty", - "parent.macrocounty_a" - ] - } - }, - { - "multi_match": { - "query": "state value", - "type": "phrase", - "fields": [ - "parent.region", - "parent.region_a", - "parent.macroregion", - "parent.macroregion_a" - ] - } - }, - { - "multi_match": { - "query": "country value", - "type": "phrase", - "fields": [ - "parent.country", - "parent.country_a", - "parent.dependency", - "parent.dependency_a" - ] - } - } - ], - "filter": { - "term": { - "layer": "localadmin" - } - } - } - }, - { - "bool": { - "_name": "fallback.county", - "must": [ - { - "multi_match": { - "query": "county value", - "type": "phrase", - "fields": [ - "parent.county", - "parent.county_a" - ] - } - }, - { - "multi_match": { - "query": "state value", - "type": "phrase", - "fields": [ - "parent.region", - "parent.region_a", - "parent.macroregion", - "parent.macroregion_a" - ] - } - }, - { - "multi_match": { - "query": "country value", - "type": "phrase", - "fields": [ - "parent.country", - "parent.country_a", - "parent.dependency", - "parent.dependency_a" - ] - } - } - ], - "filter": { - "term": { - "layer": "county" - } - } - } - }, - { - "bool": { - "_name": "fallback.macrocounty", - "must": [ - { - "multi_match": { - "query": "county value", - "type": "phrase", - "fields": [ - "parent.macrocounty", - "parent.macrocounty_a" - ] - } - }, - { - "multi_match": { - "query": "state value", - "type": "phrase", - "fields": [ - "parent.region", - "parent.region_a", - "parent.macroregion", - "parent.macroregion_a" - ] - } - }, - { - "multi_match": { - "query": "country value", - "type": "phrase", - "fields": [ - "parent.country", - "parent.country_a", - "parent.dependency", - "parent.dependency_a" - ] - } - } - ], - "filter": { - "term": { - "layer": "macrocounty" - } - } - } - }, - { - "bool": { - "_name": "fallback.region", - "must": [ - { - "multi_match": { - "query": "state value", - "type": "phrase", - "fields": [ - "parent.region", - "parent.region_a" - ] - } - }, - { - "multi_match": { - "query": "country value", - "type": "phrase", - "fields": [ - "parent.country", - "parent.country_a", - "parent.dependency", - "parent.dependency_a" - ] - } - } - ], - "filter": { - "term": { - "layer": "region" - } - } - } - }, - { - "bool": { - "_name": "fallback.macroregion", - "must": [ - { - "multi_match": { - "query": "state value", - "type": "phrase", - "fields": [ - "parent.macroregion", - "parent.macroregion_a" - ] - } - }, - { - "multi_match": { - "query": "country value", - "type": "phrase", - "fields": [ - "parent.country", - "parent.country_a", - "parent.dependency", - "parent.dependency_a" - ] - } - } - ], - "filter": { - "term": { - "layer": "macroregion" - } - } - } - }, - { - "bool": { - "_name": "fallback.dependency", - "must": [ - { - "multi_match": { - "query": "country value", - "type": "phrase", - "fields": [ - "parent.dependency", - "parent.dependency_a" - ] - } - } - ], - "filter": { - "term": { - "layer": "dependency" - } - } - } - }, - { - "bool": { - "_name": "fallback.country", - "must": [ - { - "multi_match": { - "query": "country value", - "type": "phrase", - "fields": [ - "parent.country", - "parent.country_a" - ] - } - } - ], - "filter": { - "term": { - "layer": "country" - } - } - } - } - ] - } - }, - "max_boost": 20, - "functions": [ - { - "field_value_factor": { - "modifier": "log1p", - "field": "popularity", - "missing": 1 - }, - "weight": 1 - }, - { - "field_value_factor": { - "modifier": "log1p", - "field": "population", - "missing": 1 - }, - "weight": 2 - } - ], - "score_mode": "avg", - "boost_mode": "multiply" - } - }, - "sort": [ - "_score" - ], - "size": 20, - "track_scores": true -} diff --git a/test/unit/fixture/structured_geocoding/linguistic_bbox.json b/test/unit/fixture/structured_geocoding/linguistic_bbox.json deleted file mode 100644 index 94acc81d..00000000 --- a/test/unit/fixture/structured_geocoding/linguistic_bbox.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "query": { - "function_score": { - "query": { - "bool": { - "minimum_should_match": 1, - "should": [], - "filter": { - "bool": { - "must": [ - { - "geo_bounding_box": { - "type": "indexed", - "center_point": { - "top": 11.51, - "right": -61.84, - "bottom": 47.47, - "left": -103.16 - } - } - }, - { - "terms": { - "layer": [ - "test" - ] - } - } - ] - } - } - } - }, - "max_boost": 20, - "functions": [ - { - "field_value_factor": { - "modifier": "log1p", - "field": "popularity", - "missing": 1 - }, - "weight": 1 - }, - { - "field_value_factor": { - "modifier": "log1p", - "field": "population", - "missing": 1 - }, - "weight": 2 - } - ], - "score_mode": "avg", - "boost_mode": "multiply" - } - }, - "sort": [ - "_score" - ], - "size": 10, - "track_scores": true -} diff --git a/test/unit/fixture/structured_geocoding/linguistic_focus.json b/test/unit/fixture/structured_geocoding/linguistic_focus.json deleted file mode 100644 index 0bb126d1..00000000 --- a/test/unit/fixture/structured_geocoding/linguistic_focus.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "query": { - "function_score": { - "query": { - "bool": { - "minimum_should_match": 1, - "should": [], - "filter": { - "bool": { - "must": [ - { - "terms": { - "layer": [ - "test" - ] - } - } - ] - } - } - } - }, - "max_boost": 20, - "functions": [ - { - "weight": 2, - "linear": { - "center_point": { - "origin": { - "lat": 29.49136, - "lon": -82.50622 - }, - "offset": "0km", - "scale": "50km", - "decay": 0.5 - } - } - }, - { - "field_value_factor": { - "modifier": "log1p", - "field": "popularity", - "missing": 1 - }, - "weight": 1 - }, - { - "field_value_factor": { - "modifier": "log1p", - "field": "population", - "missing": 1 - }, - "weight": 2 - } - ], - "score_mode": "avg", - "boost_mode": "multiply" - } - }, - "sort": [ - "_score" - ], - "size": 10, - "track_scores": true -} diff --git a/test/unit/fixture/structured_geocoding/linguistic_focus_bbox.json b/test/unit/fixture/structured_geocoding/linguistic_focus_bbox.json deleted file mode 100644 index 5b98f11f..00000000 --- a/test/unit/fixture/structured_geocoding/linguistic_focus_bbox.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "query": { - "function_score": { - "query": { - "bool": { - "minimum_should_match": 1, - "should": [], - "filter": { - "bool": { - "must": [ - { - "geo_bounding_box": { - "type": "indexed", - "center_point": { - "top": 11.51, - "right": -61.84, - "bottom": 47.47, - "left": -103.16 - } - } - }, - { - "terms": { - "layer": [ - "test" - ] - } - } - ] - } - } - } - }, - "max_boost": 20, - "functions": [ - { - "weight": 2, - "linear": { - "center_point": { - "origin": { - "lat": 29.49136, - "lon": -82.50622 - }, - "offset": "0km", - "scale": "50km", - "decay": 0.5 - } - } - }, - { - "field_value_factor": { - "modifier": "log1p", - "field": "popularity", - "missing": 1 - }, - "weight": 1 - }, - { - "field_value_factor": { - "modifier": "log1p", - "field": "population", - "missing": 1 - }, - "weight": 2 - } - ], - "score_mode": "avg", - "boost_mode": "multiply" - } - }, - "sort": [ - "_score" - ], - "size": 10, - "track_scores": true -} diff --git a/test/unit/fixture/structured_geocoding/linguistic_focus_null_island.json b/test/unit/fixture/structured_geocoding/linguistic_focus_null_island.json deleted file mode 100644 index d8723845..00000000 --- a/test/unit/fixture/structured_geocoding/linguistic_focus_null_island.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "query": { - "function_score": { - "query": { - "bool": { - "minimum_should_match": 1, - "should": [], - "filter": { - "bool": { - "must": [ - { - "terms": { - "layer": [ - "test" - ] - } - } - ] - } - } - } - }, - "max_boost": 20, - "functions": [ - { - "weight": 2, - "linear": { - "center_point": { - "origin": { - "lat": 0, - "lon": 0 - }, - "offset": "0km", - "scale": "50km", - "decay": 0.5 - } - } - }, - { - "field_value_factor": { - "modifier": "log1p", - "field": "popularity", - "missing": 1 - }, - "weight": 1 - }, - { - "field_value_factor": { - "modifier": "log1p", - "field": "population", - "missing": 1 - }, - "weight": 2 - } - ], - "score_mode": "avg", - "boost_mode": "multiply" - } - }, - "sort": [ - "_score" - ], - "size": 10, - "track_scores": true -} diff --git a/test/unit/fixture/structured_geocoding/linguistic_only.json b/test/unit/fixture/structured_geocoding/linguistic_only.json deleted file mode 100644 index 4de54360..00000000 --- a/test/unit/fixture/structured_geocoding/linguistic_only.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "query": { - "function_score": { - "query": { - "bool": { - "minimum_should_match": 1, - "should": [], - "filter": { - "bool": { - "must": [ - { - "terms": { - "layer": [ - "test" - ] - } - } - ] - } - } - } - }, - "max_boost": 20, - "functions": [ - { - "field_value_factor": { - "modifier": "log1p", - "field": "popularity", - "missing": 1 - }, - "weight": 1 - }, - { - "field_value_factor": { - "modifier": "log1p", - "field": "population", - "missing": 1 - }, - "weight": 2 - } - ], - "score_mode": "avg", - "boost_mode": "multiply" - } - }, - "sort": [ - "_score" - ], - "size": 10, - "track_scores": true -} diff --git a/test/unit/fixture/structured_geocoding/linguistic_viewport.json b/test/unit/fixture/structured_geocoding/linguistic_viewport.json deleted file mode 100644 index 4de54360..00000000 --- a/test/unit/fixture/structured_geocoding/linguistic_viewport.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "query": { - "function_score": { - "query": { - "bool": { - "minimum_should_match": 1, - "should": [], - "filter": { - "bool": { - "must": [ - { - "terms": { - "layer": [ - "test" - ] - } - } - ] - } - } - } - }, - "max_boost": 20, - "functions": [ - { - "field_value_factor": { - "modifier": "log1p", - "field": "popularity", - "missing": 1 - }, - "weight": 1 - }, - { - "field_value_factor": { - "modifier": "log1p", - "field": "population", - "missing": 1 - }, - "weight": 2 - } - ], - "score_mode": "avg", - "boost_mode": "multiply" - } - }, - "sort": [ - "_score" - ], - "size": 10, - "track_scores": true -} diff --git a/test/unit/fixture/structured_geocoding/linguistic_viewport_min_diagonal.json b/test/unit/fixture/structured_geocoding/linguistic_viewport_min_diagonal.json deleted file mode 100644 index 4de54360..00000000 --- a/test/unit/fixture/structured_geocoding/linguistic_viewport_min_diagonal.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "query": { - "function_score": { - "query": { - "bool": { - "minimum_should_match": 1, - "should": [], - "filter": { - "bool": { - "must": [ - { - "terms": { - "layer": [ - "test" - ] - } - } - ] - } - } - } - }, - "max_boost": 20, - "functions": [ - { - "field_value_factor": { - "modifier": "log1p", - "field": "popularity", - "missing": 1 - }, - "weight": 1 - }, - { - "field_value_factor": { - "modifier": "log1p", - "field": "population", - "missing": 1 - }, - "weight": 2 - } - ], - "score_mode": "avg", - "boost_mode": "multiply" - } - }, - "sort": [ - "_score" - ], - "size": 10, - "track_scores": true -} diff --git a/test/unit/fixture/structured_geocoding/postalcode_only.js b/test/unit/fixture/structured_geocoding/postalcode_only.js deleted file mode 100644 index 2eb10d30..00000000 --- a/test/unit/fixture/structured_geocoding/postalcode_only.js +++ /dev/null @@ -1,60 +0,0 @@ -module.exports = { - 'query': { - 'function_score': { - 'query': { - 'bool': { - 'minimum_should_match': 1, - 'should': [ - { - 'bool': { - '_name': 'fallback.postalcode', - 'must': [ - { - 'multi_match': { - 'query': 'postalcode value', - 'type': 'phrase', - 'fields': [ - 'parent.postalcode' - ] - } - } - ], - 'filter': { - 'term': { - 'layer': 'postalcode' - } - } - } - } - ] - } - }, - 'max_boost': 20, - 'functions': [ - { - 'field_value_factor': { - 'modifier': 'log1p', - 'field': 'popularity', - 'missing': 1 - }, - 'weight': 1 - }, - { - 'field_value_factor': { - 'modifier': 'log1p', - 'field': 'population', - 'missing': 1 - }, - 'weight': 2 - } - ], - 'score_mode': 'avg', - 'boost_mode': 'multiply' - } - }, - 'size': 20, - 'track_scores': true, - 'sort': [ - '_score' - ] -}; diff --git a/test/unit/fixture/structured_geocoding/with_source_filtering.json b/test/unit/fixture/structured_geocoding/with_source_filtering.json deleted file mode 100644 index 77e4fb87..00000000 --- a/test/unit/fixture/structured_geocoding/with_source_filtering.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "query": { - "function_score": { - "query": { - "bool": { - "minimum_should_match": 1, - "should": [ - ], - "filter": { - "bool": { - "must": [ - { - "terms": { - "source": [ - "test_source" - ] - } - } - ] - } - } - } - }, - "max_boost": 20, - "functions": [ - { - "field_value_factor": { - "modifier": "log1p", - "field": "popularity", - "missing": 1 - }, - "weight": 1 - }, - { - "field_value_factor": { - "modifier": "log1p", - "field": "population", - "missing": 1 - }, - "weight": 2 - } - ], - "score_mode": "avg", - "boost_mode": "multiply" - } - }, - "sort": [ - "_score" - ], - "size": 20, - "track_scores": true -} diff --git a/test/unit/query/MockQuery.js b/test/unit/query/MockQuery.js index f97bfb27..e7202cc9 100644 --- a/test/unit/query/MockQuery.js +++ b/test/unit/query/MockQuery.js @@ -3,6 +3,7 @@ module.exports = class MockQuery { constructor() { this._score_functions = []; + this._sort_functions = []; this._filter_functions = []; } @@ -10,6 +11,7 @@ module.exports = class MockQuery { return { vs: vs, score_functions: this._score_functions, + sort_functions: this._sort_functions, filter_functions: this._filter_functions }; } @@ -19,6 +21,11 @@ module.exports = class MockQuery { return this; } + sort(view) { + this._sort_functions.push(view); + return this; + } + filter(view) { this._filter_functions.push(view); return this; diff --git a/test/unit/query/reverse.js b/test/unit/query/reverse.js index eab97188..78c98e0c 100644 --- a/test/unit/query/reverse.js +++ b/test/unit/query/reverse.js @@ -1,190 +1,503 @@ -var generate = require('../../../query/reverse'); +const generate = require('../../../query/reverse'); +const _ = require('lodash'); +const proxyquire = require('proxyquire').noCallThru(); +const MockQuery = require('./MockQuery'); +const all_layers = require('../../../helper/type_mapping').layers; + +// helper for canned views +const views = { + boundary_country: 'boundary_country view', + boundary_circle: 'boundary_circle view', + sources: 'sources view', + layers: 'layers view', + categories: 'categories view', + sort_distance: 'sort_distance view' +}; module.exports.tests = {}; -module.exports.tests.interface = function(test, common) { - test('valid interface', function(t) { +module.exports.tests.interface = (test, common) => { + test('valid interface', t => { t.equal(typeof generate, 'function', 'valid function'); t.end(); }); }; -module.exports.tests.query = function(test, common) { - test('valid query', function(t) { - var query = generate({ - 'point.lat': 29.49136, - 'point.lon': -82.50622, - 'boundary.circle.lat': 29.49136, - 'boundary.circle.lon': -82.50622, - 'boundary.circle.radius': 3 - }); +module.exports.tests.query = (test, common) => { + test('base no frills', t => { + const clean = {}; + + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': { + default_parameter_1: 'first default parameter', + default_parameter_2: 'second default parameter' + } + })(clean); + + t.equals(query.type, 'reverse', 'query type set'); + t.deepEquals(query.body.vs.var('default_parameter_1').toString(), 'first default parameter'); + t.deepEquals(query.body.vs.var('default_parameter_2').toString(), 'second default parameter'); + t.notOk(query.body.vs.isset('size')); + t.notOk(query.body.vs.isset('sources')); + t.notOk(query.body.vs.isset('layers')); + t.notOk(query.body.vs.isset('focus:point:lat')); + t.notOk(query.body.vs.isset('focus:point:lon')); + t.notOk(query.body.vs.isset('boundary:circle:lat')); + t.notOk(query.body.vs.isset('boundary:circle:lon')); + t.notOk(query.body.vs.isset('boundary:circle:radius')); + t.notOk(query.body.vs.isset('boundary:country')); + t.notOk(query.body.vs.isset('input:categories')); + + t.deepEquals(query.body.score_functions, [ + 'boundary_country view' + ]); + + t.deepEquals(query.body.filter_functions, [ + 'boundary_circle view', + 'sources view', + 'layers view', + 'categories view' + ]); + + t.deepEquals(query.body.sort_functions, [ + 'sort_distance view' + ]); + + t.end(); - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/reverse_standard'); + }); - t.deepEqual(compiled.type, 'reverse', 'query type set'); - t.deepEqual(compiled.body, expected, 'reverse_standard'); + test('clean.querySize should set size parameter', t => { + const clean = { + querySize: 17 + }; + + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.deepEquals(query.body.vs.var('size').toString(), 17); t.end(); + }); - test('valid query - null island', function(t) { - var query = generate({ - 'point.lat': 0, - 'point.lon': 0, - 'boundary.circle.lat': 0, - 'boundary.circle.lon': 0, - 'boundary.circle.radius': 3 - }); +}; - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/reverse_null_island'); +module.exports.tests.sources = (test, common) => { + test('non-array clean.sources should not set sources in vs', t => { + const clean = { + sources: 'this is not an array' + }; - t.deepEqual(compiled.type, 'reverse', 'query type set'); - t.deepEqual(compiled.body, expected, 'reverse_null_island'); + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.notOk(query.body.vs.isset('sources')); t.end(); + }); - test('valid query with radius', function(t) { - var query = generate({ - 'point.lat': 29.49136, - 'point.lon': -82.50622, - 'boundary.circle.lat': 29.49136, - 'boundary.circle.lon': -82.50622, - 'boundary.circle.radius': 123 - }); + test('empty array clean.sources should not set sources in vs', t => { + const clean = { + sources: [] + }; + + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.notOk(query.body.vs.isset('sources')); + t.end(); - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = '123km'; + }); + + test('non-empty array clean.sources should set sources in vs', t => { + const clean = { + sources: ['source 1', 'source 2'] + }; - t.deepEqual(compiled.type, 'reverse', 'query type set'); - t.deepEqual(compiled.body.query.bool.filter[0].geo_distance.distance, expected, 'distance set to boundary circle radius'); + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.deepEquals(query.body.vs.var('sources').toString(), ['source 1', 'source 2']); t.end(); + }); - // for coarse reverse cases where boundary circle radius isn't used - test('undefined radius set to default radius', function(t) { - var query = generate({ - 'point.lat': 12.12121, - 'point.lon': 21.21212, - 'boundary.circle.lat': 12.12121, - 'boundary.circle.lon': 21.21212 - }); +}; - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = '1km'; +module.exports.tests.layers = (test, common) => { + test('non-array clean.layers should not set sources in vs', t => { + const clean = { + layers: 'this is not an array' + }; - t.deepEqual(compiled.type, 'reverse', 'query type set'); - t.deepEqual(compiled.body.query.bool.filter[0].geo_distance.distance, expected, 'distance set to default boundary circle radius'); + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.notOk(query.body.vs.isset('layers')); t.end(); + }); - test('boundary.circle lat/lon/radius - overrides point.lat/lon when set', function(t) { - var clean = { - 'point.lat': 29.49136, - 'point.lon': -82.50622, - 'boundary.circle.lat': 111, - 'boundary.circle.lon': 333, - 'boundary.circle.radius': 3 + test('empty array clean.layers should not set sources in vs', t => { + const clean = { + layers: [] }; - var query = generate(clean); - var compiled = JSON.parse( JSON.stringify( query ) ); - // this should not equal `point.lat` and `point.lon` as it was explitely specified - var expected = { lat: clean['boundary.circle.lat'], lon: clean['boundary.circle.lon'] }; - var centroid = compiled.body.query.bool.filter[0].geo_distance.center_point; + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.notOk(query.body.vs.isset('layers')); + t.end(); + + }); + + test('non-empty array clean.layers should only set non-coarse layers in vs', t => { + const clean = { + layers: all_layers + }; - t.deepEqual(compiled.type, 'reverse', 'query type set'); - t.deepEqual(centroid, expected, 'reverse: boundary.circle/lon overrides point.lat/lon'); + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.deepEquals(query.body.vs.var('layers').toString(), ['address', 'venue', 'street']); t.end(); + }); - test('size fuzz test', function(t) { - // test different sizes - var sizes = [1,4,20,undefined,null]; - var expected = [1,4,20,1,1]; - sizes.forEach( function( size, index ){ - var query = generate({ - 'point.lat': 29.49136, 'point.lon': -82.50622, querySize: size - }); +}; + +module.exports.tests.focus_point = (test, common) => { + test('numeric point.lat and non-numeric point.lon should not add focus:point:* fields', t => { + const clean = { + 'point.lat': 12.121212, + 'point.lon': 'this is non-numeric' + }; - var compiled = JSON.parse( JSON.stringify( query ) ); - t.equal( compiled.body.size, expected[index], 'valid reverse query for size: '+ size); - }); + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.notOk(query.body.vs.isset('focus:point:lat')); + t.notOk(query.body.vs.isset('focus:point:lon')); t.end(); + }); - test('valid boundary.country reverse search', function(t) { - var query = generate({ - 'point.lat': 29.49136, - 'point.lon': -82.50622, - 'boundary.circle.lat': 29.49136, - 'boundary.circle.lon': -82.50622, - 'boundary.circle.radius': 3, - 'boundary.country': 'ABC' - }); + test('non-numeric point.lat and numeric point.lon should not add focus:point:* fields', t => { + const clean = { + 'point.lat': 'this is non-numeric', + 'point.lon': 21.212121 + }; - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/reverse_with_boundary_country'); + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.notOk(query.body.vs.isset('focus:point:lat')); + t.notOk(query.body.vs.isset('focus:point:lon')); + t.end(); - t.deepEqual(compiled.type, 'reverse', 'query type set'); - t.deepEqual(compiled.body, expected, 'valid reverse query with boundary.country'); + }); + + test('numeric point.lat and point.lon should add focus:point:* fields', t => { + const clean = { + 'point.lat': 12.121212, + 'point.lon': 21.212121 + }; + + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.deepEquals(query.body.vs.var('focus:point:lat').toString(), 12.121212); + t.deepEquals(query.body.vs.var('focus:point:lon').toString(), 21.212121); t.end(); + }); - test('valid sources filter', function(t) { - var query = generate({ - 'point.lat': 29.49136, - 'point.lon': -82.50622, - 'boundary.circle.lat': 29.49136, - 'boundary.circle.lon': -82.50622, - 'boundary.circle.radius': 3, - 'sources': ['test'] - }); +}; - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/reverse_with_source_filtering'); +module.exports.tests.boundary_circle = (test, common) => { + test('numeric lat and non-numeric lon should not add boundary:circle:* fields', t => { + const clean = { + 'boundary.circle.lat': 12.121212, + 'boundary.circle.lon': 'this is non-numeric' + }; - t.deepEqual(compiled.type, 'reverse', 'query type set'); - t.deepEqual(compiled.body, expected, 'valid reverse query with source filtering'); + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.notOk(query.body.vs.isset('boundary:circle:lat')); + t.notOk(query.body.vs.isset('boundary:circle:lon')); + t.notOk(query.body.vs.isset('boundary:circle:radius')); t.end(); + }); - test('valid layers filter', (t) => { - const query = generate({ - 'point.lat': 29.49136, - 'point.lon': -82.50622, - 'boundary.circle.lat': 29.49136, - 'boundary.circle.lon': -82.50622, - 'boundary.circle.radius': 3, - // only venue, address, and street layers should be retained - 'layers': ['neighbourhood', 'venue', 'locality', 'address', 'region', 'street', 'country'] - }); + test('non-numeric lat and numeric lon should not add boundary:circle:* fields', t => { + const clean = { + 'boundary.circle.lat': 'this is non-numeric', + 'boundary.circle.lon': 21.212121 + }; + + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.notOk(query.body.vs.isset('boundary:circle:lat')); + t.notOk(query.body.vs.isset('boundary:circle:lon')); + t.notOk(query.body.vs.isset('boundary:circle:radius')); + t.end(); + + }); + + test('radius not supplied should default to value from reverse_defaults', t => { + const clean = { + 'boundary.circle.lat': 12.121212, + 'boundary.circle.lon': 21.212121 + }; + + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': { + 'boundary:circle:radius': 17 + } + })(clean); + + t.deepEquals(query.body.vs.var('boundary:circle:lat').toString(), 12.121212); + t.deepEquals(query.body.vs.var('boundary:circle:lon').toString(), 21.212121); + t.deepEquals(query.body.vs.var('boundary:circle:radius').toString(), 17); + t.end(); + + }); + + test('numeric radius supplied should be used instead of value from reverse_defaults', t => { + const clean = { + 'boundary.circle.lat': 12.121212, + 'boundary.circle.lon': 21.212121, + 'boundary.circle.radius': 17 + }; - const compiled = JSON.parse( JSON.stringify( query ) ); - const expected = require('../fixture/reverse_with_layer_filtering'); + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': { + 'boundary:circle:radius': 18 + } + })(clean); + + t.deepEquals(query.body.vs.var('boundary:circle:lat').toString(), 12.121212); + t.deepEquals(query.body.vs.var('boundary:circle:lon').toString(), 21.212121); + t.deepEquals(query.body.vs.var('boundary:circle:radius').toString(), '17km'); + t.end(); + + }); + + test('non-numeric radius supplied should not set any boundary:circle:radius', t => { + const clean = { + 'boundary.circle.lat': 12.121212, + 'boundary.circle.lon': 21.212121, + 'boundary.circle.radius': 'this is non-numeric' + }; - t.deepEqual(compiled.type, 'reverse', 'query type set'); - t.deepEqual(compiled.body, expected, 'valid reverse query with source filtering'); + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': { + 'boundary:circle:radius': 18 + } + })(clean); + + t.deepEquals(query.body.vs.var('boundary:circle:lat').toString(), 12.121212); + t.deepEquals(query.body.vs.var('boundary:circle:lon').toString(), 21.212121); + t.deepEquals(query.body.vs.var('boundary:circle:radius').toString(), 18); t.end(); }); - test('valid layers filter - subset of non-coarse layers', (t) => { - const query = generate({ - 'point.lat': 29.49136, - 'point.lon': -82.50622, - 'boundary.circle.lat': 29.49136, - 'boundary.circle.lon': -82.50622, - 'boundary.circle.radius': 3, - // only venue, address, and street layers should be retained - 'layers': ['neighbourhood', 'venue', 'street', 'locality'] +}; + +module.exports.tests.boundary_country = (test, common) => { + test('non-string boundary.country should not set boundary:country', t => { + [17, undefined, {}, [], true, null].forEach(value => { + const clean = { + 'boundary.country': value + }; + + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.notOk(query.body.vs.isset('boundary:country')); }); - const compiled = JSON.parse( JSON.stringify( query ) ); - const expected = require('../fixture/reverse_with_layer_filtering_non_coarse_subset'); + t.end(); + + }); + + test('string boundary.country should set boundary:country', t => { + const clean = { + 'boundary.country': 'boundary country value' + }; + + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.deepEquals(query.body.vs.var('boundary:country').toString(), 'boundary country value'); + t.end(); + + }); + +}; + +module.exports.tests.categories = (test, common) => { + test('categories supplied should set input:categories', t => { + const clean = { + categories: 'categories value' + }; - t.deepEqual(compiled.type, 'reverse', 'query type set'); - t.deepEqual(compiled.body, expected, 'valid reverse query with source filtering'); + const query = proxyquire('../../../query/reverse', { + 'pelias-query': { + layout: { + FilteredBooleanQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './reverse_defaults': {} + })(clean); + + t.deepEquals(query.body.vs.var('input:categories').toString(), 'categories value'); t.end(); }); diff --git a/test/unit/query/search.js b/test/unit/query/search.js index c97fb876..ea8a4f17 100644 --- a/test/unit/query/search.js +++ b/test/unit/query/search.js @@ -99,54 +99,6 @@ module.exports.tests.query = function(test, common) { t.end(); }); - test('search search + viewport', function(t) { - var clean = { - parsed_text: { - street: 'street value' - }, - text: 'test', querySize: 10, - 'focus.viewport.min_lat': 28.49136, - 'focus.viewport.max_lat': 30.49136, - 'focus.viewport.min_lon': -87.50622, - 'focus.viewport.max_lon': -77.50622, - layers: ['test'] - }; - - var query = generate(clean); - - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/search_linguistic_viewport'); - - t.deepEqual(compiled.type, 'fallback', 'query type set'); - t.deepEqual(compiled.body, expected, 'search_linguistic_viewport'); - t.end(); - }); - - // viewport scale sizing currently disabled. - // ref: https://github.com/pelias/api/pull/388 - test('search with viewport diagonal < 1km should set scale to 1km', function(t) { - var clean = { - parsed_text: { - street: 'street value' - }, - text: 'test', querySize: 10, - 'focus.viewport.min_lat': 28.49135, - 'focus.viewport.max_lat': 28.49137, - 'focus.viewport.min_lon': -87.50622, - 'focus.viewport.max_lon': -87.50624, - layers: ['test'] - }; - - var query = generate(clean); - - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/search_linguistic_viewport_min_diagonal'); - - t.deepEqual(compiled.type, 'fallback', 'query type set'); - t.deepEqual(compiled.body, expected, 'valid search query'); - t.end(); - }); - test('search search + focus on null island', function(t) { var clean = { parsed_text: { diff --git a/test/unit/query/structured_geocoding.js b/test/unit/query/structured_geocoding.js index d93a03aa..3fef87f0 100644 --- a/test/unit/query/structured_geocoding.js +++ b/test/unit/query/structured_geocoding.js @@ -1,253 +1,606 @@ -var generate = require('../../../query/structured_geocoding'); -var fs = require('fs'); +const generate = require('../../../query/structured_geocoding'); +const _ = require('lodash'); +const proxyquire = require('proxyquire').noCallThru(); +const MockQuery = require('./MockQuery'); + +// helper for canned views +const views = { + focus_only_function: () => 'focus_only_function view', + popularity_only_function: 'popularity_only_function view', + population_only_function: 'population_only_function view', + boundary_country: 'boundary_country view', + boundary_circle: 'boundary_circle view', + boundary_rect: 'boundary_rect view', + sources: 'sources view', + layers: 'layers view', + categories: 'categories view' +}; module.exports.tests = {}; -module.exports.tests.interface = function(test, common) { - test('valid interface', function(t) { +module.exports.tests.interface = (test, common) => { + test('valid interface', t => { t.equal(typeof generate, 'function', 'valid function'); t.end(); }); }; -module.exports.tests.query = function(test, common) { - test('valid search + focus + bbox', function(t) { - var clean = { - parsed_text: { - }, - text: 'test', - querySize: 10, - 'focus.point.lat': 29.49136, 'focus.point.lon': -82.50622, - 'boundary.rect.min_lat': 47.47, - 'boundary.rect.max_lon': -61.84, - 'boundary.rect.max_lat': 11.51, - 'boundary.rect.min_lon': -103.16, - layers: ['test'] +module.exports.tests.query = (test, common) => { + test('base no frills', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value' }; - var query = generate(clean); - - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/structured_geocoding/linguistic_focus_bbox'); + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './search_defaults': { + default_parameter_1: 'first default parameter', + default_parameter_2: 'second default parameter' + }, + './text_parser': () => { + t.fail('text_parser should not have been called'); + } + })(clean); + + t.equals(query.type, 'fallback', 'query type set'); + t.equals(query.body.vs.var('input:name').toString(), 'text value'); + t.equals(query.body.vs.var('sources').toString(), 'sources value'); + t.equals(query.body.vs.var('layers').toString(), 'layers value'); + t.deepEquals(query.body.vs.var('default_parameter_1').toString(), 'first default parameter'); + t.deepEquals(query.body.vs.var('default_parameter_2').toString(), 'second default parameter'); + t.notOk(query.body.vs.isset('size')); + t.notOk(query.body.vs.isset('focus:point:lat')); + t.notOk(query.body.vs.isset('focus:point:lon')); + t.notOk(query.body.vs.isset('boundary:rect:top')); + t.notOk(query.body.vs.isset('boundary:rect:right')); + t.notOk(query.body.vs.isset('boundary:rect:bottom')); + t.notOk(query.body.vs.isset('boundary:rect:left')); + t.notOk(query.body.vs.isset('boundary:circle:lat')); + t.notOk(query.body.vs.isset('boundary:circle:lon')); + t.notOk(query.body.vs.isset('boundary:circle:radius')); + t.notOk(query.body.vs.isset('boundary:country')); + + t.deepEquals(query.body.score_functions, [ + 'focus_only_function view', + 'popularity_only_function view', + 'population_only_function view' + ]); + + t.deepEquals(query.body.filter_functions, [ + 'boundary_country view', + 'boundary_circle view', + 'boundary_rect view', + 'sources view', + 'layers view', + 'categories view' + ]); - t.deepEqual(compiled.type, 'fallback', 'query type set'); - t.deepEqual(compiled.body, expected, 'search_linguistic_focus_bbox'); t.end(); }); - test('valid search + bbox', function(t) { - var clean = { - parsed_text: { + test('clean.parsed_text and vs should be passed to textParser', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + parsed_text: 'parsed_text value' + }; + + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './search_defaults': { + default_parameter_1: 'first default parameter', + default_parameter_2: 'second default parameter' }, - text: 'test', - querySize: 10, - 'boundary.rect.min_lat': 47.47, - 'boundary.rect.max_lon': -61.84, - 'boundary.rect.max_lat': 11.51, - 'boundary.rect.min_lon': -103.16, - layers: ['test'] + './text_parser': (parsed_text, vs) => { + vs.var('text_parser:value', 'this value populated by text_parser'); + } + })(clean); + + t.deepEquals(query.body.vs.var('text_parser:value').toString(), 'this value populated by text_parser'); + t.end(); + + }); + +}; + +module.exports.tests.query_size = (test, common) => { + test('size should be set when querySize is available', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + querySize: 17 }; - var query = generate(clean); + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); + } + })(clean); - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/structured_geocoding/linguistic_bbox'); + t.equals(query.body.vs.var('size').toString(), 17); - t.deepEqual(compiled.type, 'fallback', 'query type set'); - t.deepEqual(compiled.body, expected, 'search_linguistic_bbox'); t.end(); + }); - test('valid lingustic-only search', function(t) { - var clean = { - parsed_text: { - }, - text: 'test', querySize: 10, - layers: ['test'] +}; + +module.exports.tests.focus_point_lat_lon = (test, common) => { + test('missing focus.point.lat shouldn\'t set focus:point:lat/lon', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + 'focus.point.lon': 21.212121 }; - var query = generate(clean); + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); + } + })(clean); - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/structured_geocoding/linguistic_only'); + t.notOk(query.body.vs.isset('focus:point:lat')); + t.notOk(query.body.vs.isset('focus:point:lon')); - t.deepEqual(compiled.type, 'fallback', 'query type set'); - t.deepEqual(compiled.body, expected, 'search_linguistic_only'); t.end(); + }); - test('search search + focus', function(t) { - var clean = { - parsed_text: { - }, - text: 'test', querySize: 10, - 'focus.point.lat': 29.49136, 'focus.point.lon': -82.50622, - layers: ['test'] + test('missing focus.point.lon shouldn\'t set focus:point:lat/lon', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + 'focus.point.lat': 12.121212 }; - var query = generate(clean); + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); + } + })(clean); - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/structured_geocoding/linguistic_focus'); + t.notOk(query.body.vs.isset('focus:point:lat')); + t.notOk(query.body.vs.isset('focus:point:lon')); - t.deepEqual(compiled.type, 'fallback', 'query type set'); - t.deepEqual(compiled.body, expected, 'search_linguistic_focus'); t.end(); + }); - test('search search + viewport', function(t) { - var clean = { - parsed_text: { - }, - text: 'test', querySize: 10, - 'focus.viewport.min_lat': 28.49136, - 'focus.viewport.max_lat': 30.49136, - 'focus.viewport.min_lon': -87.50622, - 'focus.viewport.max_lon': -77.50622, - layers: ['test'] + test('focus.point.lat/lon should set focus:point:lat/lon', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + 'focus.point.lat': 12.121212, + 'focus.point.lon': 21.212121 }; - var query = generate(clean); + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); + } + })(clean); - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/structured_geocoding/linguistic_viewport'); + t.equals(query.body.vs.var('focus:point:lat').toString(), 12.121212); + t.equals(query.body.vs.var('focus:point:lon').toString(), 21.212121); - t.deepEqual(compiled.type, 'fallback', 'query type set'); - t.deepEqual(compiled.body, expected, 'search_linguistic_viewport'); t.end(); + }); - // viewport scale sizing currently disabled. - // ref: https://github.com/pelias/api/pull/388 - test('search with viewport diagonal < 1km should set scale to 1km', function(t) { - var clean = { - parsed_text: { - }, - text: 'test', querySize: 10, - 'focus.viewport.min_lat': 28.49135, - 'focus.viewport.max_lat': 28.49137, - 'focus.viewport.min_lon': -87.50622, - 'focus.viewport.max_lon': -87.50624, - layers: ['test'] +}; + +module.exports.tests.boundary_rect = (test, common) => { + test('missing boundary.rect.min_lat shouldn\'t set boundary:rect fields', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + 'boundary.rect.max_lat': 13.131313, + 'boundary.rect.min_lon': 21.212121, + 'boundary.rect.max_lon': 31.313131 }; - var query = generate(clean); + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); + } + })(clean); - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/structured_geocoding/linguistic_viewport_min_diagonal'); + t.notOk(query.body.vs.isset('boundary:rect:top')); + t.notOk(query.body.vs.isset('boundary:rect:right')); + t.notOk(query.body.vs.isset('boundary:rect:bottom')); + t.notOk(query.body.vs.isset('boundary:rect:left')); - t.deepEqual(compiled.type, 'fallback', 'query type set'); - t.deepEqual(compiled.body, expected, 'valid search query'); t.end(); + }); - test('search search + focus on null island', function(t) { - var clean = { - parsed_text: { + test('missing boundary.rect.max_lat shouldn\'t set boundary:rect fields', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + 'boundary.rect.min_lat': 12.121212, + 'boundary.rect.min_lon': 21.212121, + 'boundary.rect.max_lon': 31.313131 + }; + + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars }, - text: 'test', querySize: 10, - 'focus.point.lat': 0, 'focus.point.lon': 0, - layers: ['test'] + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); + } + })(clean); + + t.notOk(query.body.vs.isset('boundary:rect:top')); + t.notOk(query.body.vs.isset('boundary:rect:right')); + t.notOk(query.body.vs.isset('boundary:rect:bottom')); + t.notOk(query.body.vs.isset('boundary:rect:left')); + + t.end(); + + }); + + test('missing boundary.rect.min_lon shouldn\'t set boundary:rect fields', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + 'boundary.rect.min_lat': 12.121212, + 'boundary.rect.max_lat': 13.131313, + 'boundary.rect.max_lon': 31.313131 }; - var query = generate(clean); + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); + } + })(clean); - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/structured_geocoding/linguistic_focus_null_island'); + t.notOk(query.body.vs.isset('boundary:rect:top')); + t.notOk(query.body.vs.isset('boundary:rect:right')); + t.notOk(query.body.vs.isset('boundary:rect:bottom')); + t.notOk(query.body.vs.isset('boundary:rect:left')); - t.deepEqual(compiled.type, 'fallback', 'query type set'); - t.deepEqual(compiled.body, expected, 'search_linguistic_focus_null_island'); t.end(); + }); - test('parsed_text with all fields should use FallbackQuery', function(t) { - var clean = { - parsed_text: { - query: 'query value', - category: 'category value', - number: 'number value', - street: 'street value', - neighbourhood: 'neighbourhood value', - borough: 'borough value', - postalcode: 'postalcode value', - city: 'city value', - county: 'county value', - state: 'state value', - country: 'country value' + test('missing boundary.rect.max_lon shouldn\'t set boundary:rect fields', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + 'boundary.rect.min_lat': 12.121212, + 'boundary.rect.max_lat': 13.131313, + 'boundary.rect.min_lon': 21.212121 + }; + + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); } + })(clean); + + t.notOk(query.body.vs.isset('boundary:rect:top')); + t.notOk(query.body.vs.isset('boundary:rect:right')); + t.notOk(query.body.vs.isset('boundary:rect:bottom')); + t.notOk(query.body.vs.isset('boundary:rect:left')); + + t.end(); + + }); + + test('focus.point.lat/lon should set focus:point:lat/lon', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + 'boundary.rect.min_lat': 12.121212, + 'boundary.rect.max_lat': 13.131313, + 'boundary.rect.min_lon': 21.212121, + 'boundary.rect.max_lon': 31.313131 }; - var query = generate(clean); + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); + } + })(clean); - var compiled = JSON.parse(JSON.stringify(query)); - var expected = require('../fixture/structured_geocoding/fallback'); + t.equals(query.body.vs.var('boundary:rect:top').toString(), 13.131313); + t.equals(query.body.vs.var('boundary:rect:right').toString(), 31.313131); + t.equals(query.body.vs.var('boundary:rect:bottom').toString(), 12.121212); + t.equals(query.body.vs.var('boundary:rect:left').toString(), 21.212121); - t.deepEqual(compiled.type, 'fallback', 'query type set'); - t.deepEqual(compiled.body, expected, 'fallbackQuery'); t.end(); }); - test('parsed_text with all fields should use FallbackQuery', function(t) { - var clean = { - parsed_text: { - postalcode: 'postalcode value' +}; + +module.exports.tests.boundary_circle = (test, common) => { + test('missing boundary.circle.lat shouldn\'t set boundary:circle fields', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + 'boundary.circle.lon': 21.212121, + 'boundary.circle.radius': 17 + }; + + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); } + })(clean); + + t.notOk(query.body.vs.isset('boundary:circle:lat')); + t.notOk(query.body.vs.isset('boundary:circle:lon')); + t.notOk(query.body.vs.isset('boundary:circle:radius')); + + t.end(); + + }); + + test('missing boundary.circle.lon shouldn\'t set boundary:circle fields', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + 'boundary.circle.lat': 12.121212, + 'boundary.circle.radius': 17 }; - var query = generate(clean); + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); + } + })(clean); - var compiled = JSON.parse(JSON.stringify(query)); - var expected = require('../fixture/structured_geocoding/postalcode_only'); + t.notOk(query.body.vs.isset('boundary:circle:lat')); + t.notOk(query.body.vs.isset('boundary:circle:lon')); + t.notOk(query.body.vs.isset('boundary:circle:radius')); - t.deepEqual(compiled.type, 'fallback', 'query type set'); - t.deepEqual(compiled.body, expected, 'structured postalcode only'); t.end(); }); - test('valid boundary.country search', function(t) { - var clean = { - parsed_text: { + test('missing boundary.circle.radius should set lat/lon but not radius', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + 'boundary.circle.lat': 12.121212, + 'boundary.circle.lon': 21.212121 + }; + + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars }, - text: 'test', querySize: 10, - layers: ['test'], - 'boundary.country': 'ABC' + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); + } + })(clean); + + t.equals(query.body.vs.var('boundary:circle:lat').toString(), 12.121212); + t.equals(query.body.vs.var('boundary:circle:lon').toString(), 21.212121); + t.notOk(query.body.vs.isset('boundary:circle:radius')); + + t.end(); + + }); + + test('boundary.circle.* should set lat/lon/radius with last rounded and in kilometers', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + 'boundary.circle.lat': 12.121212, + 'boundary.circle.lon': 21.212121, + 'boundary.circle.radius': 17.5 }; - var query = generate(clean); + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); + } + })(clean); - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/structured_geocoding/boundary_country'); + t.equals(query.body.vs.var('boundary:circle:lat').toString(), 12.121212); + t.equals(query.body.vs.var('boundary:circle:lon').toString(), 21.212121); + t.equals(query.body.vs.var('boundary:circle:radius').toString(), '18km'); - t.deepEqual(compiled.type, 'fallback', 'query type set'); - t.deepEqual(compiled.body, expected, 'search: valid boundary.country query'); t.end(); + }); - test('valid sources filter', function(t) { - var clean = { - parsed_text: { + test('missing boundary.circle.lat/lon but existing boundary.circle.radius should not set any', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + 'boundary.circle.radius': 17 + }; + + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars }, - 'text': 'test', - 'sources': ['test_source'] + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); + } + })(clean); + + t.notOk(query.body.vs.isset('boundary:circle:lat')); + t.notOk(query.body.vs.isset('boundary:circle:lon')); + t.notOk(query.body.vs.isset('boundary:circle:radius')); + + t.end(); + + }); + +}; + +module.exports.tests.boundary_country = (test, common) => { + test('boundary.country available shoul set boundary:country', t => { + const clean = { + text: 'text value', + sources: 'sources value', + layers: 'layers value', + 'boundary.country': 'boundary country value' }; - var query = generate(clean); + const query = proxyquire('../../../query/structured_geocoding', { + 'pelias-query': { + layout: { + StructuredFallbackQuery: MockQuery + }, + view: views, + Vars: require('pelias-query').Vars + }, + './search_defaults': { }, + './text_parser': () => { + t.fail('text_parser should not have been called'); + } + })(clean); - var compiled = JSON.parse( JSON.stringify( query ) ); - var expected = require('../fixture/structured_geocoding/with_source_filtering'); + t.equals(query.body.vs.var('boundary:country').toString(), 'boundary country value'); - t.deepEqual(compiled.type, 'fallback', 'query type set'); - t.deepEqual(compiled.body, expected, 'search: valid search query with source filtering'); t.end(); + }); }; -module.exports.all = function (tape, common) { +module.exports.all = (tape, common) => { function test(name, testFunction) { - return tape('structured_geocoding query ' + name, testFunction); + return tape(`structured_geocoding query ${name}`, testFunction); } for( var testCase in module.exports.tests ){ diff --git a/test/unit/sanitizer/_geonames_deprecation.js b/test/unit/sanitizer/_geonames_deprecation.js index 41bd86c6..13461e15 100644 --- a/test/unit/sanitizer/_geonames_deprecation.js +++ b/test/unit/sanitizer/_geonames_deprecation.js @@ -2,6 +2,8 @@ const sanitizer = require('../../../sanitizer/_geonames_deprecation')(); module.exports.tests = {}; +const coarse_reverse_message ='coarse /reverse does not support geonames. See https://github.com/pelias/pelias/issues/675 for more info'; + module.exports.tests.no_warnings_or_errors_conditions = (test, common) => { test('undefined sources should add neither warnings nor errors', (t) => { const clean = {}; @@ -27,41 +29,71 @@ module.exports.tests.no_warnings_or_errors_conditions = (test, common) => { }); + test('geonames/gn in sources with layers=venue should add neither warnings nor errors', (t) => { + const clean = { + sources: ['geonames'], + layers: ['venue'] + }; + + const messages = sanitizer.sanitize(undefined, clean); + + t.deepEquals(clean.sources, ['geonames']); + t.deepEquals(messages, { errors: [], warnings: [] }); + t.end(); + }); }; module.exports.tests.error_conditions = (test, common) => { - test('only geonames in sources should not modify clean.sources and add error message', (t) => { + test('only geonames in sources with layers=coarse should not modify clean.sources and add error message', (t) => { ['gn', 'geonames'].forEach((sources) => { const clean = { - sources: [sources] + sources: [sources], + layers: ['coarse'] }; const messages = sanitizer.sanitize(undefined, clean); t.deepEquals(clean.sources, [sources]); - t.deepEquals(messages.errors, ['/reverse does not support geonames']); + t.deepEquals(messages.errors, [ coarse_reverse_message ]); t.deepEquals(messages.warnings, []); }); t.end(); - }); + test('only geonames in sources with layers=locality should not modify clean.sources and add error message', (t) => { + ['gn', 'geonames'].forEach((sources) => { + const clean = { + sources: [sources], + layers: ['locality'] + }; + + const messages = sanitizer.sanitize(undefined, clean); + + t.deepEquals(clean.sources, [sources]); + t.deepEquals(messages.errors, [ coarse_reverse_message ]); + t.deepEquals(messages.warnings, []); + + }); + + t.end(); + }); }; module.exports.tests.warning_conditions = (test, common) => { - test('only geonames in sources should not modify clean.sources and add error message', (t) => { + test('only geonames in sources and layers=coarse should not modify clean.sources and add error message', (t) => { ['gn', 'geonames'].forEach((sources) => { const clean = { - sources: ['another source', sources, 'yet another source'] + sources: ['another source', sources, 'yet another source'], + layers: ['coarse'] }; const messages = sanitizer.sanitize(undefined, clean); t.deepEquals(clean.sources, ['another source', 'yet another source']); t.deepEquals(messages.errors, []); - t.deepEquals(messages.warnings, ['/reverse does not support geonames']); + t.deepEquals(messages.warnings, [ coarse_reverse_message ]); });