diff --git a/.travis.yml b/.travis.yml index f2a4e06a..0abfae12 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,23 @@ +sudo: false language: node_js -script: "npm run unit" node_js: - - "0.10" - - "0.12" -sudo: false \ No newline at end of file + - 0.10 + - 0.12 + - 4.4 + - 5.8 +matrix: + allow_failures: + - node_js: 4.4 + - node_js: 5.8 +env: + global: + - CXX=g++-4.8 + matrix: + - TEST_SUITE=unit +script: "npm run $TEST_SUITE" +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.8 diff --git a/EXAMPLES.md b/EXAMPLES.md deleted file mode 100644 index 913afea3..00000000 --- a/EXAMPLES.md +++ /dev/null @@ -1,9 +0,0 @@ - -#### Search: - -- Nearest Museums: http://pelias.mapzen.com/search?lat=51.533&lon=-0.0652&input=museum&size=40 -- Nearest Hotels: http://pelias.mapzen.com/search?lat=51.533&lon=-0.0652&input=hotel&size=40 - -#### Autocomplete: - -- Local Neighborhoods: http://pelias.mapzen.com/suggest/nearby?lat=40.7259&lon=-73.9806&input=e&layers=neighborhood&size=40 \ No newline at end of file diff --git a/bin/generate-docs b/bin/generate-docs new file mode 100755 index 00000000..5b337e3e --- /dev/null +++ b/bin/generate-docs @@ -0,0 +1,9 @@ +#!/bin/bash -ex + +rm -r docs || true + +curl -s http://localhost:3100/v1 > /dev/null || die "Pelias server does not appear to be running \ +on http://localhost:3100, run npm start in another window before generating docs." + +cd test/ciao +node ../../node_modules/ciao/bin/ciao -c ../ciao.json . -d ../../docs diff --git a/controller/place.js b/controller/place.js index c4e78978..29acfe8b 100644 --- a/controller/place.js +++ b/controller/place.js @@ -1,4 +1,5 @@ var service = { mget: require('../service/mget') }; +var logger = require('pelias-logger').get('api:controller:place'); function setup( backend ){ @@ -13,40 +14,18 @@ function setup( backend ){ return next(); } - /* req.clean.ids contains an array of objects with id and types properties. - * types is an array of one or more types, since it can't always be known which single - * type a gid might belong to (osmnode and osmway both have source osm and layer venue). - * - * However, the mget Elasticsearch query only accepts a single type at a - * time. - * - * So, first create a new array that, has an entry - * with each type and id combination. This requires creating a new array with more entries - * than req.clean.ids in the case where entries have multiple types. - */ - - var recordsToReturn = req.clean.ids.reduce(function (acc, ids_element) { - ids_element.types.forEach(function(type) { - acc.push({ - id: ids_element.id, - type: type - }); - }); - return acc; - }, []); - - /* - * Next, map the list of records to an Elasticsearch mget query - */ - var query = recordsToReturn.map( function(id) { + var query = req.clean.ids.map( function(id) { return { _index: 'pelias', - _type: id.type, + _type: id.layers, _id: id.id }; }); + logger.debug( '[ES req]', JSON.stringify(query) ); + service.mget( backend, query, function( err, docs ) { + console.log('err:' + err); // error handler if( err ){ @@ -56,6 +35,7 @@ function setup( backend ){ else { res.data = docs; } + logger.debug('[ES response]', JSON.stringify(docs)); next(); }); diff --git a/controller/search.js b/controller/search.js index 309e7647..a564e7b4 100644 --- a/controller/search.js +++ b/controller/search.js @@ -8,7 +8,6 @@ function setup( backend, query ){ query = query || require('../query/search'); function controller( req, res, next ){ - // do not run controller when a request // validation error has occurred. if( req.errors && req.errors.length ){ @@ -25,11 +24,13 @@ function setup( backend, query ){ body: query( req.clean ) }; - // ? - if( req.clean.hasOwnProperty('type') ){ - cmd.type = req.clean.type; + // use layers field for filtering by type + if( req.clean.hasOwnProperty('layers') ){ + cmd.type = req.clean.layers; } + logger.debug( '[ES req]', JSON.stringify(cmd) ); + // query backend service.search( backend, cmd, function( err, docs, meta ){ @@ -42,7 +43,7 @@ function setup( backend, query ){ res.data = docs; res.meta = meta; } - + logger.debug('[ES response]', JSON.stringify(docs)); next(); }); diff --git a/helper/adminFields.js b/helper/adminFields.js deleted file mode 100644 index af6ac432..00000000 --- a/helper/adminFields.js +++ /dev/null @@ -1,45 +0,0 @@ - -var _ = require('lodash'), - peliasSchema = require('pelias-schema'), - peliasLogger = require( 'pelias-logger' ).get( 'api' ); - -var ADMIN_FIELDS = [ - 'admin0', - 'admin1', - 'admin1_abbr', - 'admin2', - 'local_admin', - 'locality', - 'neighborhood', - 'address.zip' -]; - -/** - * Get all admin fields that were expected and also found in schema - * - * @param {Object} [schema] optional: for testing only - * @param {Array} [expectedFields] optional: for testing only - * @param {Object} [logger] optional: for testing only - * @returns {Array.} - */ -function getAvailableAdminFields(schema, expectedFields, logger) { - - schema = schema || peliasSchema; - expectedFields = expectedFields || ADMIN_FIELDS; - logger = logger || peliasLogger; - - var actualFields = Object.keys(schema.mappings._default_.properties); - - // check if expected fields are actually in current schema - var available = expectedFields.filter(function (field) { - return _.contains( actualFields, field ); - }); - - if (available.length === 0) { - logger.error('helper/adminFields: no expected admin fields found in schema'); - } - - return available; -} - -module.exports = getAvailableAdminFields; diff --git a/helper/geojsonify.js b/helper/geojsonify.js index d01b6f36..a2dc3e2b 100644 --- a/helper/geojsonify.js +++ b/helper/geojsonify.js @@ -4,6 +4,7 @@ var GeoJSON = require('geojson'), labelGenerator = require('./labelGenerator'), logger = require('pelias-logger').get('api'), type_mapping = require('./type_mapping'), + Document = require('pelias-model').Document, _ = require('lodash'); // Properties to be copied @@ -11,48 +12,41 @@ var DETAILS_PROPS = [ 'housenumber', 'street', 'postalcode', - 'country_a', + 'confidence', + 'distance', 'country', + 'country_gid', + 'country_a', + 'macroregion', + 'macroregion_gid', + 'macroregion_a', 'region', + 'region_gid', 'region_a', + 'macrocounty', + 'macrocounty_gid', + 'macrocounty_a', 'county', + 'county_gid', + 'county_a', 'localadmin', + 'localadmin_gid', + 'localadmin_a', 'locality', + 'locality_gid', + 'locality_a', 'neighbourhood', - 'confidence', - 'distance' + 'neighbourhood_gid', + 'bounding_box' ]; function lookupSource(src) { - var sources = type_mapping.type_to_source; - return sources.hasOwnProperty(src._type) ? sources[src._type] : src._type; + return src.source; } -/* - * Use the type to layer mapping, except for Geonames, where having a full - * Elasticsearch document source allows a more specific layer name to be chosen - */ function lookupLayer(src) { - if (src._type === 'geoname') { - if (_.contains(src.category, 'admin')) { - if (_.contains(src.category, 'admin:city')) { return 'locality'; } - if (_.contains(src.category, 'admin:admin1')) { return 'region'; } - if (_.contains(src.category, 'admin:admin2')) { return 'county'; } - return 'neighbourhood'; // this could also be 'local_admin' - } - - if (src.name) { return 'venue'; } - if (src.address) { return 'address'; } - } - - if (_.contains(type_mapping.types, src._type)) { - return type_mapping.type_to_layer[src._type]; - } - - logger.warn('[geojsonify]: could not map _type ', src._type); - - return src._type; + return src.layer; } function geojsonifyPlaces( docs ){ @@ -64,11 +58,20 @@ function geojsonifyPlaces( docs ){ return !!doc; }); + // get all the bounding_box corners as well as single points + // to be used for computing the overall bounding_box for the FeatureCollection + var extentPoints = extractExtentPoints(geodata); + // convert to geojson - var geojson = GeoJSON.parse( geodata, { Point: ['lat', 'lng'] }); + var geojson = GeoJSON.parse( geodata, { Point: ['lat', 'lng'] }); + var geojsonExtentPoints = GeoJSON.parse( extentPoints, { Point: ['lat', 'lng'] }); + + // to insert the bbox property at the top level of each feature, it must be done separately after + // initial geojson construction is finished + addBBoxPerFeature(geojson); // bounding box calculations - computeBBox(geojson); + computeBBox(geojson, geojsonExtentPoints); return geojson; } @@ -116,7 +119,64 @@ function addDetails(src, dst) { * @param {object} dst */ function addLabel(src, dst) { - dst.label = labelGenerator(src); + dst.label = labelGenerator(dst); +} + +/** + * Add bounding box + * + * @param {object} geojson + */ +function addBBoxPerFeature(geojson) { + geojson.features.forEach(function (feature) { + + if (!feature.properties.hasOwnProperty('bounding_box')) { + return; + } + + if (feature.properties.bounding_box) { + feature.bbox = [ + feature.properties.bounding_box.min_lon, + feature.properties.bounding_box.min_lat, + feature.properties.bounding_box.max_lon, + feature.properties.bounding_box.max_lat + ]; + } + + delete feature.properties.bounding_box; + }); +} + +/** + * Collect all points from the geodata. + * If an item is a single point, just use that. + * If an item has a bounding box, add two corners of the box as individual points. + * + * @param {Array} geodata + * @returns {Array} + */ +function extractExtentPoints(geodata) { + var extentPoints = []; + geodata.forEach(function (place) { + if (place.bounding_box) { + extentPoints.push({ + lng: place.bounding_box.min_lon, + lat: place.bounding_box.min_lat + }); + extentPoints.push({ + lng: place.bounding_box.max_lon, + lat: place.bounding_box.max_lat + }); + } + else { + extentPoints.push({ + lng: place.lng, + lat: place.lat + }); + } + }); + + return extentPoints; } /** @@ -125,17 +185,17 @@ function addLabel(src, dst) { * * @param {object} geojson */ -function computeBBox(geojson) { +function computeBBox(geojson, geojsonExtentPoints) { // @note: extent() sometimes throws Errors for unusual data // eg: https://github.com/pelias/pelias/issues/84 try { - var bbox = extent( geojson ); + var bbox = extent( geojsonExtentPoints ); if( !!bbox ){ geojson.bbox = bbox; } } catch( e ){ console.error( 'bbox error', e.message, e.stack ); - console.error( 'geojson', JSON.stringify( geojson, null, 2 ) ); + console.error( 'geojson', JSON.stringify( geojsonExtentPoints, null, 2 ) ); } } @@ -149,8 +209,23 @@ function computeBBox(geojson) { */ function copyProperties( source, props, dst ) { props.forEach( function ( prop ) { + if ( source.hasOwnProperty( prop ) ) { - dst[prop] = source[prop]; + + // array value, take first item in array (at this time only used for admin values) + if (source[prop] instanceof Array) { + if (source[prop].length === 0) { + return; + } + if (source[prop][0]) { + dst[prop] = source[prop][0]; + } + } + + // simple value + else { + dst[prop] = source[prop]; + } } }); } @@ -162,7 +237,8 @@ function copyProperties( source, props, dst ) { * @param {object} src */ function makeGid(src) { - return lookupSource(src) + ':' + lookupLayer(src) + ':' + src._id; + var doc = new Document(lookupSource(src), lookupLayer(src), src._id); + return doc.getGid(); } /** @@ -176,6 +252,9 @@ function addMetaData(src, dst) { dst.gid = makeGid(src); dst.layer = lookupLayer(src); dst.source = lookupSource(src); + if (src.hasOwnProperty('bounding_box')) { + dst.bounding_box = src.bounding_box; + } } /** diff --git a/helper/labelGenerator.js b/helper/labelGenerator.js index 1938232d..0f0eb9dd 100644 --- a/helper/labelGenerator.js +++ b/helper/labelGenerator.js @@ -1,35 +1,74 @@ - var _ = require('lodash'), - check = require('check-types'), - schemas = require('./labelSchema.json'); + schemas = require('./labelSchema'); module.exports = function( record ){ + var schema = getSchema(record.country_a); + + var labelParts = getInitialLabel(record); - var labelParts = [ record.name.default ]; + for (var key in schema) { + var valueFunction = schema[key]; - var schema = schemas.default; - - if (record.country_a && record.country_a.length && schemas[record.country_a]) { - schema = schemas[record.country_a]; + labelParts = valueFunction(record, labelParts); } - - var buildOutput = function(parts, schemaArr, record) { - for (var i=0; i 'Grolmanstraße 101' function flipNumberAndStreet(place) { - var standard = ( place.address.number + ' ' + place.address.street ), - flipped = ( place.address.street + ' ' + place.address.number ); + var standard = ( place.address_parts.number + ' ' + place.address_parts.street ), + flipped = ( place.address_parts.street + ' ' + place.address_parts.number ); // flip street name and housenumber if( place.name.default === standard ){ diff --git a/middleware/normalizeParentIds.js b/middleware/normalizeParentIds.js new file mode 100644 index 00000000..e0e499e2 --- /dev/null +++ b/middleware/normalizeParentIds.js @@ -0,0 +1,56 @@ +var logger = require('pelias-logger').get('api'); +var Document = require('pelias-model').Document; + +var placeTypes = require('../helper/placeTypes'); + +/** + * Convert WOF integer ids to Pelias formatted ids that can be used by the /place endpoint. + * This should probably be moved to the import pipeline once we are happy with the way this works. + */ + +function setup() { + return function (req, res, next) { + // do nothing if no result data set + if (!res || !res.data) { + return next(); + } + + res.data = res.data.map(normalizeParentIds); + + next(); + }; +} + +/** + * Update all parent ids in the admin hierarchy + * + * @param {object} place + * @return {object} + */ +function normalizeParentIds(place) { + + if (place) { + placeTypes.forEach(function (placeType) { + if (place[placeType] && place[placeType].length > 0 && place[placeType][0]) { + place[placeType + '_gid'] = [ makeNewId(placeType, place[placeType + '_gid']) ]; + } + }); + } + + return place; +} + +/** + * Generate a valid Pelias ids from placetype and WOF id. + * Assumes all of the incoming ids are WOF ids. + * + * @param {string} placeType + * @param {number} id + * @return {string} + */ +function makeNewId(placeType, id) { + var doc = new Document('whosonfirst', placeType, id); + return doc.getGid(); +} + +module.exports = setup; diff --git a/middleware/parseBBox.js b/middleware/parseBBox.js new file mode 100644 index 00000000..990b3498 --- /dev/null +++ b/middleware/parseBBox.js @@ -0,0 +1,38 @@ +var logger = require('pelias-logger').get('api'); + +/** + * Parses the bounding box property in docs, if one is found + */ + +function setup() { + return function (req, res, next) { + // do nothing if no result data set + if (!res || !res.data) { + return next(); + } + + res.data = res.data.map(parseBBox); + + next(); + }; +} + +/* + * Parse the bbox property and form an object + */ +function parseBBox(place) { + + if (place && place.bounding_box) { + try { + place.bounding_box = JSON.parse(place.bounding_box); + } + catch (err) { + logger.error('Invalid bounding_box json string:', place); + delete place.bounding_box; + } + } + + return place; +} + +module.exports = setup; diff --git a/middleware/renamePlacenames.js b/middleware/renamePlacenames.js index 6fcba91a..d89a43ad 100644 --- a/middleware/renamePlacenames.js +++ b/middleware/renamePlacenames.js @@ -1,66 +1,49 @@ -var extend = require('extend'); +var _ = require('lodash'); -/** - - P is a preferred English name - - Q is a preferred name (in other languages) - - V is a well-known (but unofficial) variant for the place - (e.g. "New York City" for New York) - - S is either a synonym or a colloquial name for the place - (e.g. "Big Apple" for New York), or a version of the name which - is stripped of accent characters. - - A is an abbreviation or code for the place (e.g. "NYC" for New - York) - */ -// config mapping of old names to new ones -var NAME_MAP = { +var PARENT_PROPS = require('../helper/placeTypes'); + +var ADDRESS_PROPS = { 'number': 'housenumber', 'zip': 'postalcode', - 'alpha3': 'country_a', - 'admin0': 'country', - 'admin1': 'region', - 'admin1_abbr': 'region_a', - 'admin2': 'county', - 'local_admin': 'localadmin', - 'neighborhood': 'neighbourhood' + 'street': 'street' }; -function setup() { +function setup() { return renamePlacenames; } function renamePlacenames(req, res, next) { - // do nothing if no result data set if (!res || !res.data) { return next(); } - // loop through data items and remap placenames - res.data = res.data.map(renameProperties); + res.data = res.data.map(renameOneRecord); next(); } -function renameProperties(place) { - var newPlace = {}; - Object.keys(place).forEach(function (property) { - if (property === 'address') { - extend(newPlace, renameProperties(place[property])); - } - else { - renameProperty(place, newPlace, property); - } - }); - return newPlace; -} +/* + * Rename the fields in one record + */ +function renameOneRecord(place) { + if (place.address_parts) { + Object.keys(ADDRESS_PROPS).forEach(function (prop) { + place[ADDRESS_PROPS[prop]] = place.address_parts[prop]; + }); + } -function renameProperty(oldObj, newObj, property) { - if (!oldObj.hasOwnProperty(property)) { - return; + // merge the parent block into the top level object to flatten the structure + if (place.parent) { + PARENT_PROPS.forEach(function (prop) { + place[prop] = place.parent[prop]; + place[prop + '_a'] = place.parent[prop + '_a']; + place[prop + '_gid'] = place.parent[prop + '_id']; + }); } - newObj[(NAME_MAP[property] || property)] = oldObj[property]; + return place; } module.exports = setup; diff --git a/package.json b/package.json index eb9a4b10..f97df8eb 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,9 @@ "ciao": "node node_modules/ciao/bin/ciao -c test/ciao.json test/ciao", "coverage": "node_modules/.bin/istanbul cover test/unit/run.js", "audit": "npm shrinkwrap; node node_modules/nsp/bin/nspCLI.js audit-shrinkwrap; rm npm-shrinkwrap.json;", - "docs": "rm -r docs; cd test/ciao; node ../../node_modules/ciao/bin/ciao -c ../ciao.json . -d ../../docs" + "docs": "./bin/generate-docs", + "lint": "jshint .", + "validate": "npm ls" }, "repository": { "type": "git", @@ -34,37 +36,43 @@ }, "dependencies": { "addressit": "git://github.com/dianashk/addressit.git#temp", - "async": "^0.9.0", - "check-types": "^3.3.1", + "async": "^1.5.2", + "check-types": "^6.0.0", "cluster2": "git://github.com/missinglink/cluster2.git#node_zero_twelve", + "elasticsearch": "^10.1.3", "express": "^4.8.8", "express-http-proxy": "^0.6.0", - "extend": "2.0.1", - "geojson": "^0.2.1", + "extend": "3.0.0", + "geojson": "^0.3.0", "geojson-extent": "^0.3.1", "geolib": "^2.0.18", "geopipes-elasticsearch-backend": "^0.2.0", "iso3166-1": "^0.2.3", - "lodash": "^3.10.1", + "lodash": "^4.5.0", "markdown": "0.5.0", - "morgan": "1.5.2", + "morgan": "1.7.0", "pelias-config": "^1.0.1", - "pelias-esclient": "0.0.25", "pelias-logger": "^0.0.8", - "pelias-query": "^5.0.0", - "pelias-schema": "1.0.0", - "pelias-suggester-pipeline": "2.0.2", - "stats-lite": "^1.0.3", - "through2": "0.6.5" + "pelias-model": "^3.1.0", + "pelias-query": "6.2.0", + "pelias-suggester-pipeline": "2.0.4", + "stats-lite": "1.0.3", + "through2": "2.0.1" }, "devDependencies": { "ciao": "^0.6.0", - "istanbul": "^0.3.13", + "difflet": "^1.0.1", + "istanbul": "^0.4.2", "jshint": "^2.5.6", - "nsp": "^0.3.0", - "precommit-hook": "^1.0.7", + "nsp": "^2.2.0", + "precommit-hook": "^3.0.0", "proxyquire": "^1.4.0", - "tap-dot": "^1.0.0", - "tape": "^2.13.4" - } + "tap-dot": "1.0.5", + "tape": "^4.4.0" + }, + "pre-commit": [ + "lint", + "validate", + "test" + ] } diff --git a/public/attribution.md b/public/attribution.md index 2302c093..0eedb365 100644 --- a/public/attribution.md +++ b/public/attribution.md @@ -3,5 +3,5 @@ * Data from * [OpenStreetMap](http://www.openstreetmap.org/copyright) © OpenStreetMap contributors under [ODbL](http://opendatacommons.org/licenses/odbl/) * [OpenAddresses](http://openaddresses.io) under a [Creative Commons Zero](https://github.com/openaddresses/openaddresses/blob/master/sources/LICENSE) public domain designation - * [Quattroshapes](https://github.com/foursquare/quattroshapes/blob/master/LICENSE.md) under [CC-BY-2.0](https://creativecommons.org/licenses/by/2.0/) * [GeoNames](http://www.geonames.org/) under [CC-BY-3.0](https://creativecommons.org/licenses/by/2.0/) + * [WhosOnFirst](http://whosonfirst.mapzen.com) under [various licenses](https://github.com/whosonfirst/whosonfirst-data/blob/master/LICENSE.md) diff --git a/query/autocomplete.js b/query/autocomplete.js index bf5f76aa..4bc98c7c 100644 --- a/query/autocomplete.js +++ b/query/autocomplete.js @@ -27,14 +27,14 @@ query.score( peliasQuery.view.address('street') ); query.score( peliasQuery.view.address('postcode') ); // admin components -query.score( peliasQuery.view.admin('alpha3') ); -query.score( peliasQuery.view.admin('admin0') ); -query.score( peliasQuery.view.admin('admin1') ); -query.score( peliasQuery.view.admin('admin1_abbr') ); -query.score( peliasQuery.view.admin('admin2') ); -query.score( peliasQuery.view.admin('local_admin') ); +query.score( peliasQuery.view.admin('country') ); +query.score( peliasQuery.view.admin('country_a') ); +query.score( peliasQuery.view.admin('region') ); +query.score( peliasQuery.view.admin('region_a') ); +query.score( peliasQuery.view.admin('county') ); +query.score( peliasQuery.view.admin('localadmin') ); query.score( peliasQuery.view.admin('locality') ); -query.score( peliasQuery.view.admin('neighborhood') ); +query.score( peliasQuery.view.admin('neighbourhood') ); // scoring boost query.score( views.focus_selected_layers( views.ngrams_strict ) ); diff --git a/query/autocomplete_defaults.js b/query/autocomplete_defaults.js index 634f5165..ba52a049 100644 --- a/query/autocomplete_defaults.js +++ b/query/autocomplete_defaults.js @@ -30,57 +30,57 @@ module.exports = _.merge({}, peliasQuery.defaults, { 'phrase:slop': 2, 'focus:function': 'linear', - 'focus:offset': '10km', + 'focus:offset': '0km', 'focus:scale': '250km', 'focus:decay': 0.5, - 'focus:weight': 3, + 'focus:weight': 10, 'function_score:score_mode': 'avg', 'function_score:boost_mode': 'multiply', 'address:housenumber:analyzer': 'peliasHousenumber', - 'address:housenumber:field': 'address.number', + 'address:housenumber:field': 'address_parts.number', 'address:housenumber:boost': 2, 'address:street:analyzer': 'peliasStreet', - 'address:street:field': 'address.street', + 'address:street:field': 'address_parts.street', 'address:street:boost': 5, 'address:postcode:analyzer': 'peliasZip', - 'address:postcode:field': 'address.zip', + 'address:postcode:field': 'address_parts.zip', 'address:postcode:boost': 2000, - 'admin:alpha3:analyzer': 'standard', - 'admin:alpha3:field': 'alpha3', - 'admin:alpha3:boost': 1000, + 'admin:country_a:analyzer': 'standard', + 'admin:country_a:field': 'parent.country_a', + 'admin:country_a:boost': 1000, - 'admin:admin0:analyzer': 'peliasAdmin', - 'admin:admin0:field': 'admin0', - 'admin:admin0:boost': 800, + 'admin:country:analyzer': 'peliasAdmin', + 'admin:country:field': 'parent.country', + 'admin:country:boost': 800, - 'admin:admin1:analyzer': 'peliasAdmin', - 'admin:admin1:field': 'admin1', - 'admin:admin1:boost': 600, + 'admin:region:analyzer': 'peliasAdmin', + 'admin:region:field': 'parent.region', + 'admin:region:boost': 600, - 'admin:admin1_abbr:analyzer': 'peliasAdmin', - 'admin:admin1_abbr:field': 'admin1_abbr', - 'admin:admin1_abbr:boost': 600, + 'admin:region_a:analyzer': 'peliasAdmin', + 'admin:region_a:field': 'parent.region_a', + 'admin:region_a:boost': 600, - 'admin:admin2:analyzer': 'peliasAdmin', - 'admin:admin2:field': 'admin2', - 'admin:admin2:boost': 400, + 'admin:county:analyzer': 'peliasAdmin', + 'admin:county:field': 'parent.county', + 'admin:county:boost': 400, - 'admin:local_admin:analyzer': 'peliasAdmin', - 'admin:local_admin:field': 'local_admin', - 'admin:local_admin:boost': 200, + 'admin:localadmin:analyzer': 'peliasAdmin', + 'admin:localadmin:field': 'parent.localadmin', + 'admin:localadmin:boost': 200, 'admin:locality:analyzer': 'peliasAdmin', - 'admin:locality:field': 'locality', + 'admin:locality:field': 'parent.locality', 'admin:locality:boost': 200, - 'admin:neighborhood:analyzer': 'peliasAdmin', - 'admin:neighborhood:field': 'neighborhood', - 'admin:neighborhood:boost': 200, + 'admin:neighbourhood:analyzer': 'peliasAdmin', + 'admin:neighbourhood:field': 'parent.neighbourhood', + 'admin:neighbourhood:boost': 200, 'popularity:field': 'popularity', 'popularity:modifier': 'log1p', @@ -90,6 +90,6 @@ module.exports = _.merge({}, peliasQuery.defaults, { 'population:field': 'population', 'population:modifier': 'log1p', 'population:max_boost': 20, - 'population:weight': 2 + 'population:weight': 3 }); diff --git a/query/reverse.js b/query/reverse.js index 856b9540..13e9263c 100644 --- a/query/reverse.js +++ b/query/reverse.js @@ -15,6 +15,7 @@ query.sort( peliasQuery.view.sort_distance ); // non-scoring hard filters query.filter( peliasQuery.view.boundary_circle ); +query.filter( peliasQuery.view.sources ); // -------------------------------- @@ -27,6 +28,9 @@ function generateQuery( clean ){ vs.var( 'size', clean.querySize); } + // sources + vs.var( 'sources', clean.sources); + // focus point to score by distance if( check.number(clean['point.lat']) && check.number(clean['point.lon']) ){ diff --git a/query/reverse_defaults.js b/query/reverse_defaults.js index 01e74773..306efaac 100644 --- a/query/reverse_defaults.js +++ b/query/reverse_defaults.js @@ -39,48 +39,48 @@ module.exports = _.merge({}, peliasQuery.defaults, { 'function_score:boost_mode': 'replace', 'address:housenumber:analyzer': 'peliasHousenumber', - 'address:housenumber:field': 'address.number', + 'address:housenumber:field': 'address_parts.number', 'address:housenumber:boost': 2, 'address:street:analyzer': 'peliasStreet', - 'address:street:field': 'address.street', + 'address:street:field': 'address_parts.street', 'address:street:boost': 5, 'address:postcode:analyzer': 'peliasZip', - 'address:postcode:field': 'address.zip', + 'address:postcode:field': 'address_parts.zip', 'address:postcode:boost': 3, - 'admin:alpha3:analyzer': 'standard', - 'admin:alpha3:field': 'alpha3', - 'admin:alpha3:boost': 5, + 'admin:country_a:analyzer': 'standard', + 'admin:country_a:field': 'parent.country_a', + 'admin:country_a:boost': 5, - 'admin:admin0:analyzer': 'peliasAdmin', - 'admin:admin0:field': 'admin0', - 'admin:admin0:boost': 4, + 'admin:country:analyzer': 'peliasAdmin', + 'admin:country:field': 'parent.country', + 'admin:country:boost': 4, - 'admin:admin1:analyzer': 'peliasAdmin', - 'admin:admin1:field': 'admin1', - 'admin:admin1:boost': 3, + 'admin:region:analyzer': 'peliasAdmin', + 'admin:region:field': 'parent.region', + 'admin:region:boost': 3, - 'admin:admin1_abbr:analyzer': 'peliasAdmin', - 'admin:admin1_abbr:field': 'admin1_abbr', - 'admin:admin1_abbr:boost': 3, + 'admin:region_a:analyzer': 'peliasAdmin', + 'admin:region_a:field': 'parent.region_a', + 'admin:region_a:boost': 3, - 'admin:admin2:analyzer': 'peliasAdmin', - 'admin:admin2:field': 'admin2', - 'admin:admin2:boost': 2, + 'admin:county:analyzer': 'peliasAdmin', + 'admin:county:field': 'parent.county', + 'admin:county:boost': 2, - 'admin:local_admin:analyzer': 'peliasAdmin', - 'admin:local_admin:field': 'local_admin', - 'admin:local_admin:boost': 1, + 'admin:localadmin:analyzer': 'peliasAdmin', + 'admin:localadmin:field': 'parent.localadmin', + 'admin:localadmin:boost': 1, 'admin:locality:analyzer': 'peliasAdmin', - 'admin:locality:field': 'locality', + 'admin:locality:field': 'parent.locality', 'admin:locality:boost': 1, - 'admin:neighborhood:analyzer': 'peliasAdmin', - 'admin:neighborhood:field': 'neighborhood', - 'admin:neighborhood:boost': 1, + 'admin:neighbourhood:analyzer': 'peliasAdmin', + 'admin:neighbourhood:field': 'parent.neighbourhood', + 'admin:neighbourhood:boost': 1, 'popularity:field': 'popularity', 'popularity:modifier': 'log1p', diff --git a/query/search.js b/query/search.js index 764cf999..9f0a792c 100644 --- a/query/search.js +++ b/query/search.js @@ -25,19 +25,19 @@ query.score( peliasQuery.view.address('street') ); query.score( peliasQuery.view.address('postcode') ); // admin components -query.score( peliasQuery.view.admin('alpha3') ); -query.score( peliasQuery.view.admin('admin0') ); -query.score( peliasQuery.view.admin('admin1') ); -query.score( peliasQuery.view.admin('admin1_abbr') ); -query.score( peliasQuery.view.admin('admin2') ); -query.score( peliasQuery.view.admin('local_admin') ); +query.score( peliasQuery.view.admin('country') ); +query.score( peliasQuery.view.admin('country_a') ); +query.score( peliasQuery.view.admin('region') ); +query.score( peliasQuery.view.admin('region_a') ); +query.score( peliasQuery.view.admin('county') ); +query.score( peliasQuery.view.admin('localadmin') ); query.score( peliasQuery.view.admin('locality') ); -query.score( peliasQuery.view.admin('neighborhood') ); +query.score( peliasQuery.view.admin('neighbourhood') ); // non-scoring hard filters query.filter( peliasQuery.view.boundary_circle ); query.filter( peliasQuery.view.boundary_rect ); - +query.filter( peliasQuery.view.sources ); // -------------------------------- /** @@ -51,6 +51,9 @@ function generateQuery( clean ){ // input text vs.var( 'input:name', clean.text ); + // sources + vs.var( 'sources', clean.sources); + // size if( clean.querySize ) { vs.var( 'size', clean.querySize ); diff --git a/query/search_defaults.js b/query/search_defaults.js index 28cf6e7a..ea0dc87f 100644 --- a/query/search_defaults.js +++ b/query/search_defaults.js @@ -39,48 +39,48 @@ module.exports = _.merge({}, peliasQuery.defaults, { 'function_score:boost_mode': 'replace', 'address:housenumber:analyzer': 'peliasHousenumber', - 'address:housenumber:field': 'address.number', + 'address:housenumber:field': 'address_parts.number', 'address:housenumber:boost': 2, 'address:street:analyzer': 'peliasStreet', - 'address:street:field': 'address.street', + 'address:street:field': 'address_parts.street', 'address:street:boost': 5, 'address:postcode:analyzer': 'peliasZip', - 'address:postcode:field': 'address.zip', + 'address:postcode:field': 'address_parts.zip', 'address:postcode:boost': 20, - 'admin:alpha3:analyzer': 'standard', - 'admin:alpha3:field': 'alpha3', - 'admin:alpha3:boost': 5, + 'admin:country_a:analyzer': 'standard', + 'admin:country_a:field': 'parent.country_a', + 'admin:country_a:boost': 5, - 'admin:admin0:analyzer': 'peliasAdmin', - 'admin:admin0:field': 'admin0', - 'admin:admin0:boost': 4, + 'admin:country:analyzer': 'peliasAdmin', + 'admin:country:field': 'parent.country', + 'admin:country:boost': 4, - 'admin:admin1:analyzer': 'peliasAdmin', - 'admin:admin1:field': 'admin1', - 'admin:admin1:boost': 3, + 'admin:region:analyzer': 'peliasAdmin', + 'admin:region:field': 'parent.region', + 'admin:region:boost': 3, - 'admin:admin1_abbr:analyzer': 'peliasAdmin', - 'admin:admin1_abbr:field': 'admin1_abbr', - 'admin:admin1_abbr:boost': 3, + 'admin:region_a:analyzer': 'peliasAdmin', + 'admin:region_a:field': 'parent.region_a', + 'admin:region_a:boost': 3, - 'admin:admin2:analyzer': 'peliasAdmin', - 'admin:admin2:field': 'admin2', - 'admin:admin2:boost': 2, + 'admin:county:analyzer': 'peliasAdmin', + 'admin:county:field': 'parent.county', + 'admin:county:boost': 2, - 'admin:local_admin:analyzer': 'peliasAdmin', - 'admin:local_admin:field': 'local_admin', - 'admin:local_admin:boost': 1, + 'admin:localadmin:analyzer': 'peliasAdmin', + 'admin:localadmin:field': 'parent.localadmin', + 'admin:localadmin:boost': 1, 'admin:locality:analyzer': 'peliasAdmin', - 'admin:locality:field': 'locality', + 'admin:locality:field': 'parent.locality', 'admin:locality:boost': 1, - 'admin:neighborhood:analyzer': 'peliasAdmin', - 'admin:neighborhood:field': 'neighborhood', - 'admin:neighborhood:boost': 1, + 'admin:neighbourhood:analyzer': 'peliasAdmin', + 'admin:neighbourhood:field': 'parent.neighbourhood', + 'admin:neighbourhood:boost': 1, 'popularity:field': 'popularity', 'popularity:modifier': 'log1p', diff --git a/query/text_parser.js b/query/text_parser.js index 408d00f9..38fca48e 100644 --- a/query/text_parser.js +++ b/query/text_parser.js @@ -1,6 +1,20 @@ var logger = require('pelias-logger').get('api'); -var adminFields = require('../helper/adminFields')(); + +/* +This list should only contain admin fields we are comfortable matching in the case +when we can't identify parts of an address. This shouldn't contain fields like country_a +or postalcode because we should only try to match those when we're sure that's what they are. + */ +var adminFields = [ + 'country', + 'region', + 'region_a', + 'county', + 'localadmin', + 'locality', + 'neighbourhood' +]; /** @todo: refactor me @@ -48,17 +62,17 @@ function addParsedVariablesToQueryVariables( parsed_text, vs ){ // city if( parsed_text.hasOwnProperty('city') ){ - vs.var( 'input:admin2', parsed_text.city ); + vs.var( 'input:county', parsed_text.city ); } // state if( parsed_text.hasOwnProperty('state') ){ - vs.var( 'input:admin1_abbr', parsed_text.state ); + vs.var( 'input:region_a', parsed_text.state ); } // country if( parsed_text.hasOwnProperty('country') ){ - vs.var( 'input:alpha3', parsed_text.country ); + vs.var( 'input:country_a', parsed_text.country ); } // ==== deal with the 'leftover' components ==== @@ -76,11 +90,10 @@ function addParsedVariablesToQueryVariables( parsed_text, vs ){ // if we have 'leftovers' then assign them to any fields which // currently don't have a value assigned. if( leftoversString.length ){ - var unmatchedAdminFields = adminFields.slice(); - + // cycle through fields and set fields which // are still currently unset - unmatchedAdminFields.forEach( function( key ){ + adminFields.forEach( function( key ){ if( !vs.isset( 'input:' + key ) ){ vs.var( 'input:' + key, leftoversString ); } diff --git a/query/view/focus_selected_layers.js b/query/view/focus_selected_layers.js index 038d7ffa..ba19919f 100644 --- a/query/view/focus_selected_layers.js +++ b/query/view/focus_selected_layers.js @@ -22,10 +22,8 @@ module.exports = function( subview ){ if( view && view.hasOwnProperty('function_score') ){ view.function_score.filter = { 'or': [ - { 'type': { 'value': 'osmnode' } }, - { 'type': { 'value': 'osmway' } }, - { 'type': { 'value': 'osmaddress' } }, - { 'type': { 'value': 'openaddresses' } } + { 'term': { 'layer': 'venue' } }, + { 'term': { 'layer': 'address' } } ] }; } diff --git a/routes/v1.js b/routes/v1.js index 49c9ea4c..c8c88de1 100644 --- a/routes/v1.js +++ b/routes/v1.js @@ -12,7 +12,6 @@ var sanitisers = { /** ----------------------- middleware ------------------------ **/ var middleware = { - types: require('../middleware/_types'), calcSize: require('../middleware/sizeCalculator') }; @@ -35,7 +34,9 @@ var postProc = { localNamingConventions: require('../middleware/localNamingConventions'), renamePlacenames: require('../middleware/renamePlacenames'), geocodeJSON: require('../middleware/geocodeJSON'), - sendJSON: require('../middleware/sendJSON') + sendJSON: require('../middleware/sendJSON'), + parseBoundingBox: require('../middleware/parseBBox'), + normalizeParentIds: require('../middleware/normalizeParentIds') }; /** @@ -59,7 +60,6 @@ function addRoutes(app, peliasConfig) { ]), search: createRouter([ sanitisers.search.middleware, - middleware.types, middleware.calcSize(), controllers.search(), postProc.distances('focus.point.'), @@ -67,24 +67,26 @@ function addRoutes(app, peliasConfig) { postProc.dedupe(), postProc.localNamingConventions(), postProc.renamePlacenames(), + postProc.parseBoundingBox(), + postProc.normalizeParentIds(), postProc.geocodeJSON(peliasConfig, base), postProc.sendJSON ]), autocomplete: createRouter([ sanitisers.autocomplete.middleware, - middleware.types, controllers.search(null, require('../query/autocomplete')), postProc.distances('focus.point.'), postProc.confidenceScores(peliasConfig), postProc.dedupe(), postProc.localNamingConventions(), postProc.renamePlacenames(), + postProc.parseBoundingBox(), + postProc.normalizeParentIds(), postProc.geocodeJSON(peliasConfig, base), postProc.sendJSON ]), reverse: createRouter([ sanitisers.reverse.middleware, - middleware.types, middleware.calcSize(), controllers.search(undefined, reverseQuery), postProc.distances('point.'), @@ -94,6 +96,8 @@ function addRoutes(app, peliasConfig) { postProc.dedupe(), postProc.localNamingConventions(), postProc.renamePlacenames(), + postProc.parseBoundingBox(), + postProc.normalizeParentIds(), postProc.geocodeJSON(peliasConfig, base), postProc.sendJSON ]), @@ -102,6 +106,8 @@ function addRoutes(app, peliasConfig) { controllers.place(), postProc.localNamingConventions(), postProc.renamePlacenames(), + postProc.parseBoundingBox(), + postProc.normalizeParentIds(), postProc.geocodeJSON(peliasConfig, base), postProc.sendJSON ]), diff --git a/sanitiser/_boundary_country.js b/sanitiser/_boundary_country.js index ef965c2c..9bdceac2 100644 --- a/sanitiser/_boundary_country.js +++ b/sanitiser/_boundary_country.js @@ -13,7 +13,7 @@ function sanitize(raw, clean) { if (check.assigned(country)){ // must be valid string - if (!check.unemptyString(country)) { + if (!check.nonEmptyString(country)) { messages.errors.push('boundary.country is not a string'); } diff --git a/sanitiser/_categories.js b/sanitiser/_categories.js index e76e8767..29737922 100644 --- a/sanitiser/_categories.js +++ b/sanitiser/_categories.js @@ -11,7 +11,7 @@ function sanitize( raw, clean ){ clean.categories = []; // if categories string has been set - if( check.unemptyString( raw.categories ) ){ + if( check.nonEmptyString( raw.categories ) ){ // map input categories to valid format clean.categories = raw.categories.split(',') diff --git a/sanitiser/_deprecate_quattroshapes.js b/sanitiser/_deprecate_quattroshapes.js new file mode 100644 index 00000000..bd45c025 --- /dev/null +++ b/sanitiser/_deprecate_quattroshapes.js @@ -0,0 +1,41 @@ +var _ = require('lodash'); + +/** + In the process of phasing out the 'quattroshapes' source in favour of 'whosonfirst' + we will emit a warning to users so they can begin upgrading their clients. + + In the interim we will automatically rewrite all requests for quattroshapes to whosonfirst. + + @todo: this is only temporary + @see: https://github.com/pelias/api/issues/442 +**/ + +function sanitize( raw, clean, opts ) { + // error & warning messages + var messages = { errors: [], warnings: [] }; + + // only applicably when 'sources' param is privided + if( raw.hasOwnProperty('sources') ){ + + var sources = raw.sources.split(','); + if (_.includes(sources, 'quattroshapes') || _.includes(sources, 'qs')) { + + // emit a warning message so users can transition. + messages.warnings.push('You are using Quattroshapes as a data source in this query. ' + + 'Quattroshapes has been disabled as a data source for Mapzen Search, and has been' + + 'replaced by Who\'s on First, an actively maintained data project based on Quattroshapes' + + 'Your existing queries WILL CONTINUE TO WORK for the foreseeable future, but results will ' + + 'be coming from Who\'s on First and `sources=quattroshapes` will be interpreted as ' + + '`sources=whosonfirst`. If you have any questions, please email search@mapzen.com.'); + + // user requested 'quattroshapes', we will give them 'whosonfirst' instead. + sources = _.without(sources, 'quattroshapes', 'qs'); + sources.push('whosonfirst'); + raw.sources = sources.join(','); + } + } + + return messages; +} + +module.exports = sanitize; diff --git a/sanitiser/_details.js b/sanitiser/_details.js index f0e2bade..c67f7c47 100644 --- a/sanitiser/_details.js +++ b/sanitiser/_details.js @@ -22,7 +22,7 @@ function sanitize( raw, clean ){ // be lenient with 'truthy' values function isTruthy(val) { if( check.string( val ) ){ - return _.contains( ['true', '1'], val ); + return _.includes( ['true', '1'], val ); } return val === 1 || val === true; diff --git a/sanitiser/_flag_bool.js b/sanitiser/_flag_bool.js index 68e9ce1c..4ed5c339 100644 --- a/sanitiser/_flag_bool.js +++ b/sanitiser/_flag_bool.js @@ -44,7 +44,7 @@ function sanitize( raw, clean, opts ){ * @returns {boolean} */ function isTruthy(val) { - return _.contains( ['true', '1', 1, true], val ); + return _.includes( ['true', '1', 1, true], val ); } module.exports = setup; diff --git a/sanitiser/_groups.js b/sanitiser/_groups.js index a06b839c..fc1d3f30 100644 --- a/sanitiser/_groups.js +++ b/sanitiser/_groups.js @@ -10,7 +10,7 @@ var _ = require('lodash'); * returns true if all are present, false if none are present, throws an exception otherwise */ function optional_group(object, keys) { - var contained_in_object = _.contains.bind(null, Object.keys(object)); + var contained_in_object = _.includes.bind(null, Object.keys(object)); if (keys.every(contained_in_object)) { return true; @@ -26,7 +26,7 @@ function optional_group(object, keys) { * An error will be thrown if any of the keys are missing from the object */ function required_group(object, keys) { - var contained_in_object = _.contains.bind(null, Object.keys(object)); + var contained_in_object = _.includes.bind(null, Object.keys(object)); if (keys.every(contained_in_object)) { return true; diff --git a/sanitiser/_ids.js b/sanitiser/_ids.js index 47c95ad6..4a1844be 100644 --- a/sanitiser/_ids.js +++ b/sanitiser/_ids.js @@ -30,32 +30,25 @@ function sanitizeId(rawId, messages) { var id = parts.slice(2).join(ID_DELIM); // check if any parts of the gid are empty - if (_.contains([source, layer, id], '')) { + if (_.includes([source, layer, id], '')) { messages.errors.push( formatError(rawId) ); return; } - if (!_.contains(type_mapping.sources, source)) { + if (!_.includes(type_mapping.sources, source)) { messages.errors.push( targetError(source, type_mapping.sources) ); return; } - if (!_.contains(type_mapping.layers, layer)) { + if (!_.includes(type_mapping.layers, layer)) { messages.errors.push( targetError(layer, type_mapping.layers) ); return; } - //TODO: remove this once we have a better set of layers for Geonames - var types; - if (source === 'gn' || source === 'geonames') { - types = ['geoname']; - } else { - types = type_mapping.source_and_layer_to_type(source, layer); - } - return { + source: source, + layer: layer, id: id, - types: types }; } @@ -63,7 +56,7 @@ function sanitize( raw, clean ){ // error & warning messages var messages = { errors: [], warnings: [] }; - if (!check.unemptyString( raw.ids )) { + if (!check.nonEmptyString( raw.ids )) { messages.errors.push( lengthError); return messages; } @@ -72,10 +65,10 @@ function sanitize( raw, clean ){ var rawIds = raw.ids.split(','); // deduplicate - rawIds = _.unique(rawIds); + rawIds = _.uniq(rawIds); // ensure all elements are valid non-empty strings - if (!rawIds.every(check.unemptyString)) { + if (!rawIds.every(check.nonEmptyString)) { messages.errors.push( lengthError ); } diff --git a/sanitiser/_source.js b/sanitiser/_source.js deleted file mode 100644 index fae33d1d..00000000 --- a/sanitiser/_source.js +++ /dev/null @@ -1,46 +0,0 @@ - -var _ = require('lodash'), - check = require('check-types'), - sources_map = require( '../query/sources' ); - -var ALL_SOURCES = Object.keys(sources_map), - ALL_SOURCES_JOINED = ALL_SOURCES.join(','); - -function sanitize( raw, clean ) { - - // error & warning messages - var messages = { errors: [], warnings: [] }; - - // init clean.types (if not already init) - clean.types = clean.types || {}; - - // default case (no layers specified in GET params) - // don't even set the from_layers key in this case - if( check.unemptyString( raw.source ) ){ - - var sources = raw.source.split(','); - - var invalid_sources = sources.filter(function(source) { - return !_.contains( ALL_SOURCES, source ); - }); - - if( invalid_sources.length > 0 ){ - invalid_sources.forEach( function( invalid ){ - messages.errors.push('\'' + invalid + '\' is an invalid source parameter. Valid options: ' + ALL_SOURCES_JOINED); - }); - } - - else { - var types = sources.reduce(function(acc, source) { - return acc.concat(sources_map[source]); - }, []); - - clean.types.from_source = types; - } - - } - - return messages; -} - -module.exports = sanitize; diff --git a/sanitiser/_sources_and_layers.js b/sanitiser/_sources_and_layers.js new file mode 100644 index 00000000..448a7af1 --- /dev/null +++ b/sanitiser/_sources_and_layers.js @@ -0,0 +1,37 @@ +var _ = require( 'lodash' ); +var type_mapping = require( '../helper/type_mapping' ); + +/* + * This sanitiser depends on clean.layers and clean.sources + * so it has to be run after those sanitisers have been run + */ +function sanitize( raw, clean ){ + var messages = { errors: [], warnings: [] }; + + var possible_errors = []; + var at_least_one_valid_combination = false; + + if (clean.layers && clean.sources) { + clean.sources.forEach(function(source) { + var layers_for_source = type_mapping.layers_by_source[source]; + clean.layers.forEach(function(layer) { + if (_.includes(layers_for_source, layer)) { + at_least_one_valid_combination = true; + } else { + var message = 'You have specified both the `sources` and `layers` ' + + 'parameters in a combination that will return no results: the ' + + source + ' source has nothing in the ' + layer + ' layer'; + possible_errors.push(message); + } + }); + }); + + if (!at_least_one_valid_combination) { + messages.errors = possible_errors; + } + } + + return messages; +} + +module.exports = sanitize; diff --git a/sanitiser/_targets.js b/sanitiser/_targets.js index 81e345b6..a8a50400 100644 --- a/sanitiser/_targets.js +++ b/sanitiser/_targets.js @@ -1,34 +1,33 @@ - var _ = require('lodash'), check = require('check-types'); +function getValidKeys(mapping) { + return _.uniq(Object.keys(mapping)).join(','); +} + function setup( paramName, targetMap ) { return function( raw, clean ){ return sanitize( raw, clean, { paramName: paramName, targetMap: targetMap, - targetMapKeysString: Object.keys(targetMap).join(',') + targetMapKeysString: getValidKeys(targetMap) }); }; } function sanitize( raw, clean, opts ) { - // error & warning messages var messages = { errors: [], warnings: [] }; - // init clean.types - clean.types = clean.types || {}; - // the string of targets (comma delimeted) var targetsString = raw[opts.paramName]; // trim whitespace - if( check.unemptyString( targetsString ) ){ + if( check.nonEmptyString( targetsString ) ){ targetsString = targetsString.trim(); // param must be a valid non-empty string - if( !check.unemptyString( targetsString ) ){ + if( !check.nonEmptyString( targetsString ) ){ messages.errors.push( opts.paramName + ' parameter cannot be an empty string. Valid options: ' + opts.targetMapKeysString ); @@ -51,19 +50,13 @@ function sanitize( raw, clean, opts ) { // only set types value when no error occured if( !messages.errors.length ){ - - // store the values under a new key as 'clean.types.from_*' - var typesKey = 'from_' + opts.paramName; - - // ? - clean.types[typesKey] = targets.reduce(function(acc, target) { + clean[opts.paramName] = targets.reduce(function(acc, target) { return acc.concat(opts.targetMap[target]); }, []); // dedupe in case aliases expanded to common things or user typed in duplicates - clean.types[typesKey] = _.unique(clean.types[typesKey]); + clean[opts.paramName] = _.uniq(clean[opts.paramName]); } - } } diff --git a/sanitiser/_text.js b/sanitiser/_text.js index 824c7a87..e6897a5e 100644 --- a/sanitiser/_text.js +++ b/sanitiser/_text.js @@ -8,7 +8,7 @@ function sanitize( raw, clean ){ var messages = { errors: [], warnings: [] }; // invalid input 'text' - if( !check.unemptyString( raw.text ) ){ + if( !check.nonEmptyString( raw.text ) ){ messages.errors.push('invalid param \'text\': text length, must be >0'); } @@ -23,10 +23,6 @@ function sanitize( raw, clean ){ if (check.assigned(parsed_text)) { clean.parsed_text = parsed_text; } - - // try to set layers from query parser results - clean.types = clean.layers || {}; - clean.types.from_text_parser = text_parser.get_layers(clean.text); } return messages; diff --git a/sanitiser/_warn_quattroshapes.js b/sanitiser/_warn_quattroshapes.js deleted file mode 100644 index 2b887e5f..00000000 --- a/sanitiser/_warn_quattroshapes.js +++ /dev/null @@ -1,25 +0,0 @@ -var _ = require('lodash'); - -function setup( paramName, targetMap ) { - return function( raw, clean ){ - return sanitize( raw, clean ); - }; -} - -function sanitize( raw, clean, opts ) { - // error & warning messages - var messages = { errors: [], warnings: [] }; - - if (_.includes(raw.sources, 'quattroshapes') || _.includes(raw.sources, 'qs')) { - messages.warnings.push( 'You are using Quattroshapes as a data source in this query. ' + - 'Quattroshapes will be disabled as a data source for Mapzen Search in the next several ' + - 'weeks, and is being replaced by Who\'s on First, an actively maintained data project ' + - 'based on Quattroshapes. Your existing queries WILL CONTINUE TO WORK for the foreseeable ' + - 'future, but results will be coming from Who\'s on First and `sources=quattroshapes` will ' + - 'be deprecated. If you have any questions, please email search@mapzen.com.'); - } - - return messages; -} - -module.exports = setup; diff --git a/sanitiser/reverse.js b/sanitiser/reverse.js index 6ebcfd14..c8e1e3f4 100644 --- a/sanitiser/reverse.js +++ b/sanitiser/reverse.js @@ -2,10 +2,12 @@ var type_mapping = require('../helper/type_mapping'); var sanitizeAll = require('../sanitiser/sanitizeAll'), sanitizers = { + quattroshapes_deprecation: require('../sanitiser/_deprecate_quattroshapes'), singleScalarParameters: require('../sanitiser/_single_scalar_parameters'), - layers: require('../sanitiser/_targets')('layers', type_mapping.layer_with_aliases_to_type), - sources: require('../sanitiser/_targets')('sources', type_mapping.source_to_type), - quattroshapes_warning: require('../sanitiser/_warn_quattroshapes')(), + layers: require('../sanitiser/_targets')('layers', type_mapping.layer_mapping), + sources: require('../sanitiser/_targets')('sources', type_mapping.source_mapping), + // depends on the layers and sources sanitisers, must be run after them + sources_and_layers: require('../sanitiser/_sources_and_layers'), size: require('../sanitiser/_size'), private: require('../sanitiser/_flag_bool')('private', false), geo_reverse: require('../sanitiser/_geo_reverse'), diff --git a/sanitiser/search.js b/sanitiser/search.js index 75be20ca..8b26e54e 100644 --- a/sanitiser/search.js +++ b/sanitiser/search.js @@ -2,12 +2,14 @@ var type_mapping = require('../helper/type_mapping'); var sanitizeAll = require('../sanitiser/sanitizeAll'), sanitizers = { + quattroshapes_deprecation: require('../sanitiser/_deprecate_quattroshapes'), singleScalarParameters: require('../sanitiser/_single_scalar_parameters'), text: require('../sanitiser/_text'), size: require('../sanitiser/_size'), - layers: require('../sanitiser/_targets')('layers', type_mapping.layer_with_aliases_to_type), - sources: require('../sanitiser/_targets')('sources', type_mapping.source_to_type), - quattroshapes_warning: require('../sanitiser/_warn_quattroshapes')(), + layers: require('../sanitiser/_targets')('layers', type_mapping.layer_mapping), + sources: require('../sanitiser/_targets')('sources', type_mapping.source_mapping), + // depends on the layers and sources sanitisers, must be run after them + sources_and_layers: require('../sanitiser/_sources_and_layers'), private: require('../sanitiser/_flag_bool')('private', false), geo_search: require('../sanitiser/_geo_search'), boundary_country: require('../sanitiser/_boundary_country'), diff --git a/service/mget.js b/service/mget.js index a878e8cb..529d0fc1 100644 --- a/service/mget.js +++ b/service/mget.js @@ -24,7 +24,6 @@ function service( backend, query, cb ){ // query new backend backend().client.mget( cmd, function( err, data ){ - // handle backend errors if( err ){ return cb( err ); } diff --git a/src/backend.js b/src/backend.js index d0388966..c983dbba 100644 --- a/src/backend.js +++ b/src/backend.js @@ -1,6 +1,6 @@ - +var config = require( 'pelias-config' ).generate().esclient; var Backend = require('geopipes-elasticsearch-backend'), - client = require('pelias-esclient')(), + client = require('elasticsearch').Client(config), backends = {}; function getBackend( index, type ){ @@ -11,4 +11,4 @@ function getBackend( index, type ){ return backends[key]; } -module.exports = getBackend; \ No newline at end of file +module.exports = getBackend; diff --git a/test/ciao/place/basic_place.coffee b/test/ciao/place/basic_place.coffee index b4a18711..e46bbee8 100644 --- a/test/ciao/place/basic_place.coffee +++ b/test/ciao/place/basic_place.coffee @@ -29,5 +29,5 @@ should.not.exist json.geocoding.errors should.not.exist json.geocoding.warnings #? inputs -json.geocoding.query['ids'].should.eql [{ id: '1', types: [ 'geoname' ] }] +json.geocoding.query['ids'].should.eql [{ id: '1', layer: 'venue', source: 'geonames' }] should.not.exist json.geocoding.query['size'] diff --git a/test/ciao/reverse/layers_alias_address.coffee b/test/ciao/reverse/layers_alias_address.coffee new file mode 100644 index 00000000..fb8e3262 --- /dev/null +++ b/test/ciao/reverse/layers_alias_address.coffee @@ -0,0 +1,33 @@ + +#> layer alias +path: '/v1/reverse?point.lat=1&point.lon=2&layers=address' + +#? 200 ok +response.statusCode.should.be.equal 200 +response.should.have.header 'charset', 'utf8' +response.should.have.header 'content-type', 'application/json; charset=utf-8' + +#? valid geocoding block +should.exist json.geocoding +should.exist json.geocoding.version +should.exist json.geocoding.attribution +should.exist json.geocoding.query +should.exist json.geocoding.engine +should.exist json.geocoding.engine.name +should.exist json.geocoding.engine.author +should.exist json.geocoding.engine.version +should.exist json.geocoding.timestamp + +#? valid geojson +json.type.should.be.equal 'FeatureCollection' +json.features.should.be.instanceof Array + +#? expected errors +should.not.exist json.geocoding.errors + +#? expected warnings +should.not.exist json.geocoding.warnings + +#? inputs +json.geocoding.query['size'].should.eql 10 +json.geocoding.query.layers.should.eql ["address"] diff --git a/test/ciao/reverse/layers_alias_coarse.coffee b/test/ciao/reverse/layers_alias_coarse.coffee index 13671a05..f16f5a17 100644 --- a/test/ciao/reverse/layers_alias_coarse.coffee +++ b/test/ciao/reverse/layers_alias_coarse.coffee @@ -30,5 +30,16 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_layers'].should.eql ["admin0","admin1","admin2","neighborhood","locality","local_admin"] -json.geocoding.query['type'].should.eql ["admin0","admin1","admin2","neighborhood","locality","local_admin"] \ No newline at end of file +json.geocoding.query.layers.should.eql [ "continent", + "macrocountry", + "country", + "dependency", + "region", + "locality", + "localadmin", + "county", + "macrohood", + "neighbourhood", + "microhood", + "disputed" +] diff --git a/test/ciao/reverse/layers_invalid.coffee b/test/ciao/reverse/layers_invalid.coffee index 354efb02..9d89e05a 100644 --- a/test/ciao/reverse/layers_invalid.coffee +++ b/test/ciao/reverse/layers_invalid.coffee @@ -24,7 +24,7 @@ json.features.should.be.instanceof Array #? expected errors should.exist json.geocoding.errors -json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,venue,address,country,region,county,locality,localadmin,neighbourhood' ] +json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,country,region,county,locality,continent,macrocountry,dependency,localadmin,macrohood,neighbourhood,microhood,disputed' ] #? expected warnings should.not.exist json.geocoding.warnings diff --git a/test/ciao/reverse/layers_mix_invalid_valid.coffee b/test/ciao/reverse/layers_mix_invalid_valid.coffee index a636a26b..3e6a57fe 100644 --- a/test/ciao/reverse/layers_mix_invalid_valid.coffee +++ b/test/ciao/reverse/layers_mix_invalid_valid.coffee @@ -24,7 +24,7 @@ json.features.should.be.instanceof Array #? expected errors should.exist json.geocoding.errors -json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,venue,address,country,region,county,locality,localadmin,neighbourhood' ] +json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,country,region,county,locality,continent,macrocountry,dependency,localadmin,macrohood,neighbourhood,microhood,disputed' ] #? expected warnings should.not.exist json.geocoding.warnings diff --git a/test/ciao/reverse/layers_multiple.coffee b/test/ciao/reverse/layers_multiple.coffee index f5f2d7dd..75b466d2 100644 --- a/test/ciao/reverse/layers_multiple.coffee +++ b/test/ciao/reverse/layers_multiple.coffee @@ -30,5 +30,4 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_layers'].should.eql ["admin0","admin1"] -json.geocoding.query['type'].should.eql ["admin0","admin1"] +json.geocoding.query.layers.should.eql ["country","region"] diff --git a/test/ciao/reverse/layers_single.coffee b/test/ciao/reverse/layers_single.coffee index 4b65c331..8f5220eb 100644 --- a/test/ciao/reverse/layers_single.coffee +++ b/test/ciao/reverse/layers_single.coffee @@ -30,5 +30,4 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_layers'].should.eql ["admin0"] -json.geocoding.query['type'].should.eql ["admin0"] +json.geocoding.query.layers.should.eql ["country"] diff --git a/test/ciao/reverse/sources_deprecation_warning.coffee b/test/ciao/reverse/sources_deprecation_warning.coffee new file mode 100644 index 00000000..45c2c967 --- /dev/null +++ b/test/ciao/reverse/sources_deprecation_warning.coffee @@ -0,0 +1,34 @@ + +#> quattroshapes is being phased out and so should emit a warning message +path: '/v1/reverse?point.lat=1&point.lon=2&sources=qs' + +#? 200 ok +response.statusCode.should.be.equal 200 +response.should.have.header 'charset', 'utf8' +response.should.have.header 'content-type', 'application/json; charset=utf-8' + +#? valid geocoding block +should.exist json.geocoding +should.exist json.geocoding.version +should.exist json.geocoding.attribution +should.exist json.geocoding.query +should.exist json.geocoding.engine +should.exist json.geocoding.engine.name +should.exist json.geocoding.engine.author +should.exist json.geocoding.engine.version +should.exist json.geocoding.timestamp + +#? valid geojson +json.type.should.be.equal 'FeatureCollection' +json.features.should.be.instanceof Array + +#? expected errors +should.not.exist json.geocoding.errors + +#? expected warnings +should.exist json.geocoding.warnings +json.geocoding.warnings.should.eql ['You are using Quattroshapes as a data source in this query. Quattroshapes has been disabled as a data source for Mapzen Search, and has beenreplaced by Who\'s on First, an actively maintained data project based on QuattroshapesYour existing queries WILL CONTINUE TO WORK for the foreseeable future, but results will be coming from Who\'s on First and `sources=quattroshapes` will be interpreted as `sources=whosonfirst`. If you have any questions, please email search@mapzen.com.' ] + +#? inputs +json.geocoding.query['size'].should.eql 10 +json.geocoding.query.sources.should.eql ['whosonfirst'] # should use 'whosonfirst' instead of 'quattroshapes' diff --git a/test/ciao/reverse/sources_invalid.coffee b/test/ciao/reverse/sources_invalid.coffee index bdc6c826..62a6c4c2 100644 --- a/test/ciao/reverse/sources_invalid.coffee +++ b/test/ciao/reverse/sources_invalid.coffee @@ -24,7 +24,7 @@ json.features.should.be.instanceof Array #? expected errors should.exist json.geocoding.errors -json.geocoding.errors.should.eql [ '\'notasource\' is an invalid sources parameter. Valid options: gn,geonames,oa,openaddresses,qs,quattroshapes,osm,openstreetmap' ] +json.geocoding.errors.should.eql [ '\'notasource\' is an invalid sources parameter. Valid options: osm,oa,gn,wof,openstreetmap,openaddresses,geonames,whosonfirst' ] #? expected warnings should.not.exist json.geocoding.warnings diff --git a/test/ciao/reverse/sources_layers_invalid_combo.coffee b/test/ciao/reverse/sources_layers_invalid_combo.coffee index 25ae9da3..baf157cc 100644 --- a/test/ciao/reverse/sources_layers_invalid_combo.coffee +++ b/test/ciao/reverse/sources_layers_invalid_combo.coffee @@ -1,6 +1,6 @@ #> sources and layers specified (invalid combo) -path: '/v1/reverse?point.lat=1&point.lon=2&sources=quattroshapes&layers=address' +path: '/v1/reverse?point.lat=1&point.lon=2&sources=whosonfirst&layers=address' #? 200 ok response.statusCode.should.be.equal 400 @@ -24,13 +24,12 @@ json.features.should.be.instanceof Array #? expected errors should.exist json.geocoding.errors -json.geocoding.errors.should.eql [ 'You have specified both the `sources` and `layers` parameters in a combination that will return no results.' ] +json.geocoding.errors.should.eql [ 'You have specified both the `sources` and `layers` parameters in a combination that will return no results: the whosonfirst source has nothing in the address layer' ] #? expected warnings -json.geocoding.warnings.should.eql [ 'You are using Quattroshapes as a data source in this query. Quattroshapes will be disabled as a data source for Mapzen Search in the next several weeks, and is being replaced by Who\'s on First, an actively maintained data project based on Quattroshapes. Your existing queries WILL CONTINUE TO WORK for the foreseeable future, but results will be coming from Who\'s on First and `sources=quattroshapes` will be deprecated. If you have any questions, please email search@mapzen.com.' ] +should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_layers'].should.eql ["osmaddress","openaddresses"] -json.geocoding.query.types['from_sources'].should.eql ["admin0","admin1","admin2","neighborhood","locality","local_admin"] -should.not.exist json.geocoding.query['type'] +json.geocoding.query.layers.should.eql ["address"] +json.geocoding.query.sources.should.eql ["whosonfirst"] diff --git a/test/ciao/reverse/sources_layers_valid_combo.coffee b/test/ciao/reverse/sources_layers_valid_combo.coffee index a24ea221..fc2fee76 100644 --- a/test/ciao/reverse/sources_layers_valid_combo.coffee +++ b/test/ciao/reverse/sources_layers_valid_combo.coffee @@ -30,5 +30,5 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_layers'].should.eql ["osmaddress","openaddresses"] -json.geocoding.query['type'].should.eql ["openaddresses"] +json.geocoding.query.layers.should.eql ["address"] +json.geocoding.query.sources.should.eql ["openaddresses"] diff --git a/test/ciao/reverse/sources_multiple.coffee b/test/ciao/reverse/sources_multiple.coffee index 8728109c..4fd24366 100644 --- a/test/ciao/reverse/sources_multiple.coffee +++ b/test/ciao/reverse/sources_multiple.coffee @@ -30,5 +30,4 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_sources'].should.eql ["osmaddress","osmnode","osmway","geoname"] -json.geocoding.query['type'].should.eql ["geoname","osmnode","osmway","osmaddress"] +json.geocoding.query.sources.should.eql ["openstreetmap", "geonames"] diff --git a/test/ciao/reverse/sources_single.coffee b/test/ciao/reverse/sources_single.coffee index 2c638503..6a062c9a 100644 --- a/test/ciao/reverse/sources_single.coffee +++ b/test/ciao/reverse/sources_single.coffee @@ -30,5 +30,4 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_sources'].should.eql ["osmaddress","osmnode","osmway"] -json.geocoding.query['type'].should.eql ["osmnode","osmway","osmaddress"] +json.geocoding.query.sources.should.eql ["openstreetmap"] diff --git a/test/ciao/search/layers_alias_address.coffee b/test/ciao/search/layers_alias_address.coffee index 9bf32a04..e3e39d15 100644 --- a/test/ciao/search/layers_alias_address.coffee +++ b/test/ciao/search/layers_alias_address.coffee @@ -31,5 +31,4 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['text'].should.eql 'a' json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_layers'].should.eql ["osmaddress","openaddresses"] -json.geocoding.query['type'].should.eql ["osmaddress","openaddresses"] +json.geocoding.query.layers.should.eql ["address"] diff --git a/test/ciao/search/layers_alias_coarse.coffee b/test/ciao/search/layers_alias_coarse.coffee index 262be88e..9dda2a48 100644 --- a/test/ciao/search/layers_alias_coarse.coffee +++ b/test/ciao/search/layers_alias_coarse.coffee @@ -31,5 +31,16 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['text'].should.eql 'a' json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_layers'].should.eql ["admin0","admin1","admin2","neighborhood","locality","local_admin"] -json.geocoding.query['type'].should.eql ["admin0","admin1","admin2","neighborhood","locality","local_admin"] \ No newline at end of file +json.geocoding.query.layers.should.eql [ "continent", + "macrocountry", + "country", + "dependency", + "region", + "locality", + "localadmin", + "county", + "macrohood", + "neighbourhood", + "microhood", + "disputed" +] diff --git a/test/ciao/search/layers_invalid.coffee b/test/ciao/search/layers_invalid.coffee index ec9c58bd..5d07f99c 100644 --- a/test/ciao/search/layers_invalid.coffee +++ b/test/ciao/search/layers_invalid.coffee @@ -24,7 +24,7 @@ json.features.should.be.instanceof Array #? expected errors should.exist json.geocoding.errors -json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,venue,address,country,region,county,locality,localadmin,neighbourhood' ] +json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,country,region,county,locality,continent,macrocountry,dependency,localadmin,macrohood,neighbourhood,microhood,disputed' ] #? expected warnings should.not.exist json.geocoding.warnings @@ -32,5 +32,3 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['text'].should.eql 'a' json.geocoding.query['size'].should.eql 10 -should.not.exist json.geocoding.query['types'] -should.not.exist json.geocoding.query['type'] diff --git a/test/ciao/search/layers_mix_invalid_valid.coffee b/test/ciao/search/layers_mix_invalid_valid.coffee index fab29ef2..0a13c70b 100644 --- a/test/ciao/search/layers_mix_invalid_valid.coffee +++ b/test/ciao/search/layers_mix_invalid_valid.coffee @@ -24,7 +24,7 @@ json.features.should.be.instanceof Array #? expected errors should.exist json.geocoding.errors -json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,venue,address,country,region,county,locality,localadmin,neighbourhood' ] +json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,country,region,county,locality,continent,macrocountry,dependency,localadmin,macrohood,neighbourhood,microhood,disputed' ] #? expected warnings should.not.exist json.geocoding.warnings @@ -32,5 +32,4 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['text'].should.eql 'a' json.geocoding.query['size'].should.eql 10 -should.not.exist json.geocoding.query['types'] -should.not.exist json.geocoding.query['type'] +should.not.exist json.geocoding.query['layers'] diff --git a/test/ciao/search/layers_multiple.coffee b/test/ciao/search/layers_multiple.coffee index 5f714d4d..2555573e 100644 --- a/test/ciao/search/layers_multiple.coffee +++ b/test/ciao/search/layers_multiple.coffee @@ -31,5 +31,4 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['text'].should.eql 'a' json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_layers'].should.eql ["admin0","admin1"] -json.geocoding.query['type'].should.eql ["admin0","admin1"] +json.geocoding.query.layers.should.eql ["country","region"] diff --git a/test/ciao/search/layers_single.coffee b/test/ciao/search/layers_single.coffee index 227f7bc9..e1931c04 100644 --- a/test/ciao/search/layers_single.coffee +++ b/test/ciao/search/layers_single.coffee @@ -31,5 +31,4 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['text'].should.eql 'a' json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_layers'].should.eql ["admin0"] -json.geocoding.query['type'].should.eql ["admin0"] +json.geocoding.query.layers.should.eql ["country"] diff --git a/test/ciao/search/sources_deprecation_warning.coffee b/test/ciao/search/sources_deprecation_warning.coffee new file mode 100644 index 00000000..c6d96a2c --- /dev/null +++ b/test/ciao/search/sources_deprecation_warning.coffee @@ -0,0 +1,35 @@ + +#> quattroshapes is being phased out and so should emit a warning message +path: '/v1/search?sources=qs&text=a' + +#? 200 ok +response.statusCode.should.be.equal 200 +response.should.have.header 'charset', 'utf8' +response.should.have.header 'content-type', 'application/json; charset=utf-8' + +#? valid geocoding block +should.exist json.geocoding +should.exist json.geocoding.version +should.exist json.geocoding.attribution +should.exist json.geocoding.query +should.exist json.geocoding.engine +should.exist json.geocoding.engine.name +should.exist json.geocoding.engine.author +should.exist json.geocoding.engine.version +should.exist json.geocoding.timestamp + +#? valid geojson +json.type.should.be.equal 'FeatureCollection' +json.features.should.be.instanceof Array + +#? expected errors +should.not.exist json.geocoding.errors + +#? expected warnings +should.exist json.geocoding.warnings +json.geocoding.warnings.should.eql ['You are using Quattroshapes as a data source in this query. Quattroshapes has been disabled as a data source for Mapzen Search, and has beenreplaced by Who\'s on First, an actively maintained data project based on QuattroshapesYour existing queries WILL CONTINUE TO WORK for the foreseeable future, but results will be coming from Who\'s on First and `sources=quattroshapes` will be interpreted as `sources=whosonfirst`. If you have any questions, please email search@mapzen.com.' ] + +#? inputs +json.geocoding.query['size'].should.eql 10 +json.geocoding.query['text'].should.eql 'a' +json.geocoding.query.sources.should.eql ['whosonfirst'] # should use 'whosonfirst' instead of 'quattroshapes' diff --git a/test/ciao/search/sources_invalid.coffee b/test/ciao/search/sources_invalid.coffee index 2114c9c0..1620d7ba 100644 --- a/test/ciao/search/sources_invalid.coffee +++ b/test/ciao/search/sources_invalid.coffee @@ -24,7 +24,7 @@ json.features.should.be.instanceof Array #? expected errors should.exist json.geocoding.errors -json.geocoding.errors.should.eql [ '\'notasource\' is an invalid sources parameter. Valid options: gn,geonames,oa,openaddresses,qs,quattroshapes,osm,openstreetmap' ] +json.geocoding.errors.should.eql [ '\'notasource\' is an invalid sources parameter. Valid options: osm,oa,gn,wof,openstreetmap,openaddresses,geonames,whosonfirst' ] #? expected warnings should.not.exist json.geocoding.warnings @@ -32,5 +32,3 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['text'].should.eql 'a' json.geocoding.query['size'].should.eql 10 -should.not.exist json.geocoding.query['types'] -should.not.exist json.geocoding.query['type'] diff --git a/test/ciao/search/sources_layers_invalid_combo.coffee b/test/ciao/search/sources_layers_invalid_combo.coffee index d73ca61a..821bf243 100644 --- a/test/ciao/search/sources_layers_invalid_combo.coffee +++ b/test/ciao/search/sources_layers_invalid_combo.coffee @@ -1,6 +1,6 @@ #> sources and layers specified (invalid combo) -path: '/v1/search?text=a&sources=quattroshapes&layers=address' +path: '/v1/search?text=a&sources=whosonfirst&layers=address' #? 200 ok response.statusCode.should.be.equal 400 @@ -24,14 +24,14 @@ json.features.should.be.instanceof Array #? expected errors should.exist json.geocoding.errors -json.geocoding.errors.should.eql [ 'You have specified both the `sources` and `layers` parameters in a combination that will return no results.' ] +json.geocoding.errors.should.eql [ 'You have specified both the `sources` and `layers` parameters in a combination that will return no results: the whosonfirst source has nothing in the address layer' ] #? expected warnings -json.geocoding.warnings.should.eql [ 'You are using Quattroshapes as a data source in this query. Quattroshapes will be disabled as a data source for Mapzen Search in the next several weeks, and is being replaced by Who\'s on First, an actively maintained data project based on Quattroshapes. Your existing queries WILL CONTINUE TO WORK for the foreseeable future, but results will be coming from Who\'s on First and `sources=quattroshapes` will be deprecated. If you have any questions, please email search@mapzen.com.' ] +should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['text'].should.eql 'a' json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_layers'].should.eql ["osmaddress","openaddresses"] -json.geocoding.query.types['from_sources'].should.eql ["admin0","admin1","admin2","neighborhood","locality","local_admin"] +json.geocoding.query.layers.should.eql ["address"] +json.geocoding.query.sources.should.eql ["whosonfirst"] should.not.exist json.geocoding.query['type'] diff --git a/test/ciao/search/sources_layers_valid_combo.coffee b/test/ciao/search/sources_layers_valid_combo.coffee index eeb2a663..b17a81b0 100644 --- a/test/ciao/search/sources_layers_valid_combo.coffee +++ b/test/ciao/search/sources_layers_valid_combo.coffee @@ -31,5 +31,5 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['text'].should.eql 'a' json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_layers'].should.eql ["osmaddress","openaddresses"] -json.geocoding.query['type'].should.eql ["openaddresses"] +json.geocoding.query.layers.should.eql ["address"] +json.geocoding.query.sources.should.eql ["openaddresses"] diff --git a/test/ciao/search/sources_multiple.coffee b/test/ciao/search/sources_multiple.coffee index fa5a650c..08352113 100644 --- a/test/ciao/search/sources_multiple.coffee +++ b/test/ciao/search/sources_multiple.coffee @@ -31,5 +31,4 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['text'].should.eql 'a' json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_sources'].should.eql ["osmaddress","osmnode","osmway","geoname"] -json.geocoding.query['type'].should.eql ["geoname","osmnode","osmway","osmaddress"] +json.geocoding.query.sources.should.eql ["openstreetmap", "geonames"] diff --git a/test/ciao/search/sources_single.coffee b/test/ciao/search/sources_single.coffee index 3b3e0bf4..e639a8b5 100644 --- a/test/ciao/search/sources_single.coffee +++ b/test/ciao/search/sources_single.coffee @@ -31,5 +31,4 @@ should.not.exist json.geocoding.warnings #? inputs json.geocoding.query['text'].should.eql 'a' json.geocoding.query['size'].should.eql 10 -json.geocoding.query.types['from_sources'].should.eql ["osmaddress","osmnode","osmway"] -json.geocoding.query['type'].should.eql ["osmnode","osmway","osmaddress"] +json.geocoding.query.sources.should.eql ["openstreetmap"] diff --git a/test/unit/controller/place.js b/test/unit/controller/place.js index e8a8c333..977be59e 100644 --- a/test/unit/controller/place.js +++ b/test/unit/controller/place.js @@ -41,7 +41,7 @@ module.exports.tests.functional_success = function(test, common) { test('functional success', function(t) { var backend = mockBackend( 'client/mget/ok/1', function( cmd ){ - t.deepEqual(cmd, { body: { docs: [ { _id: 123, _index: 'pelias', _type: 'a' } ] } }, 'correct backend command'); + t.deepEqual(cmd, { body: { docs: [ { _id: 123, _index: 'pelias', _type: [ 'a' ] } ] } }, 'correct backend command'); }); var controller = setup( backend ); var res = { @@ -57,7 +57,7 @@ module.exports.tests.functional_success = function(test, common) { t.deepEqual(json.features, expected, 'values correctly mapped'); } }; - var req = { clean: { ids: [ {'id' : 123, types: [ 'a' ] } ] }, errors: [], warnings: [] }; + var req = { clean: { ids: [ {'id' : 123, layers: [ 'a' ] } ] }, errors: [], warnings: [] }; var next = function next() { t.equal(req.errors.length, 0, 'next was called without error'); t.end(); @@ -70,10 +70,10 @@ module.exports.tests.functional_success = function(test, common) { module.exports.tests.functional_failure = function(test, common) { test('functional failure', function(t) { var backend = mockBackend( 'client/mget/fail/1', function( cmd ){ - t.deepEqual(cmd, { body: { docs: [ { _id: 123, _index: 'pelias', _type: 'b' } ] } }, 'correct backend command'); + t.deepEqual(cmd, { body: { docs: [ { _id: 123, _index: 'pelias', _type: ['b'] } ] } }, 'correct backend command'); }); var controller = setup( backend ); - var req = { clean: { ids: [ {'id' : 123, types: [ 'b' ] } ] }, errors: [], warnings: [] }; + var req = { clean: { ids: [ {'id' : 123, layers: [ 'b' ] } ] }, errors: [], warnings: [] }; var next = function( message ){ t.equal(req.errors[0],'a backend error occurred','error passed to errorHandler'); t.end(); diff --git a/test/unit/controller/search.js b/test/unit/controller/search.js index 2b412013..cfb715fa 100644 --- a/test/unit/controller/search.js +++ b/test/unit/controller/search.js @@ -50,9 +50,11 @@ module.exports.tests.functional_success = function(test, common) { _id: 'myid1', _score: 10, _type: 'mytype1', - admin0: 'country1', - admin1: 'state1', - admin2: 'city1', + parent: { + country: ['country1'], + region: ['state1'], + county: ['city1'] + }, center_point: { lat: 100.1, lon: -50.5 }, name: { default: 'test name1' }, value: 1 @@ -61,9 +63,11 @@ module.exports.tests.functional_success = function(test, common) { _id: 'myid2', _score: 20, _type: 'mytype2', - admin0: 'country2', - admin1: 'state2', - admin2: 'city2', + parent: { + country: ['country2'], + region: ['state2'], + county: ['city2'] + }, center_point: { lat: 100.2, lon: -51.5 }, name: { default: 'test name2' }, value: 2 diff --git a/test/unit/fixture/autocomplete_linguistic_final_token.js b/test/unit/fixture/autocomplete_linguistic_final_token.js index e63c84b6..fbe80052 100644 --- a/test/unit/fixture/autocomplete_linguistic_final_token.js +++ b/test/unit/fixture/autocomplete_linguistic_final_token.js @@ -31,15 +31,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 }] @@ -60,17 +56,13 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, - 'weight': 2 + 'weight': 3 }] } }] diff --git a/test/unit/fixture/autocomplete_linguistic_focus.js b/test/unit/fixture/autocomplete_linguistic_focus.js index 7f3c7174..4f722b84 100644 --- a/test/unit/fixture/autocomplete_linguistic_focus.js +++ b/test/unit/fixture/autocomplete_linguistic_focus.js @@ -35,35 +35,25 @@ module.exports = { 'lat': 29.49136, 'lon': -82.50622 }, - 'offset': '10km', + 'offset': '0km', 'scale': '250km', 'decay': 0.5 } }, - 'weight': 3 + 'weight': 10 }], 'score_mode': 'avg', 'boost_mode': 'multiply', 'filter': { 'or': [ { - 'type': { - 'value': 'osmnode' - } - }, - { - 'type': { - 'value': 'osmway' - } - }, - { - 'type': { - 'value': 'osmaddress' + 'term': { + 'layer': 'venue' } }, { - 'type': { - 'value': 'openaddresses' + 'term': { + 'layer': 'address' } } ] @@ -85,15 +75,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 }] @@ -114,17 +100,13 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, - 'weight': 2 + 'weight': 3 }] } }] diff --git a/test/unit/fixture/autocomplete_linguistic_focus_null_island.js b/test/unit/fixture/autocomplete_linguistic_focus_null_island.js index 46554715..d9c04fd1 100644 --- a/test/unit/fixture/autocomplete_linguistic_focus_null_island.js +++ b/test/unit/fixture/autocomplete_linguistic_focus_null_island.js @@ -35,35 +35,25 @@ module.exports = { 'lat': 0, 'lon': 0 }, - 'offset': '10km', + 'offset': '0km', 'scale': '250km', 'decay': 0.5 } }, - 'weight': 3 + 'weight': 10 }], 'score_mode': 'avg', 'boost_mode': 'multiply', 'filter': { 'or': [ { - 'type': { - 'value': 'osmnode' - } - }, - { - 'type': { - 'value': 'osmway' - } - }, - { - 'type': { - 'value': 'osmaddress' + 'term': { + 'layer': 'venue' } }, { - 'type': { - 'value': 'openaddresses' + 'term': { + 'layer': 'address' } } ] @@ -85,15 +75,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 }] @@ -114,17 +100,13 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, - 'weight': 2 + 'weight': 3 }] } }] diff --git a/test/unit/fixture/autocomplete_linguistic_multiple_tokens.js b/test/unit/fixture/autocomplete_linguistic_multiple_tokens.js index dab5e31a..9018fdab 100644 --- a/test/unit/fixture/autocomplete_linguistic_multiple_tokens.js +++ b/test/unit/fixture/autocomplete_linguistic_multiple_tokens.js @@ -42,15 +42,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 }] @@ -71,17 +67,13 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, - 'weight': 2 + 'weight': 3 }] } }] diff --git a/test/unit/fixture/autocomplete_linguistic_only.js b/test/unit/fixture/autocomplete_linguistic_only.js index 2ec025d4..24b89ad9 100644 --- a/test/unit/fixture/autocomplete_linguistic_only.js +++ b/test/unit/fixture/autocomplete_linguistic_only.js @@ -31,15 +31,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 }] @@ -60,17 +56,13 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, - 'weight': 2 + 'weight': 3 }] } }] diff --git a/test/unit/fixture/autocomplete_linguistic_with_admin.js b/test/unit/fixture/autocomplete_linguistic_with_admin.js index 215a5fda..245f6451 100644 --- a/test/unit/fixture/autocomplete_linguistic_with_admin.js +++ b/test/unit/fixture/autocomplete_linguistic_with_admin.js @@ -20,7 +20,7 @@ module.exports = { 'should': [ { 'match': { - 'admin0': { + 'parent.country': { 'analyzer': 'peliasAdmin', 'boost': 800, 'query': 'three' @@ -29,7 +29,7 @@ module.exports = { }, { 'match': { - 'admin1': { + 'parent.region': { 'analyzer': 'peliasAdmin', 'boost': 600, 'query': 'three' @@ -38,7 +38,7 @@ module.exports = { }, { 'match': { - 'admin1_abbr': { + 'parent.region_a': { 'analyzer': 'peliasAdmin', 'boost': 600, 'query': 'three' @@ -47,7 +47,7 @@ module.exports = { }, { 'match': { - 'admin2': { + 'parent.county': { 'analyzer': 'peliasAdmin', 'boost': 400, 'query': 'three' @@ -56,7 +56,7 @@ module.exports = { }, { 'match': { - 'local_admin': { + 'parent.localadmin': { 'analyzer': 'peliasAdmin', 'boost': 200, 'query': 'three' @@ -65,7 +65,7 @@ module.exports = { }, { 'match': { - 'locality': { + 'parent.locality': { 'analyzer': 'peliasAdmin', 'boost': 200, 'query': 'three' @@ -74,7 +74,7 @@ module.exports = { }, { 'match': { - 'neighborhood': { + 'parent.neighbourhood': { 'analyzer': 'peliasAdmin', 'boost': 200, 'query': 'three' @@ -99,18 +99,14 @@ module.exports = { { 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 } ], 'score_mode': 'first', - 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - } + 'boost_mode': 'replace' } }, { @@ -131,18 +127,14 @@ module.exports = { { 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, - 'weight': 2 + 'weight': 3 } ], 'score_mode': 'first', - 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - } + 'boost_mode': 'replace' } } ] diff --git a/test/unit/fixture/dedupe_elasticsearch_nonascii_results.js b/test/unit/fixture/dedupe_elasticsearch_nonascii_results.js index 3341141a..437060b9 100644 --- a/test/unit/fixture/dedupe_elasticsearch_nonascii_results.js +++ b/test/unit/fixture/dedupe_elasticsearch_nonascii_results.js @@ -7,9 +7,11 @@ module.exports = [ 'name': { 'default': '万里长城万里长城' }, - 'country_a': 'CHN', - 'country': 'China', - 'region': 'Beijing', + 'parent': { + 'country_a': ['CHN'], + 'country': ['China'], + 'region': ['Beijing'] + }, 'confidence': 0.733 }, { @@ -20,9 +22,11 @@ module.exports = [ 'name': { 'default': '万里长城' }, - 'country_a': 'CHN', - 'country': 'China', - 'region': 'Beijing', + 'parent': { + 'country_a': ['CHN'], + 'country': ['China'], + 'region': ['Beijing'] + }, 'confidence': 0.733 }, { @@ -33,12 +37,14 @@ module.exports = [ 'name': { 'default': '万里花' }, - 'country_a': 'JPN', - 'country': 'Japan', - 'region': 'Tokyo', - 'county': '豊島区', - 'locality': 'Tokyo', - 'neighbourhood': '2丁目', + 'parent': { + 'country_a': ['JPN'], + 'country': ['Japan'], + 'region': ['Tokyo'], + 'county': ['豊島区'], + 'locality': ['Tokyo'], + 'neighbourhood': ['2丁目'] + }, 'confidence': 0.646 }, { @@ -49,13 +55,15 @@ module.exports = [ 'name': { 'default': '万里加油站' }, - 'address': { + 'address_parts': { 'street': 'S308', 'postalcode': '312044' }, - 'country_a': 'CHN', - 'country': 'China', - 'region': 'Zhejiang', + 'parent': { + 'country_a': ['CHN'], + 'country': ['China'], + 'region': ['Zhejiang'] + }, 'confidence': 0.646 } ]; \ No newline at end of file diff --git a/test/unit/fixture/dedupe_elasticsearch_results.js b/test/unit/fixture/dedupe_elasticsearch_results.js index 6ed761c4..59da3746 100644 --- a/test/unit/fixture/dedupe_elasticsearch_results.js +++ b/test/unit/fixture/dedupe_elasticsearch_results.js @@ -4,23 +4,79 @@ module.exports = [ 'lon': -76.207456, 'lat': 40.039265 }, + 'address_parts': {}, + 'parent': { + 'localadmin': ['East Lampeter'], + 'region_a': ['PA'], + 'region': ['Pennsylvania'], + 'locality': ['Smoketown'], + 'country_a': ['USA'], + 'county': ['Lancaster County'], + 'country': ['United States'], + 'neighbourhood': ['Greenland'] + }, + 'name': { + 'default': 'East Lampeter High School' + }, + 'category': [ + 'education' + ], + '_id': '357321757', + '_type': 'venue', + '_score': 1.2367082, + 'confidence': 0.879 + }, + { // same as above, but change the neighbourhood + 'center_point': { + 'lon': -77.207456, + 'lat': 41.039265 + }, + 'address': {}, + 'parent': { + 'localadmin': 'East Lampeter', + 'region_a': 'PA', + 'region': 'Pennsylvania', + 'locality': 'Smoketown', + 'country_a': 'USA', + 'county': 'Lancaster County', + 'country': 'United States', + 'neighbourhood': 'Blueland' // ### + }, + 'name': { + 'default': 'East Lampeter High School' + }, + 'category': [ + 'education' + ], + '_id': '357321757', + '_type': 'venue', + '_score': 1.2367082, + 'confidence': 0.879 + }, + { // same as #1, but change the locality + 'center_point': { + 'lon': -73.207456, + 'lat': 42.039265 + }, 'address': {}, - 'local_admin': 'East Lampeter', - 'admin1_abbr': 'PA', + 'parent': { + 'localadmin': 'East Lampeter', + 'region_a': 'PA', + 'region': 'Pennsylvania', + 'locality': 'Firetown', // ### + 'country_a': 'USA', + 'county': 'Lancaster County', + 'country': 'United States', + 'neighbourhood': 'Greenland' + }, 'name': { 'default': 'East Lampeter High School' }, - 'admin1': 'Pennsylvania', - 'locality': 'Smoketown', - 'alpha3': 'USA', - 'admin2': 'Lancaster County', - 'admin0': 'United States', - 'neighborhood': 'Greenland', 'category': [ 'education' ], '_id': '357321757', - '_type': 'osmnode', + '_type': 'venue', '_score': 1.2367082, 'confidence': 0.879 }, @@ -29,23 +85,25 @@ module.exports = [ 'lon': -76.207456, 'lat': 40.039265 }, - 'address': {}, - 'local_admin': 'East Lampeter', - 'admin1_abbr': 'PA', + 'address_parts': {}, + 'parent': { + 'localadmin': ['East Lampeter'], + 'region_a': ['PA'], + 'region': ['Pennsylvania'], + 'locality': ['Smoketown'], + 'country_a': ['USA'], + 'county': ['Lancaster County'], + 'country': ['United States'], + 'neighbourhood': ['Greenland'] + }, 'name': { 'default': 'East Lampeter, High-School' }, - 'admin1': 'Pennsylvania', - 'locality': 'Smoketown', - 'alpha3': 'USA', - 'admin2': 'Lancaster County', - 'admin0': 'United States', - 'neighborhood': 'Greenland', 'category': [ 'education' ], '_id': '357321757', - '_type': 'osmnode', + '_type': 'venue', '_score': 1.2367082, 'confidence': 0.879 }, @@ -54,18 +112,20 @@ module.exports = [ 'lon': -76.23246, 'lat': 39.99288 }, - 'address': {}, - 'local_admin': 'West Lampeter', - 'admin1_abbr': 'PA', + 'address_parts': {}, + 'parent': { + 'localadmin': ['West Lampeter'], + 'region_a': ['PA'], + 'region': ['Pennsylvania'], + 'locality': ['Lampeter'], + 'country_a': ['USA'], + 'county': ['Lancaster County'], + 'country': ['United States'], + 'neighbourhood': ['Wheatland Mills'] + }, 'name': { 'default': 'Lampeter-Strasburg High School' }, - 'admin1': 'Pennsylvania', - 'locality': 'Lampeter', - 'alpha3': 'USA', - 'admin2': 'Lancaster County', - 'admin0': 'United States', - 'neighborhood': 'Wheatland Mills', 'category': [ 'education' ], @@ -79,18 +139,20 @@ module.exports = [ 'lon': -76.20746, 'lat': 40.03927 }, - 'address': {}, - 'local_admin': 'East Lampeter', - 'admin1_abbr': 'PA', + 'address_parts': {}, + 'parent': { + 'localadmin': ['East Lampeter'], + 'region_a': ['PA'], + 'region': ['Pennsylvania'], + 'locality': ['Smoketown'], + 'country_a': ['USA'], + 'county': ['Lancaster County'], + 'country': ['United States'], + 'neighbourhood': ['Greenland'] + }, 'name': { 'default': 'East Lampeter High School' }, - 'admin1': 'Pennsylvania', - 'locality': 'Smoketown', - 'alpha3': 'USA', - 'admin2': 'Lancaster County', - 'admin0': 'United States', - 'neighborhood': 'Greenland', 'category': [ 'education' ], @@ -104,23 +166,25 @@ module.exports = [ 'lon': -76.232457, 'lat': 39.992877 }, - 'address': {}, - 'local_admin': 'West Lampeter', - 'admin1_abbr': 'PA', + 'address_parts': {}, + 'parent': { + 'region': ['Pennsylvania'], + 'locality': ['Lampeter'], + 'country_a': ['USA'], + 'county': ['Lancaster County'], + 'country': ['United States'], + 'neighbourhood': ['Wheatland Mills'], + 'localadmin': ['West Lampeter'], + 'region_a': ['PA'] + }, 'name': { 'default': 'Lampeter-Strasburg High School' }, - 'admin1': 'Pennsylvania', - 'locality': 'Lampeter', - 'alpha3': 'USA', - 'admin2': 'Lancaster County', - 'admin0': 'United States', - 'neighborhood': 'Wheatland Mills', 'category': [ 'education' ], '_id': '357294404', - '_type': 'osmnode', + '_type': 'venue', '_score': 1.2367082, 'confidence': 0.879 }, @@ -129,23 +193,25 @@ module.exports = [ 'lon': -76.207456, 'lat': 40.038987 }, - 'address': {}, - 'local_admin': 'East Lampeter', - 'admin1_abbr': 'PA', + 'address_parts': {}, + 'parent': { + 'region': ['Pennsylvania'], + 'locality': ['Smoketown'], + 'country_a': ['USA'], + 'county': ['Lancaster County'], + 'country': ['United States'], + 'neighbourhood': ['Greenland'], + 'localadmin': ['East Lampeter'], + 'region_a': ['PA'] + }, 'name': { 'default': 'East Lampeter School' }, - 'admin1': 'Pennsylvania', - 'locality': 'Smoketown', - 'alpha3': 'USA', - 'admin2': 'Lancaster County', - 'admin0': 'United States', - 'neighborhood': 'Greenland', 'category': [ 'education' ], '_id': '357283977', - '_type': 'osmnode', + '_type': 'venue', '_score': 1.1036991, 'confidence': 0.664 }, @@ -154,18 +220,20 @@ module.exports = [ 'lon': -76.20746, 'lat': 40.03899 }, - 'address': {}, - 'local_admin': 'East Lampeter', - 'admin1_abbr': 'PA', + 'address_parts': {}, + 'parent': { + 'region': ['Pennsylvania'], + 'locality': ['Smoketown'], + 'country_a': ['USA'], + 'county': ['Lancaster County'], + 'country': ['United States'], + 'neighbourhood': ['Greenland'], + 'localadmin': ['East Lampeter'], + 'region_a': ['PA'] + }, 'name': { 'default': 'East Lampeter School' }, - 'admin1': 'Pennsylvania', - 'locality': 'Smoketown', - 'alpha3': 'USA', - 'admin2': 'Lancaster County', - 'admin0': 'United States', - 'neighborhood': 'Greenland', 'category': [ 'education' ], @@ -179,22 +247,24 @@ module.exports = [ 'lon': -94.167445, 'lat': 38.762788 }, - 'address': {}, - 'local_admin': 'Polk', - 'admin1_abbr': 'MO', + 'address_parts': {}, + 'parent': { + 'region': ['Missouri'], + 'locality': ['Strasburg'], + 'country_a': ['USA'], + 'county': ['Cass County'], + 'country': ['United States'], + 'localadmin': ['Polk'], + 'region_a': ['MO'] + }, 'name': { 'default': 'Strasburg School' }, - 'admin1': 'Missouri', - 'locality': 'Strasburg', - 'alpha3': 'USA', - 'admin2': 'Cass County', - 'admin0': 'United States', 'category': [ 'education' ], '_id': '358058986', - '_type': 'osmnode', + '_type': 'venue', '_score': 1.0492544, 'confidence': 0.658 }, @@ -203,17 +273,19 @@ module.exports = [ 'lon': -78.36317, 'lat': 38.98445 }, - 'address': {}, - 'admin1_abbr': 'VA', + 'address_parts': {}, 'name': { 'default': 'Strasburg High School' }, - 'admin1': 'Virginia', - 'locality': 'Strasburg', - 'alpha3': 'USA', - 'admin2': 'Shenandoah County', - 'admin0': 'United States', - 'neighborhood': 'Strasburg Junction', + 'parent': { + 'region_a': ['VA'], + 'region': ['Virginia'], + 'locality': ['Strasburg'], + 'country_a': ['USA'], + 'county': ['Shenandoah County'], + 'country': ['United States'], + 'neighbourhood': ['Strasburg Junction'] + }, 'category': [ 'education' ], @@ -227,17 +299,19 @@ module.exports = [ 'lon': -100.16516, 'lat': 46.13427 }, - 'address': {}, - 'local_admin': 'Strasburg', - 'admin1_abbr': 'ND', + 'address_parts': {}, 'name': { 'default': 'Strasburg High School' }, - 'admin1': 'North Dakota', - 'locality': 'Strasburg', - 'alpha3': 'USA', - 'admin2': 'Emmons County', - 'admin0': 'United States', + 'parent': { + 'localadmin': ['Strasburg'], + 'region_a': ['ND'], + 'region': ['North Dakota'], + 'locality': ['Strasburg'], + 'country_a': ['USA'], + 'county': ['Emmons County'], + 'country': ['United States'] + }, 'category': [ 'education' ], @@ -251,23 +325,25 @@ module.exports = [ 'lon': -81.532392, 'lat': 40.597578 }, - 'address': {}, - 'local_admin': 'Franklin', - 'admin1_abbr': 'OH', + 'address_parts': {}, 'name': { 'default': 'Strasburg High School' }, - 'admin1': 'Ohio', - 'locality': 'Strasburg', - 'alpha3': 'USA', - 'admin2': 'Tuscarawas County', - 'admin0': 'United States', + 'parent': { + 'localadmin': ['Franklin'], + 'region_a': ['OH'], + 'region': ['Ohio'], + 'locality': ['Strasburg'], + 'country_a': ['USA'], + 'county': ['Tuscarawas County'], + 'country': ['United States'] + }, 'category': [ 'education' ], '_id': '356646971', - '_type': 'osmway', + '_type': 'venue', '_score': 0.9724125, 'confidence': 0.649 } -]; \ No newline at end of file +]; diff --git a/test/unit/fixture/reverse_with_boundary_country.js b/test/unit/fixture/reverse_with_boundary_country.js index 8d739eaa..789cadaf 100644 --- a/test/unit/fixture/reverse_with_boundary_country.js +++ b/test/unit/fixture/reverse_with_boundary_country.js @@ -8,7 +8,7 @@ module.exports = { 'must': [ { 'match': { - 'alpha3': { + 'parent.country_a': { 'analyzer': 'standard', 'query': 'ABC' } diff --git a/test/unit/fixture/reverse_with_source_filtering.js b/test/unit/fixture/reverse_with_source_filtering.js new file mode 100644 index 00000000..28fce18b --- /dev/null +++ b/test/unit/fixture/reverse_with_source_filtering.js @@ -0,0 +1,51 @@ +var vs = require('../../../query/reverse_defaults'); + +module.exports = { + 'query': { + 'filtered': { + 'query': { + 'bool': { + 'must': [] + } + }, + 'filter': { + 'bool': { + 'must': [ + { + 'geo_distance': { + 'distance': '500km', + 'distance_type': 'plane', + 'optimize_bbox': 'indexed', + '_cache': true, + 'center_point': { + 'lat': 29.49136, + 'lon': -82.50622 + } + } + }, + { + 'terms': { + 'source': ['test'] + } + } + ] + } + } + } + }, + '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_boundary_country.js b/test/unit/fixture/search_boundary_country.js index 3e6f83bc..4bf45315 100644 --- a/test/unit/fixture/search_boundary_country.js +++ b/test/unit/fixture/search_boundary_country.js @@ -7,7 +7,7 @@ module.exports = { 'must': [ { 'match': { - 'alpha3': { + 'parent.country_a': { 'analyzer': 'standard', 'query': 'ABC' } @@ -49,15 +49,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 }] @@ -78,15 +74,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, 'weight': 2 }] diff --git a/test/unit/fixture/search_full_address.js b/test/unit/fixture/search_full_address.js index 316903e7..8a8290ab 100644 --- a/test/unit/fixture/search_full_address.js +++ b/test/unit/fixture/search_full_address.js @@ -41,15 +41,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 }] @@ -70,22 +66,18 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, 'weight': 2 }] } },{ 'match': { - 'address.number': { + 'address_parts.number': { 'query': '123', 'boost': vs['address:housenumber:boost'], 'analyzer': vs['address:housenumber:analyzer'] @@ -93,7 +85,7 @@ module.exports = { } }, { 'match': { - 'address.street': { + 'address_parts.street': { 'query': 'main st', 'boost': vs['address:street:boost'], 'analyzer': vs['address:street:analyzer'] @@ -101,7 +93,7 @@ module.exports = { } }, { 'match': { - 'address.zip': { + 'address_parts.zip': { 'query': '10010', 'boost': vs['address:postcode:boost'], 'analyzer': vs['address:postcode:analyzer'] @@ -109,55 +101,55 @@ module.exports = { } }, { 'match': { - 'alpha3': { - 'query': 'USA', - 'boost': vs['admin:alpha3:boost'], - 'analyzer': vs['admin:alpha3:analyzer'] + 'parent.country': { + 'query': 'new york', + 'boost': vs['admin:country:boost'], + 'analyzer': vs['admin:country:analyzer'] } } }, { 'match': { - 'admin0': { - 'query': 'new york', - 'boost': vs['admin:admin0:boost'], - 'analyzer': vs['admin:admin0:analyzer'] + 'parent.country_a': { + 'query': 'USA', + 'boost': vs['admin:country_a:boost'], + 'analyzer': vs['admin:country_a:analyzer'] } } }, { 'match': { - 'admin1': { + 'parent.region': { 'query': 'new york', - 'boost': vs['admin:admin1:boost'], - 'analyzer': vs['admin:admin1:analyzer'] + 'boost': vs['admin:region:boost'], + 'analyzer': vs['admin:region:analyzer'] } } }, { 'match': { - 'admin1_abbr': { + 'parent.region_a': { 'query': 'NY', - 'boost': vs['admin:admin1_abbr:boost'], - 'analyzer': vs['admin:admin1_abbr:analyzer'] + 'boost': vs['admin:region_a:boost'], + 'analyzer': vs['admin:region_a:analyzer'] } } }, { 'match': { - 'admin2': { + 'parent.county': { 'query': 'new york', - 'boost': vs['admin:admin2:boost'], - 'analyzer': vs['admin:admin2:analyzer'] + 'boost': vs['admin:county:boost'], + 'analyzer': vs['admin:county:analyzer'] } } }, { 'match': { - 'local_admin': { + 'parent.localadmin': { 'query': 'new york', - 'boost': vs['admin:local_admin:boost'], - 'analyzer': vs['admin:local_admin:analyzer'] + 'boost': vs['admin:localadmin:boost'], + 'analyzer': vs['admin:localadmin:analyzer'] } } }, { 'match': { - 'locality': { + 'parent.locality': { 'query': 'new york', 'boost': vs['admin:locality:boost'], 'analyzer': vs['admin:locality:analyzer'] @@ -165,10 +157,10 @@ module.exports = { } }, { 'match': { - 'neighborhood': { + 'parent.neighbourhood': { 'query': 'new york', - 'boost': vs['admin:neighborhood:boost'], - 'analyzer': vs['admin:neighborhood:analyzer'] + 'boost': vs['admin:neighbourhood:boost'], + 'analyzer': vs['admin:neighbourhood:analyzer'] } } }] diff --git a/test/unit/fixture/search_linguistic_bbox.js b/test/unit/fixture/search_linguistic_bbox.js index c9859aeb..5bb5907c 100644 --- a/test/unit/fixture/search_linguistic_bbox.js +++ b/test/unit/fixture/search_linguistic_bbox.js @@ -39,15 +39,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 }] @@ -68,15 +64,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, 'weight': 2 }] diff --git a/test/unit/fixture/search_linguistic_focus.js b/test/unit/fixture/search_linguistic_focus.js index 562f32c3..5d03d66d 100644 --- a/test/unit/fixture/search_linguistic_focus.js +++ b/test/unit/fixture/search_linguistic_focus.js @@ -69,15 +69,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 }] @@ -98,15 +94,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, 'weight': 2 }] diff --git a/test/unit/fixture/search_linguistic_focus_bbox.js b/test/unit/fixture/search_linguistic_focus_bbox.js index bd7c40ab..96fe92f6 100644 --- a/test/unit/fixture/search_linguistic_focus_bbox.js +++ b/test/unit/fixture/search_linguistic_focus_bbox.js @@ -69,15 +69,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 }] @@ -98,15 +94,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, 'weight': 2 }] diff --git a/test/unit/fixture/search_linguistic_focus_null_island.js b/test/unit/fixture/search_linguistic_focus_null_island.js index cf8b1625..0924475d 100644 --- a/test/unit/fixture/search_linguistic_focus_null_island.js +++ b/test/unit/fixture/search_linguistic_focus_null_island.js @@ -69,15 +69,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 }] @@ -98,15 +94,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, 'weight': 2 }] diff --git a/test/unit/fixture/search_linguistic_only.js b/test/unit/fixture/search_linguistic_only.js index 53ece28e..58c05826 100644 --- a/test/unit/fixture/search_linguistic_only.js +++ b/test/unit/fixture/search_linguistic_only.js @@ -39,15 +39,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 }] @@ -68,15 +64,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, 'weight': 2 }] diff --git a/test/unit/fixture/search_linguistic_viewport.js b/test/unit/fixture/search_linguistic_viewport.js index 7cf6d138..be76ab05 100644 --- a/test/unit/fixture/search_linguistic_viewport.js +++ b/test/unit/fixture/search_linguistic_viewport.js @@ -77,18 +77,14 @@ module.exports = { { 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 } ], 'score_mode': 'first', - 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - } + 'boost_mode': 'replace' } }, { @@ -109,18 +105,14 @@ module.exports = { { 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, 'weight': 2 } ], 'score_mode': 'first', - 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - } + 'boost_mode': 'replace' } } ] diff --git a/test/unit/fixture/search_linguistic_viewport_min_diagonal.js b/test/unit/fixture/search_linguistic_viewport_min_diagonal.js index c5306919..cf44d0d8 100644 --- a/test/unit/fixture/search_linguistic_viewport_min_diagonal.js +++ b/test/unit/fixture/search_linguistic_viewport_min_diagonal.js @@ -77,18 +77,14 @@ module.exports = { { 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 } ], 'score_mode': 'first', - 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - } + 'boost_mode': 'replace' } }, { @@ -109,18 +105,14 @@ module.exports = { { 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, 'weight': 2 } ], 'score_mode': 'first', - 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - } + 'boost_mode': 'replace' } } ] diff --git a/test/unit/fixture/search_partial_address.js b/test/unit/fixture/search_partial_address.js index 745598da..6c4174b6 100644 --- a/test/unit/fixture/search_partial_address.js +++ b/test/unit/fixture/search_partial_address.js @@ -41,15 +41,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 }] @@ -70,62 +66,58 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, 'weight': 2 }] } },{ 'match': { - 'admin0': { + 'parent.country': { 'query': 'new york', - 'boost': vs['admin:admin0:boost'], - 'analyzer': vs['admin:admin0:analyzer'] + 'boost': vs['admin:country:boost'], + 'analyzer': vs['admin:country:analyzer'] } } }, { 'match': { - 'admin1': { + 'parent.region': { 'query': 'new york', - 'boost': vs['admin:admin1:boost'], - 'analyzer': vs['admin:admin1:analyzer'] + 'boost': vs['admin:region:boost'], + 'analyzer': vs['admin:region:analyzer'] } } }, { 'match': { - 'admin1_abbr': { + 'parent.region_a': { 'query': 'new york', - 'boost': vs['admin:admin1_abbr:boost'], - 'analyzer': vs['admin:admin1_abbr:analyzer'] + 'boost': vs['admin:region_a:boost'], + 'analyzer': vs['admin:region_a:analyzer'] } } }, { 'match': { - 'admin2': { + 'parent.county': { 'query': 'new york', - 'boost': vs['admin:admin2:boost'], - 'analyzer': vs['admin:admin2:analyzer'] + 'boost': vs['admin:county:boost'], + 'analyzer': vs['admin:county:analyzer'] } } }, { 'match': { - 'local_admin': { + 'parent.localadmin': { 'query': 'new york', - 'boost': vs['admin:local_admin:boost'], - 'analyzer': vs['admin:local_admin:analyzer'] + 'boost': vs['admin:localadmin:boost'], + 'analyzer': vs['admin:localadmin:analyzer'] } } }, { 'match': { - 'locality': { + 'parent.locality': { 'query': 'new york', 'boost': vs['admin:locality:boost'], 'analyzer': vs['admin:locality:analyzer'] @@ -133,10 +125,10 @@ module.exports = { } }, { 'match': { - 'neighborhood': { + 'parent.neighbourhood': { 'query': 'new york', - 'boost': vs['admin:neighborhood:boost'], - 'analyzer': vs['admin:neighborhood:analyzer'] + 'boost': vs['admin:neighbourhood:boost'], + 'analyzer': vs['admin:neighbourhood:analyzer'] } } }] diff --git a/test/unit/fixture/search_regions_address.js b/test/unit/fixture/search_regions_address.js index 51bb57cc..e0c05f3c 100644 --- a/test/unit/fixture/search_regions_address.js +++ b/test/unit/fixture/search_regions_address.js @@ -41,15 +41,11 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'popularity' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'popularity' + 'field': 'popularity', + 'missing': 1 }, 'weight': 1 }] @@ -70,22 +66,18 @@ module.exports = { 'max_boost': 20, 'score_mode': 'first', 'boost_mode': 'replace', - 'filter': { - 'exists': { - 'field': 'population' - } - }, 'functions': [{ 'field_value_factor': { 'modifier': 'log1p', - 'field': 'population' + 'field': 'population', + 'missing': 1 }, 'weight': 2 }] } },{ 'match': { - 'address.number': { + 'address_parts.number': { 'query': '1', 'boost': vs['address:housenumber:boost'], 'analyzer': vs['address:housenumber:analyzer'] @@ -93,7 +85,7 @@ module.exports = { } }, { 'match': { - 'address.street': { + 'address_parts.street': { 'query': 'water st', 'boost': vs['address:street:boost'], 'analyzer': vs['address:street:analyzer'] @@ -101,47 +93,47 @@ module.exports = { } }, { 'match': { - 'admin0': { + 'parent.country': { 'query': 'manhattan', - 'boost': vs['admin:admin0:boost'], - 'analyzer': vs['admin:admin0:analyzer'] + 'boost': vs['admin:country:boost'], + 'analyzer': vs['admin:country:analyzer'] } } }, { 'match': { - 'admin1': { + 'parent.region': { 'query': 'manhattan', - 'boost': vs['admin:admin1:boost'], - 'analyzer': vs['admin:admin1:analyzer'] + 'boost': vs['admin:region:boost'], + 'analyzer': vs['admin:region:analyzer'] } } }, { 'match': { - 'admin1_abbr': { + 'parent.region_a': { 'query': 'NY', - 'boost': vs['admin:admin1_abbr:boost'], - 'analyzer': vs['admin:admin1_abbr:analyzer'] + 'boost': vs['admin:region_a:boost'], + 'analyzer': vs['admin:region_a:analyzer'] } } }, { 'match': { - 'admin2': { + 'parent.county': { 'query': 'manhattan', - 'boost': vs['admin:admin2:boost'], - 'analyzer': vs['admin:admin2:analyzer'] + 'boost': vs['admin:county:boost'], + 'analyzer': vs['admin:county:analyzer'] } } }, { 'match': { - 'local_admin': { + 'parent.localadmin': { 'query': 'manhattan', - 'boost': vs['admin:local_admin:boost'], - 'analyzer': vs['admin:local_admin:analyzer'] + 'boost': vs['admin:localadmin:boost'], + 'analyzer': vs['admin:localadmin:analyzer'] } } }, { 'match': { - 'locality': { + 'parent.locality': { 'query': 'manhattan', 'boost': vs['admin:locality:boost'], 'analyzer': vs['admin:locality:analyzer'] @@ -149,10 +141,10 @@ module.exports = { } }, { 'match': { - 'neighborhood': { + 'parent.neighbourhood': { 'query': 'manhattan', - 'boost': vs['admin:neighborhood:boost'], - 'analyzer': vs['admin:neighborhood:analyzer'] + 'boost': vs['admin:neighbourhood:boost'], + 'analyzer': vs['admin:neighbourhood:analyzer'] } } }] diff --git a/test/unit/helper/adminFields.js b/test/unit/helper/adminFields.js deleted file mode 100644 index 8780b267..00000000 --- a/test/unit/helper/adminFields.js +++ /dev/null @@ -1,84 +0,0 @@ -var adminFields = require('../../../helper/adminFields'); - -module.exports.tests = {}; - -module.exports.tests.interface = function(test, common) { - test('validate fields', function(t) { - t.assert(adminFields instanceof Function, 'adminFields is a function'); - t.assert(adminFields() instanceof Array, 'adminFields() returns an array'); - t.assert(adminFields().length > 0, 'adminFields array is not empty'); - t.end(); - }); -}; - -module.exports.tests.lookupExistance = function(test, common) { - test('all expected fields in schema', function(t) { - - var expectedFields = [ - 'one', - 'two', - 'three', - 'four' - ]; - var schema = { mappings: { _default_: { properties: {} } } }; - - // inject all expected fields into schema mock - expectedFields.forEach(function (field) { - schema.mappings._default_.properties[field] = {}; - }); - - var res = adminFields(schema, expectedFields); - - t.deepEquals(res, expectedFields, 'all expected fields are returned'); - t.end(); - }); - - test('some expected fields in schema', function(t) { - - var expectedFields = [ - 'one', - 'two', - 'three', - 'four' - ]; - var schema = { mappings: { _default_: { properties: {} } } }; - - // inject only some of the expected fields into schema mock - expectedFields.slice(0, 3).forEach(function (field) { - schema.mappings._default_.properties[field] = {}; - }); - - var res = adminFields(schema, expectedFields); - - t.deepEquals(res, expectedFields.slice(0, 3), 'only matching expected fields are returned'); - t.end(); - }); - - test('no expected fields in schema', function(t) { - - var schema = { mappings: { _default_: { properties: { foo: {} } } } }; - - var logErrorCalled = false; - var logger = { - error: function () { - logErrorCalled = true; - }}; - - var res = adminFields(schema, undefined, logger); - - t.deepEquals(res, [], 'no admin fields found'); - t.assert(logErrorCalled, 'log error called'); - t.end(); - }); -}; - -module.exports.all = function (tape, common) { - - function test(name, testFunction) { - return tape('adminFields: ' + name, testFunction); - } - - for( var testCase in module.exports.tests ){ - module.exports.tests[testCase](test, common); - } -}; \ No newline at end of file diff --git a/test/unit/helper/geojsonify.js b/test/unit/helper/geojsonify.js index a66421de..ea784b62 100644 --- a/test/unit/helper/geojsonify.js +++ b/test/unit/helper/geojsonify.js @@ -17,6 +17,8 @@ module.exports.tests.earth = function(test, common) { var earth = [{ '_type': 'geoname', '_id': '6295630', + 'source': 'whosonfirst', + 'layer': 'continent', 'name': { 'default': 'Earth' }, @@ -40,7 +42,9 @@ module.exports.tests.search = function(test, common) { var input = [ { '_id': 'id1', - '_type': 'type1', + '_type': 'layer1', + 'source': 'source1', + 'layer': 'layer1', 'center_point': { 'lat': 51.5337144, 'lon': -0.1069716 @@ -59,12 +63,6 @@ module.exports.tests.search = function(test, common) { 'localadmin': 'test1', 'locality': 'test2', 'neighbourhood': 'test3', - 'suggest': { - 'input': [ - '\'round midnight jazz and blues bar' - ], - 'output': 'osmnode:2208150035' - }, 'category': [ 'food', 'nightlife' @@ -72,7 +70,9 @@ module.exports.tests.search = function(test, common) { }, { '_id': 'id2', - '_type': 'type2', + '_type': 'layer2', + 'source': 'source2', + 'layer': 'layer2', 'name': { 'default': 'Blues Cafe' }, @@ -88,16 +88,12 @@ module.exports.tests.search = function(test, common) { 'localadmin': 'test1', 'locality': 'test2', 'neighbourhood': 'test3', - 'suggest': { - 'input': [ - 'blues cafe' - ], - 'output': 'osmway:147495160' - } }, { - '_id': '34633854', - '_type': 'osmway', + '_id': 'node:34633854', + '_type': 'venue', + 'source': 'openstreetmap', + 'layer': 'venue', 'name': { 'default': 'Empire State Building' }, @@ -113,12 +109,6 @@ module.exports.tests.search = function(test, common) { 'localadmin': 'Manhattan', 'locality': 'New York', 'neighbourhood': 'Koreatown', - 'suggest': { - 'input': [ - 'empire state building' - ], - 'output': 'osmway:34633854' - }, 'category': [ 'tourism', 'transport' @@ -141,9 +131,9 @@ module.exports.tests.search = function(test, common) { }, 'properties': { 'id': 'id1', - 'gid': 'type1:type1:id1', - 'layer': 'type1', - 'source': 'type1', + 'gid': 'source1:layer1:id1', + 'layer': 'layer1', + 'source': 'source1', 'label': '\'Round Midnight Jazz and Blues Bar, test3, Angel', 'name': '\'Round Midnight Jazz and Blues Bar', 'country_a': 'GBR', @@ -170,9 +160,9 @@ module.exports.tests.search = function(test, common) { }, 'properties': { 'id': 'id2', - 'gid': 'type2:type2:id2', - 'layer': 'type2', - 'source': 'type2', + 'gid': 'source2:layer2:id2', + 'layer': 'layer2', + 'source': 'source2', 'label': 'Blues Cafe, test3, Smithfield', 'name': 'Blues Cafe', 'country_a': 'GBR', @@ -195,11 +185,11 @@ module.exports.tests.search = function(test, common) { ] }, 'properties': { - 'id': '34633854', - 'gid': 'osm:venue:34633854', + 'id': 'node:34633854', + 'gid': 'openstreetmap:venue:node:34633854', 'layer': 'venue', - 'source': 'osm', - 'label': 'Empire State Building, Manhattan, NY', + 'source': 'openstreetmap', + 'label': 'Empire State Building, Manhattan, NY, USA', 'name': 'Empire State Building', 'country_a': 'USA', 'country': 'United States', @@ -219,6 +209,150 @@ module.exports.tests.search = function(test, common) { t.deepEqual(json, expected, 'all docs mapped'); t.end(); }); + + test('filtering out empty items', function (t) { + var input = [ + { + 'bounding_box': { + 'min_lat': 40.6514712164, + 'max_lat': 40.6737320588, + 'min_lon': -73.8967895508, + 'max_lon': -73.8665771484 + }, + 'locality': [ + 'New York' + ], + 'source': 'whosonfirst', + 'layer': 'neighbourhood', + 'population': 173198, + 'popularity': 495, + 'center_point': { + 'lon': -73.881319, + 'lat': 40.663303 + }, + 'name': { + 'default': 'East New York' + }, + 'source_id': '85816607', + 'category': [], + '_id': '85816607', + '_type': 'neighbourhood', + '_score': 21.434, + 'confidence': 0.888, + 'country': [ + 'United States' + ], + 'country_gid': [ + '85633793' + ], + 'country_a': [ + 'USA' + ], + 'macroregion': [ + 'MacroRegion Name' + ], + 'macroregion_gid': [ + 'MacroRegion Id' + ], + 'macroregion_a': [ + 'MacroRegion Abbreviation' + ], + 'region': [ + 'New York' + ], + 'region_gid': [ + '85688543' + ], + 'region_a': [ + 'NY' + ], + 'macrocounty': [ + 'MacroCounty Name' + ], + 'macrocounty_gid': [ + 'MacroCounty Id' + ], + 'macrocounty_a': [ + 'MacroCounty Abbreviation' + ], + 'county': [ + 'Kings County' + ], + 'county_gid': [ + '102082361' + ], + 'county_a': [ + null + ], + 'localadmin': [ + 'Brooklyn' + ], + 'localadmin_gid': [ + '404521211' + ], + 'localadmin_a': [ + null + ], + 'locality_gid': [ + '85977539' + ], + 'locality_a': [ + null + ], + 'neighbourhood': [], + 'neighbourhood_gid': [] + } + ]; + + var expected = { + 'type': 'FeatureCollection', + 'bbox': [-73.8967895508, 40.6514712164, -73.8665771484, 40.6737320588], + 'features': [ + { + 'type': 'Feature', + 'properties': { + 'id': '85816607', + 'gid': 'whosonfirst:neighbourhood:85816607', + 'layer': 'neighbourhood', + 'source': 'whosonfirst', + 'name': 'East New York', + 'confidence': 0.888, + 'country': 'United States', + 'country_gid': '85633793', + 'country_a': 'USA', + 'macroregion': 'MacroRegion Name', + 'macroregion_gid': 'MacroRegion Id', + 'macroregion_a': 'MacroRegion Abbreviation', + 'region': 'New York', + 'region_gid': '85688543', + 'region_a': 'NY', + 'macrocounty': 'MacroCounty Name', + 'macrocounty_gid': 'MacroCounty Id', + 'macrocounty_a': 'MacroCounty Abbreviation', + 'county': 'Kings County', + 'county_gid': '102082361', + 'localadmin': 'Brooklyn', + 'localadmin_gid': '404521211', + 'locality': 'New York', + 'locality_gid': '85977539', + 'label': 'East New York, Brooklyn, NY, USA' + }, + 'bbox': [-73.8967895508,40.6514712164,-73.8665771484,40.6737320588], + 'geometry': { + 'type': 'Point', + 'coordinates': [ + -73.881319, + 40.663303 + ] + } + } + ] + }; + + var json = geojsonify.search( input ); + t.deepEqual(json, expected, 'all wanted properties exposed'); + t.end(); + }); }; module.exports.all = function (tape, common) { diff --git a/test/unit/helper/labelGenerator_GBR.js b/test/unit/helper/labelGenerator_GBR.js new file mode 100644 index 00000000..a0f74312 --- /dev/null +++ b/test/unit/helper/labelGenerator_GBR.js @@ -0,0 +1,87 @@ + +var generator = require('../../../helper/labelGenerator'); + +module.exports.tests = {}; + +module.exports.tests.interface = function(test, common) { + test('interface', function(t) { + t.equal(typeof generator, 'function', 'valid function'); + t.end(); + }); +}; + +// GBR street address +module.exports.tests.one_main_street_uk = function(test, common) { + test('one main street uk', function(t) { + var doc = { + 'name': '1 Main St', + 'housenumber': '1', + 'street': 'Main St', + 'postalcode': 'BT77 0BG', + 'country_a': 'GBR', + 'country': 'United Kingdom', + 'region': 'Dungannon' + }; + t.equal(generator(doc),'1 Main St, Dungannon, United Kingdom'); + t.end(); + }); +}; + +// GBR venue +module.exports.tests.hackney_city_farm = function(test, common) { + test('hackney city farm', function(t) { + var doc = { + 'name': 'Hackney City Farm', + 'country_a': 'GBR', + 'country': 'United Kingdom', + 'region': 'Hackney', + 'county': 'Greater London', + 'locality': 'London', + 'neighbourhood': 'Haggerston' + }; + t.equal(generator(doc),'Hackney City Farm, Haggerston, Greater London'); + t.end(); + }); +}; + +// GBR country +module.exports.tests.wales = function(test, common) { + test('wales', function(t) { + var doc = { + 'name': 'Wales', + 'country_a': 'GBR', + 'country': 'United Kingdom', + 'region': 'Wales' + }; + t.equal(generator(doc),'Wales, United Kingdom'); + t.end(); + }); +}; + +// GBR macroregion +module.exports.tests.macroregion_trumps_region = function(test, common) { + test('macroregion should trump region when none of neighbourhood, county, localadmin, locality are available', function(t) { + var doc = { + 'name': 'Name', + 'country_a': 'GBR', + 'country': 'Country Name', + 'macroregion': 'Macroregion Name', + 'region': 'Region Name' + }; + + t.equal(generator(doc), 'Name, Macroregion Name, Country Name'); + t.end(); + + }); +}; + +module.exports.all = function (tape, common) { + + function test(name, testFunction) { + return tape('label generator (GBR): ' + name, testFunction); + } + + for( var testCase in module.exports.tests ){ + module.exports.tests[testCase](test, common); + } +}; diff --git a/test/unit/helper/labelGenerator_SGP.js b/test/unit/helper/labelGenerator_SGP.js new file mode 100644 index 00000000..622f8915 --- /dev/null +++ b/test/unit/helper/labelGenerator_SGP.js @@ -0,0 +1,51 @@ + +var generator = require('../../../helper/labelGenerator'); + +module.exports.tests = {}; + +module.exports.tests.interface = function(test, common) { + test('interface', function(t) { + t.equal(typeof generator, 'function', 'valid function'); + t.end(); + }); +}; + +// SGP region +module.exports.tests.north_west_singapore = function(test, common) { + test('north west singapore', function(t) { + var doc = { + 'name': 'North West', + 'country_a': 'SGP', + 'country': 'Singapore', + 'region': 'North West' + }; + t.equal(generator(doc),'North West, Singapore'); + t.end(); + }); +}; + +// SGP venue +module.exports.tests.singapore_mcdonalds = function(test, common) { + test('singapore_mcdonalds', function(t) { + var doc = { + 'name': 'McDonald\'s', + 'country_a': 'SGP', + 'country': 'Singapore', + 'region': 'Central Singapore', + 'locality': 'Singapore' + }; + t.equal(generator(doc),'McDonald\'s, Central Singapore, Singapore'); + t.end(); + }); +}; + +module.exports.all = function (tape, common) { + + function test(name, testFunction) { + return tape('label generator: ' + name, testFunction); + } + + for( var testCase in module.exports.tests ){ + module.exports.tests[testCase](test, common); + } +}; diff --git a/test/unit/helper/labelGenerator_SWE.js b/test/unit/helper/labelGenerator_SWE.js new file mode 100644 index 00000000..e3f8534d --- /dev/null +++ b/test/unit/helper/labelGenerator_SWE.js @@ -0,0 +1,53 @@ + +var generator = require('../../../helper/labelGenerator'); + +module.exports.tests = {}; + +module.exports.tests.interface = function(test, common) { + test('interface', function(t) { + t.equal(typeof generator, 'function', 'valid function'); + t.end(); + }); +}; + +// SWE city +module.exports.tests.skane1 = function(test, common) { + test('skåne 1', function(t) { + var doc = { + 'name': 'Malmö', + 'country_a': 'SWE', + 'country': 'Sweden', + 'region': 'Skåne', + 'county': 'Malmö' + }; + t.equal(generator(doc),'Malmö, Skåne, Sweden'); + t.end(); + }); +}; + +// SWE city +module.exports.tests.skane2 = function(test, common) { + test('skåne 2', function(t) { + var doc = { + 'name': 'Malmö', + 'country_a': 'SWE', + 'country': 'Sweden', + 'region': 'Skåne', + 'county': 'Malmö', + 'locality': 'Malmö' + }; + t.equal(generator(doc),'Malmö, Skåne, Sweden'); + t.end(); + }); +}; + +module.exports.all = function (tape, common) { + + function test(name, testFunction) { + return tape('label generator: ' + name, testFunction); + } + + for( var testCase in module.exports.tests ){ + module.exports.tests[testCase](test, common); + } +}; diff --git a/test/unit/helper/labelGenerator_USA.js b/test/unit/helper/labelGenerator_USA.js new file mode 100644 index 00000000..7791aa24 --- /dev/null +++ b/test/unit/helper/labelGenerator_USA.js @@ -0,0 +1,229 @@ +var generator = require('../../../helper/labelGenerator'); + +module.exports.tests = {}; + +module.exports.tests.interface = function(test, common) { + test('interface', function(t) { + t.equal(typeof generator, 'function', 'valid function'); + t.end(); + }); +}; + +module.exports.tests.localadmin = function(test, common) { + test('localadmin should trump locality, neighbourhood, and county', function(t) { + var doc = { + 'name': 'Default Name', + 'country_a': 'USA', + 'country': 'United States', + 'region': 'Region Name', + 'region_a': 'Region Abbr', + 'county': 'County Name', + 'localadmin': 'LocalAdmin Name', + 'locality': 'Locality Name', + 'neighbourhood': 'Neighbourhood Name' + }; + t.equal(generator(doc),'Default Name, LocalAdmin Name, Region Abbr, USA'); + t.end(); + }); +}; + +module.exports.tests.locality = function(test, common) { + test('locality should trump neighbourhood and county when localadmin not available', function(t) { + var doc = { + 'name': 'Default Name', + 'country_a': 'USA', + 'country': 'United States', + 'region': 'Region Name', + 'region_a': 'Region Abbr', + 'county': 'County Name', + 'locality': 'Locality Name', + 'neighbourhood': 'Neighbourhood Name' + }; + t.equal(generator(doc),'Default Name, Locality Name, Region Abbr, USA'); + t.end(); + }); +}; + +module.exports.tests.neighbourhood = function(test, common) { + test('neighbourhood should trump county when neither localadmin nor locality', function(t) { + var doc = { + 'name': 'Default Name', + 'country_a': 'USA', + 'country': 'United States', + 'region': 'Region Name', + 'region_a': 'Region Abbr', + 'county': 'County Name', + 'neighbourhood': 'Neighbourhood Name' + }; + t.equal(generator(doc),'Default Name, Neighbourhood Name, Region Abbr, USA'); + t.end(); + }); +}; + +module.exports.tests.county = function(test, common) { + test('county should be used when localadmin, locality, and neighbourhood are not available', function(t) { + var doc = { + 'name': 'Default Name', + 'country_a': 'USA', + 'country': 'United States', + 'region': 'Region Name', + 'region_a': 'Region Abbr', + 'county': 'County Name' + }; + t.equal(generator(doc),'Default Name, County Name, Region Abbr, USA'); + t.end(); + }); +}; + +module.exports.tests.region = function(test, common) { + test('region should be used when region_a is not available', function(t) { + var doc = { + 'name': 'Default Name', + 'country_a': 'USA', + 'country': 'United States', + 'region': 'Region Name' + }; + t.equal(generator(doc),'Default Name, Region Name, USA'); + t.end(); + }); +}; + +// USA geonames state +module.exports.tests.region_geonames = function(test, common) { + test('default name should not be prepended when source=geonames and layer=region', function(t) { + var doc = { + 'name': 'Region Name', + 'country_a': 'USA', + 'country': 'United States', + 'region': 'Region Name', + 'region_a': 'Region Abbr', + 'source': 'geonames', + 'layer': 'region' + }; + t.equal(generator(doc),'Region Name, USA'); + t.end(); + }); +}; + +// USA whosonfirst state +module.exports.tests.region_whosonfirst = function(test, common) { + test('default name should not be prepended when source=whosonfirst and layer=region', function(t) { + var doc = { + 'name': 'Region Name', + 'country_a': 'USA', + 'country': 'United States', + 'region': 'Region Name', + 'region_a': 'Region Abbr', + 'source': 'whosonfirst', + 'layer': 'region' + }; + t.equal(generator(doc),'Region Name, USA'); + t.end(); + }); +}; + +// USA non-geonames/whosonfirst state +module.exports.tests.region_other_source = function(test, common) { + test('default name should be prepended when layer=region and source is not whosonfirst or geonames', function(t) { + var doc = { + 'name': 'Default Name', + 'country_a': 'USA', + 'country': 'United States', + 'region': 'Region Name', + 'region_a': 'Region Abbr', + 'source': 'not geonames or whosonfirst', + 'layer': 'region' + }; + t.equal(generator(doc),'Default Name, Region Name, USA',generator(doc)); + t.end(); + }); +}; + +// major USA city +module.exports.tests.san_francisco = function(test, common) { + test('san francisco', function(t) { + var doc = { + 'name': 'San Francisco', + 'country_a': 'USA', + 'country': 'United States', + 'region': 'California', + 'region_a': 'CA', + 'county': 'San Francisco County', + 'locality': 'San Francisco' + }; + t.equal(generator(doc),'San Francisco, San Francisco County, CA, USA'); + t.end(); + }); +}; + +// USA venue +module.exports.tests.nyc_office = function(test, common) { + test('30 West 26th Street', function(t) { + var doc = { + 'name': '30 West 26th Street', + 'housenumber': '30', + 'street': 'West 26th Street', + 'postalcode': '10010', + 'country_a': 'USA', + 'country': 'United States', + 'region': 'New York', + 'region_a': 'NY', + 'county': 'New York County', + 'localadmin': 'Manhattan', + 'locality': 'New York', + 'neighbourhood': 'Flatiron District' + }; + t.equal(generator(doc),'30 West 26th Street, Manhattan, NY, USA'); + t.end(); + }); +}; + +// USA NYC eatery +module.exports.tests.nyc_bakery = function(test, common) { + test('New York Bakery', function(t) { + var doc = { + 'name': 'New York Bakery', + 'housenumber': '51 W', + 'street': '29th', + 'country_a': 'USA', + 'country': 'United States', + 'region': 'New York', + 'region_a': 'NY', + 'county': 'New York County', + 'localadmin': 'Manhattan', + 'locality': 'New York', + 'neighbourhood': 'Koreatown' + }; + t.equal(generator(doc),'New York Bakery, Manhattan, NY, USA'); + t.end(); + }); +}; + +// USA SFC building +module.exports.tests.ferry_building = function(test, common) { + test('Ferry Building', function(t) { + var doc = { + 'name': 'Ferry Building', + 'country_a': 'USA', + 'country': 'United States', + 'region': 'California', + 'region_a': 'CA', + 'county': 'San Francisco County', + 'locality': 'San Francisco', + 'neighbourhood': 'Financial District' + }; + t.equal(generator(doc),'Ferry Building, San Francisco, CA, USA'); + t.end(); + }); +}; + +module.exports.all = function (tape, common) { + + function test(name, testFunction) { + return tape('label generator: ' + name, testFunction); + } + + for( var testCase in module.exports.tests ){ + module.exports.tests[testCase](test, common); + } +}; diff --git a/test/unit/helper/labelGenerator.js b/test/unit/helper/labelGenerator_default.js similarity index 50% rename from test/unit/helper/labelGenerator.js rename to test/unit/helper/labelGenerator_default.js index 9a382397..cbb76f05 100644 --- a/test/unit/helper/labelGenerator.js +++ b/test/unit/helper/labelGenerator_default.js @@ -10,89 +10,11 @@ module.exports.tests.interface = function(test, common) { }); }; -// major USA city -module.exports.tests.san_francisco = function(test, common) { - test('san francisco', function(t) { - var doc = { - 'name': { 'default': 'San Francisco' }, - 'country_a': 'USA', - 'country': 'United States', - 'region': 'California', - 'region_a': 'CA', - 'county': 'San Francisco County', - 'locality': 'San Francisco' - }; - t.equal(generator(doc),'San Francisco, San Francisco County, CA'); - t.end(); - }); -}; - -// USA venue -module.exports.tests.nyc_office = function(test, common) { - test('30 West 26th Street', function(t) { - var doc = { - 'name': { 'default': '30 West 26th Street' }, - 'housenumber': '30', - 'street': 'West 26th Street', - 'postalcode': '10010', - 'country_a': 'USA', - 'country': 'United States', - 'region': 'New York', - 'region_a': 'NY', - 'county': 'New York County', - 'localadmin': 'Manhattan', - 'locality': 'New York', - 'neighbourhood': 'Flatiron District' - }; - t.equal(generator(doc),'30 West 26th Street, Manhattan, NY'); - t.end(); - }); -}; - -// USA NYC eatery -module.exports.tests.nyc_bakery = function(test, common) { - test('New York Bakery', function(t) { - var doc = { - 'name': { 'default': 'New York Bakery' }, - 'housenumber': '51 W', - 'street': '29th', - 'country_a': 'USA', - 'country': 'United States', - 'region': 'New York', - 'region_a': 'NY', - 'county': 'New York County', - 'localadmin': 'Manhattan', - 'locality': 'New York', - 'neighbourhood': 'Koreatown' - }; - t.equal(generator(doc),'New York Bakery, Manhattan, NY'); - t.end(); - }); -}; - -// USA SFC building -module.exports.tests.ferry_building = function(test, common) { - test('Ferry Building', function(t) { - var doc = { - 'name': { 'default': 'Ferry Building' }, - 'country_a': 'USA', - 'country': 'United States', - 'region': 'California', - 'region_a': 'CA', - 'county': 'San Francisco County', - 'locality': 'San Francisco', - 'neighbourhood': 'Financial District' - }; - t.equal(generator(doc),'Ferry Building, San Francisco, CA'); - t.end(); - }); -}; - // AUS state module.exports.tests.new_south_wales = function(test, common) { test('new south wales', function(t) { var doc = { - 'name': { 'default': 'New South Wales' }, + 'name': 'New South Wales', 'country_a': 'AUS', 'country': 'Australia', 'region': 'New South Wales' @@ -102,26 +24,11 @@ module.exports.tests.new_south_wales = function(test, common) { }); }; -// USA state -module.exports.tests.california = function(test, common) { - test('california', function(t) { - var doc = { - 'name': { 'default': 'California' }, - 'country_a': 'USA', - 'country': 'United States', - 'region': 'California', - 'region_a': 'CA' - }; - t.equal(generator(doc),'California, CA'); - t.end(); - }); -}; - // IND state module.exports.tests.west_bengal = function(test, common) { test('west bengal', function(t) { var doc = { - 'name': { 'default': 'West Bengal' }, + 'name': 'West Bengal', 'country_a': 'IND', 'country': 'India', 'region': 'West Bengal' @@ -135,7 +42,7 @@ module.exports.tests.west_bengal = function(test, common) { module.exports.tests.bangalore = function(test, common) { test('bangalore', function(t) { var doc = { - 'name': { 'default': 'Bangalore' }, + 'name': 'Bangalore', 'country_a': 'IND', 'country': 'India', 'region': 'Karnataka', @@ -151,7 +58,7 @@ module.exports.tests.bangalore = function(test, common) { module.exports.tests.sarjapur = function(test, common) { test('Sarjapur', function(t) { var doc = { - 'name': { 'default': 'Sarjapur' }, + 'name': 'Sarjapur', 'country_a': 'IND', 'country': 'India', 'region': 'Karnataka' @@ -165,7 +72,7 @@ module.exports.tests.sarjapur = function(test, common) { module.exports.tests.bengaluru_east = function(test, common) { test('Bengaluru East', function(t) { var doc = { - 'name': { 'default': 'Bengaluru East' }, + 'name': 'Bengaluru East', 'country_a': 'IND', 'country': 'India', 'region': 'Karnataka', @@ -183,7 +90,7 @@ module.exports.tests.bengaluru_east = function(test, common) { module.exports.tests.wellington_victoria = function(test, common) { test('Wellington, Victoria, Australia', function(t) { var doc = { - 'name': { 'default': 'Wellington' }, + 'name': 'Wellington', 'country_a': 'AUS', 'country': 'Australia', 'region': 'Victoria', @@ -194,25 +101,11 @@ module.exports.tests.wellington_victoria = function(test, common) { }); }; -// SGP region -module.exports.tests.north_west_singapore = function(test, common) { - test('north west singapore', function(t) { - var doc = { - 'name': { 'default': 'North West' }, - 'country_a': 'SGP', - 'country': 'Singapore', - 'region': 'North West' - }; - t.equal(generator(doc),'North West, Singapore'); - t.end(); - }); -}; - // IRQ region module.exports.tests.arbil = function(test, common) { test('arbil', function(t) { var doc = { - 'name': { 'default': 'Arbil' }, + 'name': 'Arbil', 'country_a': 'IRQ', 'country': 'Iraq', 'region': 'Arbil' @@ -226,7 +119,7 @@ module.exports.tests.arbil = function(test, common) { module.exports.tests.madrid = function(test, common) { test('madrid', function(t) { var doc = { - 'name': { 'default': 'Madrid' }, + 'name': 'Madrid', 'country_a': 'ESP', 'country': 'Spain', 'region': 'Madrid' @@ -236,76 +129,11 @@ module.exports.tests.madrid = function(test, common) { }); }; -// SWE city -module.exports.tests.skane1 = function(test, common) { - test('skåne 1', function(t) { - var doc = { - 'name': { 'default': 'Malmö' }, - 'country_a': 'SWE', - 'country': 'Sweden', - 'region': 'Skåne', - 'county': 'Malmö' - }; - t.equal(generator(doc),'Malmö, Skåne, Sweden'); - t.end(); - }); -}; - -// SWE city -module.exports.tests.skane2 = function(test, common) { - test('skåne 2', function(t) { - var doc = { - 'name': { 'default': 'Malmö' }, - 'country_a': 'SWE', - 'country': 'Sweden', - 'region': 'Skåne', - 'county': 'Malmö', - 'locality': 'Malmö' - }; - t.equal(generator(doc),'Malmö, Skåne, Sweden'); - t.end(); - }); -}; - -// GBR street address -module.exports.tests.one_main_street_uk = function(test, common) { - test('one main street uk', function(t) { - var doc = { - 'name': { 'default': '1 Main St' }, - 'housenumber': '1', - 'street': 'Main St', - 'postalcode': 'BT77 0BG', - 'country_a': 'GBR', - 'country': 'United Kingdom', - 'region': 'Dungannon' - }; - t.equal(generator(doc),'1 Main St, Dungannon, United Kingdom'); - t.end(); - }); -}; - -// GBR venue -module.exports.tests.hackney_city_farm = function(test, common) { - test('hackney city farm', function(t) { - var doc = { - 'name': { 'default': 'Hackney City Farm' }, - 'country_a': 'GBR', - 'country': 'United Kingdom', - 'region': 'Hackney', - 'county': 'Greater London', - 'locality': 'London', - 'neighbourhood': 'Haggerston' - }; - t.equal(generator(doc),'Hackney City Farm, Haggerston, Greater London'); - t.end(); - }); -}; - // DEU street address module.exports.tests.one_grolmanstrasse = function(test, common) { test('one grolmanstrasse', function(t) { var doc = { - 'name': { 'default': '1 Grolmanstraße' }, + 'name': '1 Grolmanstraße', 'housenumber': '1', 'street': 'Grolmanstraße', 'postalcode': '10623', @@ -325,7 +153,7 @@ module.exports.tests.one_grolmanstrasse = function(test, common) { module.exports.tests.new_zealand = function(test, common) { test('new zealand', function(t) { var doc = { - 'name': { 'default': 'New Zealand' }, + 'name': 'New Zealand', 'country_a': 'NZL', 'country': 'New Zealand' }; @@ -334,25 +162,11 @@ module.exports.tests.new_zealand = function(test, common) { }); }; -// GBR country -module.exports.tests.wales = function(test, common) { - test('wales', function(t) { - var doc = { - 'name': { 'default': 'Wales' }, - 'country_a': 'GBR', - 'country': 'United Kingdom', - 'region': 'Wales' - }; - t.equal(generator(doc),'Wales, United Kingdom'); - t.end(); - }); -}; - // IRL country module.exports.tests.republic_of_ireland = function(test, common) { test('northern ireland', function(t) { var doc = { - 'name': { 'default': 'Ireland' }, + 'name': 'Ireland', 'country_a': 'IRL', 'country': 'Ireland' }; @@ -362,26 +176,11 @@ module.exports.tests.republic_of_ireland = function(test, common) { }); }; -// SGP venue -module.exports.tests.singapore_mcdonalds = function(test, common) { - test('singapore_mcdonalds', function(t) { - var doc = { - 'name': { 'default': 'McDonald\'s' }, - 'country_a': 'SGP', - 'country': 'Singapore', - 'region': 'Central Singapore', - 'locality': 'Singapore' - }; - t.equal(generator(doc),'McDonald\'s, Central Singapore, Singapore'); - t.end(); - }); -}; - // THA province module.exports.tests.krabi_province = function(test, common) { test('Krabi Provence', function(t) { var doc = { - 'name': { 'default': 'Krabi' }, + 'name': 'Krabi', 'country_a': 'THA', 'country': 'Thailand', 'region': 'Krabi' @@ -395,7 +194,7 @@ module.exports.tests.krabi_province = function(test, common) { module.exports.tests.koh_lanta = function(test, common) { test('Koh Lanta', function(t) { var doc = { - 'name': { 'default': 'Ko Lanta' }, + 'name': 'Ko Lanta', 'country_a': 'THA', 'country': 'Thailand', 'region': 'Krabi' @@ -409,7 +208,7 @@ module.exports.tests.koh_lanta = function(test, common) { module.exports.tests.black_dog_cafe = function(test, common) { test('Black Dog Cafe', function(t) { var doc = { - 'name': { 'default': 'Black Dog Cafe' }, + 'name': 'Black Dog Cafe', 'country_a': 'NZL', 'country': 'New Zealand', 'region': 'Auckland Region', @@ -424,7 +223,7 @@ module.exports.tests.black_dog_cafe = function(test, common) { module.exports.tests.beach_bablyon = function(test, common) { test('Beach Bablyon', function(t) { var doc = { - 'name': { 'default': 'Beach Bablyon' }, + 'name': 'Beach Bablyon', 'country_a': 'NZL', 'country': 'New Zealand', 'region': 'Wellington Region', @@ -441,7 +240,7 @@ module.exports.tests.beach_bablyon = function(test, common) { module.exports.tests.waiotapu = function(test, common) { test('Waiotapu', function(t) { var doc = { - 'name': { 'default': 'Waiotapu' }, + 'name': 'Waiotapu', 'country_a': 'NZL', 'country': 'New Zealand', 'region': 'Bay of Plenty', @@ -452,6 +251,57 @@ module.exports.tests.waiotapu = function(test, common) { }); }; + +module.exports.tests.non_us_or_ca_region = function(test, common) { + test('geonames US', function(t) { + var doc = { + 'name': 'Default Name', + 'country_a': 'XYZ', + 'country': 'Full Country Name', + 'region': 'Full Region Name', + 'layer': 'region', + 'source': 'geonames' + }; + + t.equal(generator(doc), 'Default Name, Full Region Name, Full Country Name'); + t.end(); + + }); + + test('whosonfirst US', function(t) { + var doc = { + 'name': 'Default Name', + 'country_a': 'XYZ', + 'country': 'Full Country Name', + 'region': 'Full Region Name', + 'layer': 'region', + 'source': 'whosonfirst' + }; + + t.equal(generator(doc), 'Default Name, Full Region Name, Full Country Name'); + t.end(); + + }); + +}; + +// macroregion +module.exports.tests.macroregion_trumps_region = function(test, common) { + test('macroregion should trump region when none of localadmin, locality, neighbourhood, county are available', function(t) { + var doc = { + 'name': 'Name', + 'country_a': 'Country abbreviation', + 'country': 'Country Name', + 'macroregion': 'Macroregion Name', + 'region': 'Region Name' + }; + + t.equal(generator(doc), 'Name, Macroregion Name, Country Name'); + t.end(); + + }); +}; + module.exports.all = function (tape, common) { function test(name, testFunction) { diff --git a/test/unit/helper/labelSchema.js b/test/unit/helper/labelSchema.js index dcc3cdfa..edbf2b77 100644 --- a/test/unit/helper/labelSchema.js +++ b/test/unit/helper/labelSchema.js @@ -1,5 +1,5 @@ -var schemas = require('../../../helper/labelSchema.json'); +var schemas = require('../../../helper/labelSchema'); var alpha3 = require('../mock/alpha3.json'); module.exports.tests = {}; @@ -12,36 +12,766 @@ module.exports.tests.interface = function(test, common) { }); }; -module.exports.tests.valid = function(test, common) { - var valid_keys = ['localadmin', 'locality', 'neighbourhood', 'county', 'region_a', 'region', 'country']; - var default_schema = { - local: ['localadmin', 'locality', 'neighbourhood', 'county', 'region'], - regional: ['country'] - }; - - var isValid = function(keys, schema) { - test('valid key/object (' + keys + ')' , function(t) { - if (keys === 'default') { - t.deepEqual(schema, default_schema, 'valid default schema'); - } else { - t.equal(alpha3.hasOwnProperty(keys), true, 'valid key'); - } - t.equal(typeof schema, 'object', 'valid object'); - t.notEqual(Object.getOwnPropertyNames(schema).length, 0, 'object not empty'); - for (var levels in schema) { - t.equal(Object.prototype.toString.call(schema[levels]), '[object Array]', levels+' is an array'); - for (var i=0;i 'Grolmanstraße 101' t.equal( res.data[1].name.default, 'Grolmanstraße 23', 'flipped name' ); + // NLD address should have the housenumber and street name flipped, too + // this definition comes from pelias configuration + t.equal( res.data[2].name.default, 'Keizersgracht 117', 'flipped name' ); + t.end(); }); }); diff --git a/test/unit/middleware/normalizeParentIds.js b/test/unit/middleware/normalizeParentIds.js new file mode 100644 index 00000000..472df0e3 --- /dev/null +++ b/test/unit/middleware/normalizeParentIds.js @@ -0,0 +1,91 @@ +var normalizer = require('../../../middleware/normalizeParentIds')(); + +module.exports.tests = {}; + +module.exports.tests.interface = function(test, common) { + test('WOF ids converted to Pelias ids', function(t) { + + var input = { + data: [{ + 'parent': { + 'country': ['United States'], // these shouldn't change + 'country_id': ['85633793'], + 'country_a': ['USA'] + }, + 'country': ['United States'], + 'country_gid': ['85633793'], + 'country_a': ['USA'], + 'macroregion': ['MacroRegion Name'], + 'macroregion_gid': ['foobar'], + 'macroregion_a': ['MacroRegion Abbreviation'], + 'region': ['New York'], + 'region_gid': ['85688543'], + 'region_a': ['NY'], + 'macrocounty': ['MacroCounty Name'], + 'macrocounty_gid': ['~~~~~'], + 'macrocounty_a': ['MacroCounty Abbreviation'], + 'county': ['Kings County'], + 'county_gid': ['102082361'], + 'county_a': [null], + 'localadmin': ['Brooklyn'], + 'localadmin_gid': ['404521211'], + 'localadmin_a': [null], + 'locality': ['Some Locality'], + 'locality_gid': ['85977539'], + 'locality_a': [null], + 'neighbourhood': [], + 'neighbourhood_gid': [] + }] + }; + + var expected = { + data: [{ + 'parent': { + 'country': ['United States'], + 'country_id': ['85633793'], + 'country_a': ['USA'] + }, + 'country': ['United States'], + 'country_gid': ['whosonfirst:country:85633793'], + 'country_a': ['USA'], + 'macroregion': ['MacroRegion Name'], + 'macroregion_gid': ['whosonfirst:macroregion:foobar'], + 'macroregion_a': ['MacroRegion Abbreviation'], + 'region': ['New York'], + 'region_gid': ['whosonfirst:region:85688543'], + 'region_a': ['NY'], + 'macrocounty': ['MacroCounty Name'], + 'macrocounty_gid': ['whosonfirst:macrocounty:~~~~~'], + 'macrocounty_a': ['MacroCounty Abbreviation'], + 'county': ['Kings County'], + 'county_gid': ['whosonfirst:county:102082361'], + 'county_a': [null], + 'localadmin': ['Brooklyn'], + 'localadmin_gid': ['whosonfirst:localadmin:404521211'], + 'localadmin_a': [null], + 'locality': ['Some Locality'], + 'locality_gid': ['whosonfirst:locality:85977539'], + 'locality_a': [null], + 'neighbourhood': [], + 'neighbourhood_gid': [] + }] + }; + + normalizer({}, input, function () { + t.deepEqual(input, expected); + t.end(); + }); + + }); +}; + +module.exports.all = function (tape, common) { + + function test(name, testFunction) { + return tape('[middleware] normalizeParentIds: ' + name, testFunction); + } + + for( var testCase in module.exports.tests ){ + module.exports.tests[testCase](test, common); + } +}; diff --git a/test/unit/middleware/parseBBox.js b/test/unit/middleware/parseBBox.js new file mode 100644 index 00000000..4e2cd802 --- /dev/null +++ b/test/unit/middleware/parseBBox.js @@ -0,0 +1,65 @@ +var parseBBox = require('../../../middleware/parseBBox')(); + +module.exports.tests = {}; + +module.exports.tests.computeDistance = function(test, common) { + test('valid bounding_box json', function(t) { + var res = { + data: [ + { + bounding_box: '{"min_lat":40.6514712164,"max_lat":40.6737320588,"min_lon":-73.8967895508,"max_lon":-73.8665771484}' + } + ] + }; + + var expected = { + data: [ + { + bounding_box: { + min_lat: 40.6514712164, + max_lat: 40.6737320588, + min_lon: -73.8967895508, + max_lon: -73.8665771484 + } + } + ] + }; + + parseBBox({}, res, function () { + t.deepEquals(res, expected, 'correct bounding_box'); + t.end(); + }); + }); + + test('invalid bounding_box json', function(t) { + var res = { + data: [ + { + bounding_box: 'garbage json' + } + ] + }; + + var expected = { + data: [ + {} + ] + }; + + parseBBox({}, res, function () { + t.deepEquals(res, expected, 'correct bounding_box'); + t.end(); + }); + }); +}; + +module.exports.all = function (tape, common) { + + function test(name, testFunction) { + return tape('[middleware] parseBBox: ' + name, testFunction); + } + + for( var testCase in module.exports.tests ){ + module.exports.tests[testCase](test, common); + } +}; diff --git a/test/unit/mock/backend.js b/test/unit/mock/backend.js index 7d0eb8d7..84147783 100644 --- a/test/unit/mock/backend.js +++ b/test/unit/mock/backend.js @@ -15,7 +15,7 @@ responses['client/search/ok/1'] = function( cmd, cb ){ value: 1, center_point: { lat: 100.1, lon: -50.5 }, name: { default: 'test name1' }, - admin0: 'country1', admin1: 'state1', admin2: 'city1' + parent: { country: ['country1'], region: ['state1'], county: ['city1'] } } }, { _id: 'myid2', @@ -25,7 +25,7 @@ responses['client/search/ok/1'] = function( cmd, cb ){ value: 2, center_point: { lat: 100.2, lon: -51.5 }, name: { default: 'test name2' }, - admin0: 'country2', admin1: 'state2', admin2: 'city2' + parent: { country: ['country2'], region: ['state2'], county: ['city2'] } } }])); }; @@ -43,7 +43,7 @@ responses['client/mget/ok/1'] = function( cmd, cb ){ value: 1, center_point: { lat: 100.1, lon: -50.5 }, name: { default: 'test name1' }, - admin0: 'country1', admin1: 'state1', admin2: 'city1' + parent: { country: ['country1'], region: ['state1'], county: ['city1'] } } }, { _id: 'myid2', @@ -54,7 +54,7 @@ responses['client/mget/ok/1'] = function( cmd, cb ){ value: 2, center_point: { lat: 100.2, lon: -51.5 }, name: { default: 'test name2' }, - admin0: 'country2', admin1: 'state2', admin2: 'city2' + parent: { country: ['country2'], region: ['state2'], county: ['city2'] } } }])); }; diff --git a/test/unit/query/reverse.js b/test/unit/query/reverse.js index 7e6c2ba1..4060d679 100644 --- a/test/unit/query/reverse.js +++ b/test/unit/query/reverse.js @@ -108,6 +108,23 @@ module.exports.tests.query = function(test, common) { t.deepEqual(compiled, expected, 'valid reverse query with boundary.country'); 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': 500, + 'sources': ['test'] + }); + + var compiled = JSON.parse( JSON.stringify( query ) ); + var expected = require('../fixture/reverse_with_source_filtering'); + + t.deepEqual(compiled, expected, 'valid reverse query with source filtering'); + t.end(); + }); }; module.exports.all = function (tape, common) { diff --git a/test/unit/query/search.js b/test/unit/query/search.js index b468f493..f20122bb 100644 --- a/test/unit/query/search.js +++ b/test/unit/query/search.js @@ -126,8 +126,7 @@ module.exports.tests.query = function(test, common) { test('valid query with a full valid address', function(t) { var address = '123 main st new york ny 10010 US'; var query = generate({ text: address, - layers: [ 'geoname', 'osmnode', 'osmway', 'admin0', 'admin1', 'admin2', 'neighborhood', - 'locality', 'local_admin', 'osmaddress', 'openaddresses' ], + layers: [ 'address', 'venue', 'country', 'region', 'county', 'neighbourhood', 'locality', 'localadmin' ], querySize: 10, parsed_text: parser.get_parsed_address(address), }); @@ -142,8 +141,7 @@ module.exports.tests.query = function(test, common) { test('valid query with partial address', function(t) { var partial_address = 'soho grand, new york'; var query = generate({ text: partial_address, - layers: [ 'geoname', 'osmnode', 'osmway', 'admin0', 'admin1', 'admin2', 'neighborhood', - 'locality', 'local_admin', 'osmaddress', 'openaddresses' ], + layers: [ 'address', 'venue', 'country', 'region', 'county', 'neighbourhood', 'locality', 'localadmin' ], querySize: 10, parsed_text: parser.get_parsed_address(partial_address), }); @@ -158,8 +156,7 @@ module.exports.tests.query = function(test, common) { test('valid query with regions in address', function(t) { var partial_address = '1 water st manhattan ny'; var query = generate({ text: partial_address, - layers: [ 'geoname', 'osmnode', 'osmway', 'admin0', 'admin1', 'admin2', 'neighborhood', - 'locality', 'local_admin', 'osmaddress', 'openaddresses' ], + layers: [ 'address', 'venue', 'country', 'region', 'county', 'neighbourhood', 'locality', 'localadmin' ], querySize: 10, parsed_text: parser.get_parsed_address(partial_address), }); diff --git a/test/unit/run.js b/test/unit/run.js index 0510cda6..c1ab11b7 100644 --- a/test/unit/run.js +++ b/test/unit/run.js @@ -1,23 +1,34 @@ +var tape = require('tape'), + diff = require('difflet')({ indent : 2, comment : true }); -var tape = require('tape'); -var common = {}; +var common = { + // a visual deep diff rendered using console.error() + diff: function( actual, expected ){ + console.error( diff.compare( actual, expected ) ); + } +}; var tests = [ require('./controller/index'), require('./controller/place'), require('./controller/search'), require('./helper/geojsonify'), - require('./helper/labelGenerator'), + require('./helper/labelGenerator_default'), + require('./helper/labelGenerator_GBR'), + require('./helper/labelGenerator_SGP'), + require('./helper/labelGenerator_SWE'), + require('./helper/labelGenerator_USA'), require('./helper/labelSchema'), require('./helper/text_parser'), require('./helper/type_mapping'), - require('./helper/types'), require('./helper/sizeCalculator'), require('./middleware/confidenceScore'), require('./middleware/confidenceScoreReverse'), require('./middleware/distance'), require('./middleware/localNamingConventions'), require('./middleware/dedupe'), + require('./middleware/parseBBox'), + require('./middleware/normalizeParentIds'), require('./query/autocomplete'), require('./query/autocomplete_defaults'), require('./query/search_defaults'), @@ -34,7 +45,10 @@ var tests = [ require('./sanitiser/_single_scalar_parameters'), require('./sanitiser/_size'), require('./sanitiser/_sources'), + require('./sanitiser/_sources_and_layers'), require('./sanitiser/_text'), + require('./sanitiser/_deprecate_quattroshapes'), + require('./src/backend'), require('./sanitiser/autocomplete'), require('./sanitiser/place'), require('./sanitiser/reverse'), diff --git a/test/unit/sanitiser/_deprecate_quattroshapes.js b/test/unit/sanitiser/_deprecate_quattroshapes.js new file mode 100644 index 00000000..9a4ab53d --- /dev/null +++ b/test/unit/sanitiser/_deprecate_quattroshapes.js @@ -0,0 +1,65 @@ +var sanitize = require('../../../sanitiser/_deprecate_quattroshapes'); + +module.exports.tests = {}; + +module.exports.tests.warning_message_1 = function(test, common) { + test('[qs] should emit a deprecation warning', function(t) { + var raw = { sources: 'qs' }; + var clean = {}; + + var messages = sanitize(raw, clean); + t.deepEquals(messages, { + errors: [], + warnings: ['You are using Quattroshapes as a data source in this query. ' + + 'Quattroshapes has been disabled as a data source for Mapzen Search, and has been' + + 'replaced by Who\'s on First, an actively maintained data project based on Quattroshapes' + + 'Your existing queries WILL CONTINUE TO WORK for the foreseeable future, but results will ' + + 'be coming from Who\'s on First and `sources=quattroshapes` will be interpreted as ' + + '`sources=whosonfirst`. If you have any questions, please email search@mapzen.com.'] + }, 'warning emitted'); + + t.end(); + }); +}; + +module.exports.tests.warning_message_2 = function(test, common) { + test('[quattroshapes] should emit a deprecation warning', function(t) { + var raw = { sources: 'quattroshapes' }; + var clean = {}; + + var messages = sanitize(raw, clean); + t.deepEquals(messages, { + errors: [], + warnings: ['You are using Quattroshapes as a data source in this query. ' + + 'Quattroshapes has been disabled as a data source for Mapzen Search, and has been' + + 'replaced by Who\'s on First, an actively maintained data project based on Quattroshapes' + + 'Your existing queries WILL CONTINUE TO WORK for the foreseeable future, but results will ' + + 'be coming from Who\'s on First and `sources=quattroshapes` will be interpreted as ' + + '`sources=whosonfirst`. If you have any questions, please email search@mapzen.com.'] + }, 'warning emitted'); + + t.end(); + }); +}; + +module.exports.tests.rewrite = function(test, common) { + test('should rewrite qs and quattroshapes to whosonfirst', function(t) { + var raw = { sources: 'qs,quattroshapes,qs,quattroshapes,osm' }; + var clean = {}; + + sanitize(raw, clean); + t.equals(raw.sources,'osm,whosonfirst','use wof instead of qs'); + + t.end(); + }); +}; + +module.exports.all = function (tape, common) { + function test(name, testFunction) { + return tape('SANTIZE _deprecate_quattroshapes ' + name, testFunction); + } + + for( var testCase in module.exports.tests ){ + module.exports.tests[testCase](test, common); + } +}; diff --git a/test/unit/sanitiser/_ids.js b/test/unit/sanitiser/_ids.js index 40af368f..64e749b0 100644 --- a/test/unit/sanitiser/_ids.js +++ b/test/unit/sanitiser/_ids.js @@ -94,88 +94,79 @@ module.exports.tests.valid_ids = function(test, common) { test('ids: valid input (openaddresses)', function(t) { var raw = { ids: 'openaddresses:address:20' }; var clean = {}; - var expected_ids = [{ - id: '20', - types: [ 'openaddresses' ] - }]; var messages = sanitize( raw, clean ); + var expected_ids = [{ + source: 'openaddresses', + layer: 'address', + id: '20', + }]; t.deepEqual( messages.errors, [], ' no errors'); t.deepEqual( clean.ids, expected_ids, 'single type value returned'); t.end(); }); test('ids: valid input (osm)', function(t) { - var raw = { ids: 'osm:venue:500' }; + var raw = { ids: 'openstreetmap:venue:node:500' }; var clean = {}; var expected_ids = [{ - id: '500', - types: [ 'osmnode', 'osmway' ] + source: 'openstreetmap', + layer: 'venue', + id: 'node:500', }]; var messages = sanitize( raw, clean ); t.deepEqual( messages.errors, [], ' no errors'); - t.deepEqual( clean.ids, expected_ids, 'osm could be two types, but that\'s ok'); - t.end(); - }); -}; - -module.exports.tests.geonames = function(test, common) { - test('geonames venue maps correctly as normal', function(t) { - var raw = { ids: 'geonames:venue:15' }; - var clean = {}; - - var messages = sanitize( raw, clean); - - var expected_clean = { ids: [ { id: '15', types: [ 'geoname' ] } ] }; - t.deepEqual( messages.errors, [], 'no errors' ); - t.deepEqual( messages.warnings, [], 'no warnings' ); - t.deepEqual(clean, expected_clean, 'clean set correctly'); - t.end(); - }); - - test('arbitrary geonames layer maps to geoname type', function(t) { - var raw = { ids: 'geonames:address:16' }; // geonames technically has no address records! - var clean = {}; - - var messages = sanitize( raw, clean); - - var expected_clean = { ids: [ { id: '16', types: [ 'geoname' ] } ] }; - t.deepEqual( messages.errors, [], 'no errors' ); - t.deepEqual( messages.warnings, [], 'no warnings' ); - t.deepEqual(clean, expected_clean, 'clean set correctly'); + t.deepEqual( clean.ids, expected_ids, 'osm has node: or way: in id field'); t.end(); }); }; module.exports.tests.multiple_ids = function(test, common) { test('multiple ids', function(t) { - var raw = { ids: 'geonames:venue:1,osm:venue:2' }; + var raw = { ids: 'geonames:venue:1,openstreetmap:address:way:2' }; var clean = {}; - var expected_clean = { ids: [ { id: '1', types: [ 'geoname' ] }, { id: '2', types: [ 'osmnode', 'osmway' ] } ] }; var messages = sanitize( raw, clean); + var expected_ids = [ { + source: 'geonames', + layer: 'venue', + id: '1' + }, { + source: 'openstreetmap', + layer: 'address', + id: 'way:2' + } ]; + t.deepEqual( messages.errors, [], 'no errors' ); t.deepEqual( messages.warnings, [], 'no warnings' ); - t.deepEqual(clean, expected_clean, 'clean set correctly'); + t.deepEqual(clean.ids, expected_ids, 'clean set correctly'); t.end(); }); }; module.exports.tests.de_dupe = function(test, common) { test('duplicate ids', function(t) { - var expected_clean = { ids: [ { id: '1', types: [ 'geoname' ] }, { id: '2', types: [ 'osmnode', 'osmway' ] } ] }; - var raw = { ids: 'geonames:venue:1,osm:venue:2,geonames:venue:1' }; + var raw = { ids: 'geonames:venue:1,openstreetmap:venue:node:2,geonames:venue:1' }; var clean = {}; var messages = sanitize( raw, clean ); + var expected_ids = [ { + source: 'geonames', + layer: 'venue', + id: '1' + }, { + source: 'openstreetmap', + layer: 'venue', + id: 'node:2' + } ]; t.deepEqual( messages.errors, [], 'no errors' ); t.deepEqual( messages.warnings, [], 'no warnings' ); - t.deepEqual(clean, expected_clean, 'clean set correctly'); + t.deepEqual(clean.ids, expected_ids, 'clean set correctly'); t.end(); }); }; diff --git a/test/unit/sanitiser/_layers.js b/test/unit/sanitiser/_layers.js index 519d95d9..a1cdd7c5 100644 --- a/test/unit/sanitiser/_layers.js +++ b/test/unit/sanitiser/_layers.js @@ -1,6 +1,6 @@ var type_mapping = require('../../../helper/type_mapping'); -var sanitize = require('../../../sanitiser/_targets')('layers', type_mapping.layer_with_aliases_to_type); +var sanitize = require('../../../sanitiser/_targets')('layers', type_mapping.layer_mapping); module.exports.tests = {}; @@ -10,6 +10,7 @@ module.exports.tests.sanitize_layers = function(test, common) { t.equal(messages.errors.length, 0, 'no errors'); t.end(); }); + test('invalid layer', function(t) { var raw = { layers: 'test_layer' }; var clean = {}; @@ -22,95 +23,108 @@ module.exports.tests.sanitize_layers = function(test, common) { t.true(messages.errors[0].length > msg.length, 'invalid error message'); t.end(); }); + test('venue (alias) layer', function(t) { - var venue_layers = ['geoname','osmnode','osmway']; var raw = { layers: 'venue' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, venue_layers, 'venue layers set'); + var venue_layers = ['venue']; + t.deepEqual(clean.layers, venue_layers, 'venue layers set'); t.end(); }); + test('coarse (alias) layer', function(t) { - var admin_layers = ['admin0','admin1','admin2','neighborhood','locality','local_admin']; var raw = { layers: 'coarse' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, admin_layers, 'coarse layers set'); + var admin_layers = [ 'continent', 'macrocountry', 'country', 'dependency', + 'region', 'locality', 'localadmin', 'county', 'macrohood', 'neighbourhood', + 'microhood', 'disputed' ]; + + t.deepEqual(clean.layers, admin_layers, 'coarse layers set'); t.end(); }); - test('address (alias) layer', function(t) { - var address_layers = ['osmaddress','openaddresses']; + + test('address layer', function(t) { var raw = { layers: 'address' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, address_layers, 'address layers set'); + t.deepEqual(clean.layers, ['address'], 'address layer set'); t.end(); }); + test('venue alias layer plus regular layers', function(t) { - var venue_layers = ['geoname','osmnode','osmway']; - var reg_layers = ['admin0', 'admin1']; var raw = { layers: 'venue,country,region' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, venue_layers.concat(reg_layers), 'venue + regular layers'); + var expected_layers = ['venue', 'country', 'region']; + t.deepEqual(clean.layers, expected_layers, 'venue + regular layers'); t.end(); }); - test('coarse alias layer plus regular layers', function(t) { - var admin_layers = ['admin0','admin1','admin2','neighborhood','locality','local_admin']; - var reg_layers = ['geoname', 'osmnode', 'osmway']; - var raw = { layers: 'coarse,venue,country' }; + test('coarse alias layer plus regular layers', function(t) { + var raw = { layers: 'coarse,country' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, admin_layers.concat(reg_layers), 'coarse + regular layers set'); + var expected_layers = [ 'continent', 'macrocountry', 'country', 'dependency', + 'region', 'locality', 'localadmin', 'county', 'macrohood', 'neighbourhood', + 'microhood', 'disputed' ]; + + t.deepEqual(clean.layers, expected_layers, 'coarse + regular layers set'); t.end(); }); - test('address alias layer plus regular layers', function(t) { - var address_layers = ['osmaddress','openaddresses']; - var reg_layers = ['admin0', 'locality']; + test('address alias layer plus regular layers', function(t) { var raw = { layers: 'address,country,locality' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, address_layers.concat(reg_layers), 'address + regular layers set'); + var expected_layers = ['address', 'country', 'locality' ]; + t.deepEqual(clean.layers, expected_layers, 'address + regular layers set'); t.end(); }); + test('alias layer plus regular layers (no duplicates)', function(t) { - var venue_layers = ['geoname','osmnode','osmway','admin0']; var raw = { layers: 'venue,country' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, venue_layers, 'venue layers found (no duplicates)'); + var expected_layers = ['venue', 'country']; + t.deepEqual(clean.layers, expected_layers, 'venue layers found (no duplicates)'); t.end(); }); + test('multiple alias layers (no duplicates)', function(t) { - var alias_layers = ['geoname','osmnode','osmway','admin0','admin1','admin2','neighborhood','locality','local_admin']; var raw = { layers: 'venue,coarse' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, alias_layers, 'all layers found (no duplicates)'); + var coarse_layers = [ 'continent', 'macrocountry', + 'country', 'dependency', 'region', 'locality', 'localadmin', + 'county', 'macrohood', 'neighbourhood', 'microhood', + 'disputed' ]; + + var venue_layers = [ 'venue' ]; + var expected_layers = venue_layers.concat(coarse_layers); + t.deepEqual(clean.layers, expected_layers, 'all layers found (no duplicates)'); t.end(); }); }; module.exports.all = function (tape, common) { - function test(name, testFunction) { return tape('SANTIZE _layers ' + name, testFunction); } diff --git a/test/unit/sanitiser/_sources.js b/test/unit/sanitiser/_sources.js index d2ef405f..b0daffef 100644 --- a/test/unit/sanitiser/_sources.js +++ b/test/unit/sanitiser/_sources.js @@ -1,5 +1,5 @@ var type_mapping = require('../../../helper/type_mapping'); -var sanitize = require( '../../../sanitiser/_targets' )('sources', type_mapping.source_to_type); +var sanitize = require( '../../../sanitiser/_targets' )('sources', type_mapping.source_mapping); var success_messages = { error: false }; @@ -14,7 +14,7 @@ module.exports.tests.no_sources = function(test, common) { var messages = sanitize(req.query, req.clean); - t.deepEqual(req.clean.types, {}, 'clean.types should be empty object'); + t.equal(req.clean.sources, undefined, 'no sources should be defined'); t.deepEqual(messages.errors, [], 'no error returned'); t.deepEqual(messages.warnings, [], 'no warnings returned'); t.end(); @@ -29,11 +29,11 @@ module.exports.tests.no_sources = function(test, common) { }; var expected_error = 'sources parameter cannot be an empty string. ' + - 'Valid options: gn,geonames,oa,openaddresses,qs,quattroshapes,osm,openstreetmap'; + 'Valid options: osm,oa,gn,wof,openstreetmap,openaddresses,geonames,whosonfirst'; var messages = sanitize(req.query, req.clean); - t.deepEqual(req.clean.types, {}, 'clean.types should be empty object'); + t.equal(req.clean.sources, undefined, 'no sources should be defined'); t.deepEqual(messages.errors.length, 1, 'error returned'); t.deepEqual(messages.errors[0], expected_error, 'error returned'); t.deepEqual(messages.warnings, [], 'no warnings returned'); @@ -52,27 +52,24 @@ module.exports.tests.valid_sources = function(test, common) { var messages = sanitize(req.query, req.clean); - t.deepEqual(req.clean.types, { from_sources: ['geoname'] }, 'clean.types should contain from_source entry with geonames'); + t.deepEqual(req.clean.sources, ['geonames'], 'sources should contain geonames'); t.deepEqual(messages.errors, [], 'no error returned'); t.deepEqual(messages.warnings, [], 'no warnings returned'); t.end(); }); - test('openstreetmap source', function(t) { + test('openstreetmap (abbreviated) source', function(t) { var req = { query: { - sources: 'openstreetmap' + sources: 'osm' }, clean: { } }; - var expected_types = { - from_sources: ['osmaddress', 'osmnode', 'osmway'] - }; var messages = sanitize(req.query, req.clean); - t.deepEqual(req.clean.types, expected_types, 'clean.types should contain from_source entry with multiple entries for openstreetmap'); + t.deepEqual(req.clean.sources, ['openstreetmap'], 'abbreviation is expanded to full version'); t.deepEqual(messages.errors, [], 'no error returned'); t.deepEqual(messages.warnings, [], 'no warnings returned'); t.end(); @@ -85,14 +82,11 @@ module.exports.tests.valid_sources = function(test, common) { }, clean: { } }; - var expected_types = { - from_sources: ['osmaddress', 'osmnode', 'osmway', 'openaddresses'] - }; var messages = sanitize(req.query, req.clean); - t.deepEqual(req.clean.types, expected_types, - 'clean.types should contain from_source entry with multiple entries for openstreetmap and openadresses'); + t.deepEqual(req.clean.sources, ['openstreetmap', 'openaddresses'], + 'clean.sources should contain openstreetmap and openadresses'); t.deepEqual(messages.errors, [], 'no error returned'); t.deepEqual(messages.warnings, [], 'no warnings returned'); t.end(); @@ -109,7 +103,8 @@ module.exports.tests.invalid_sources = function(test, common) { }; var expected_messages = { errors: [ - '\'notasource\' is an invalid sources parameter. Valid options: gn,geonames,oa,openaddresses,qs,quattroshapes,osm,openstreetmap' + '\'notasource\' is an invalid sources parameter. ' + + 'Valid options: osm,oa,gn,wof,openstreetmap,openaddresses,geonames,whosonfirst' ], warnings: [] }; @@ -117,7 +112,7 @@ module.exports.tests.invalid_sources = function(test, common) { var messages = sanitize(req.query, req.clean); t.deepEqual(messages, expected_messages, 'error with message returned'); - t.deepEqual(req.clean.types, { }, 'clean.types should remain empty'); + t.equal(req.clean.sources, undefined, 'clean.sources should remain empty'); t.end(); }); }; diff --git a/test/unit/sanitiser/_sources_and_layers.js b/test/unit/sanitiser/_sources_and_layers.js new file mode 100644 index 00000000..96001f4e --- /dev/null +++ b/test/unit/sanitiser/_sources_and_layers.js @@ -0,0 +1,115 @@ +var sanitize = require('../../../sanitiser/_sources_and_layers'); + +var type_mapping = require('../../../helper/type_mapping'); + +module.exports.tests = {}; + +module.exports.tests.inactive = function(test, common) { + test('no source or layer specified', function(t) { + var raw = {}; + var clean = {}; + + var messages = sanitize(raw, clean); + + t.equal(messages.errors.length, 0, 'should return no errors'); + t.equal(messages.warnings.length, 0, 'should return no warnings'); + t.end(); + }); + + test('only layers specified', function(t) { + var raw = {}; + var clean = { layers: ['venue'] }; + + var messages = sanitize(raw, clean); + + t.equal(messages.errors.length, 0, 'should return no errors'); + t.equal(messages.warnings.length, 0, 'should return no warnings'); + t.end(); + }); + + test('only sources specified', function(t) { + var raw = {}; + var clean = { sources: ['openstreetmap'] }; + + var messages = sanitize(raw, clean); + + t.equal(messages.errors.length, 0, 'should return no errors'); + t.equal(messages.warnings.length, 0, 'should return no warnings'); + t.end(); + }); +}; + +module.exports.tests.no_errors = function(test, common) { + test('valid combination', function(t) { + var raw = {}; + var clean = { sources: ['openstreetmap'], layers: ['venue'] }; + + var messages = sanitize(raw, clean); + + t.equal(messages.errors.length, 0, 'should return no errors'); + t.equal(messages.warnings.length, 0, 'should return no warnings'); + t.end(); + }); + + test('valid combination because of multiple sources', function(t) { + var raw = {}; + var clean = { sources: ['openstreetmap', 'openaddresses'], layers: ['venue'] }; + + var messages = sanitize(raw, clean); + + t.equal(messages.errors.length, 0, 'should return no errors'); + t.equal(messages.warnings.length, 0, 'should return no warnings'); + t.end(); + }); + + test('valid combination because of multiple layers', function(t) { + var raw = {}; + var clean = { sources: ['openaddresses'], layers: ['address', 'country'] }; + + var messages = sanitize(raw, clean); + + t.equal(messages.errors.length, 0, 'should return no errors'); + t.equal(messages.warnings.length, 0, 'should return no warnings'); + t.end(); + }); +}; + +module.exports.tests.invalid_combination = function(test, common) { + test('address layer with wof', function(t) { + var raw = {}; + var clean = { sources: ['whosonfirst'], layers: ['address'] }; + + var messages = sanitize(raw, clean); + + t.equal(messages.errors.length, 1, 'should return an error'); + t.equal(messages.errors[0], 'You have specified both the `sources` and `layers` ' + + 'parameters in a combination that will return no results: the whosonfirst source has nothing in the address layer'); + t.equal(messages.warnings.length, 0, 'should return no warnings'); + t.end(); + }); + + test('admin layers with osm', function(t) { + var raw = {}; + var clean = { sources: ['openstreetmap'], layers: ['country', 'locality'] }; + + var messages = sanitize(raw, clean); + + t.equal(messages.errors.length, 2, 'should return an error'); + t.equal(messages.errors[0], 'You have specified both the `sources` and `layers` ' + + 'parameters in a combination that will return no results: the openstreetmap source has nothing in the country layer'); + t.equal(messages.errors[1], 'You have specified both the `sources` and `layers` ' + + 'parameters in a combination that will return no results: the openstreetmap source has nothing in the locality layer'); + t.equal(messages.warnings.length, 0, 'should return no warnings'); + t.end(); + }); +}; + +module.exports.all = function (tape, common) { + function test(name, testFunction) { + return tape('SANTIZE _sources_and_layers ' + name, testFunction); + } + + for( var testCase in module.exports.tests ){ + module.exports.tests[testCase](test, common); + } +}; diff --git a/test/unit/sanitiser/_text.js b/test/unit/sanitiser/_text.js index 8bc0710f..3868c86c 100644 --- a/test/unit/sanitiser/_text.js +++ b/test/unit/sanitiser/_text.js @@ -15,7 +15,6 @@ module.exports.tests.text_parser = function(test, common) { t.deepEquals(messages.errors, [], 'no errors'); t.deepEquals(messages.warnings, [], 'no warnings'); - t.equal(clean.types.from_text_parser, type_mapping.layer_with_aliases_to_type.coarse, 'coarse layers preferred'); t.end(); }); diff --git a/test/unit/sanitiser/place.js b/test/unit/sanitiser/place.js index f7cda1e9..dd9d1645 100644 --- a/test/unit/sanitiser/place.js +++ b/test/unit/sanitiser/place.js @@ -1,7 +1,7 @@ var place = require('../../../sanitiser/place'), sanitize = place.sanitize, middleware = place.middleware, - defaultClean = { ids: [ { id: '123', types: [ 'geoname' ] } ], private: false }; + defaultClean = { ids: [ { source: 'geonames', layer: 'venue', id: '123' } ], private: false }; // these are the default values you would expect when no input params are specified. module.exports.tests = {}; diff --git a/test/unit/sanitiser/reverse.js b/test/unit/sanitiser/reverse.js index d9fdebc9..fcce8226 100644 --- a/test/unit/sanitiser/reverse.js +++ b/test/unit/sanitiser/reverse.js @@ -11,15 +11,13 @@ var reverse = require('../../../sanitiser/reverse'), 'boundary.circle.lat': 0, 'boundary.circle.lon': 0, 'boundary.circle.radius': parseFloat(defaults['boundary:circle:radius']), - types: { - }, size: 10, private: false }; // these are the default values you would expect when no input params are specified. // @todo: why is this different from $defaultClean? -var emptyClean = { private: false, size: 10, types: {} }; +var emptyClean = { private: false, size: 10 }; module.exports.tests = {}; @@ -38,8 +36,8 @@ module.exports.tests.interface = function(test, common) { module.exports.tests.sanitisers = function(test, common) { test('check sanitiser list', function (t) { - var expected = [ 'singleScalarParameters', 'layers', 'sources', 'quattroshapes_warning', - 'size', 'private', 'geo_reverse', 'boundary_country' ]; + var expected = ['quattroshapes_deprecation', 'singleScalarParameters', 'layers', + 'sources', 'sources_and_layers', 'size', 'private', 'geo_reverse', 'boundary_country']; t.deepEqual(Object.keys(reverse.sanitiser_list), expected); t.end(); }); @@ -105,7 +103,7 @@ module.exports.tests.sanitize_lon = function(test, common) { var req = { query: { 'point.lat': 0, 'point.lon': lon } }; // @todo: why is lat set? - var expected = { 'point.lat': 0, private: false, size: 10, types: {} }; + var expected = { 'point.lat': 0, private: false, size: 10 }; sanitize(req, function(){ t.equal(req.errors[0], 'missing param \'point.lon\'', 'longitude is a required field'); t.deepEqual(req.clean, expected, 'clean only has default values set'); diff --git a/test/unit/sanitiser/search.js b/test/unit/sanitiser/search.js index 07178626..e09672ce 100644 --- a/test/unit/sanitiser/search.js +++ b/test/unit/sanitiser/search.js @@ -5,7 +5,7 @@ var extend = require('extend'), middleware = search.middleware, defaultError = 'invalid param \'text\': text length, must be >0'; // these are the default values you would expect when no input params are specified. -var emptyClean = { private: false, size: 10, types: {} }; +var emptyClean = { private: false, size: 10 }; module.exports.tests = {}; @@ -24,8 +24,8 @@ module.exports.tests.interface = function(test, common) { module.exports.tests.sanitisers = function(test, common) { test('check sanitiser list', function (t) { - var expected = [ 'singleScalarParameters', 'text', 'size', 'layers', 'sources', - 'quattroshapes_warning', 'private', 'geo_search', 'boundary_country' ]; + var expected = ['quattroshapes_deprecation', 'singleScalarParameters', 'text', 'size', + 'layers', 'sources', 'sources_and_layers', 'private', 'geo_search', 'boundary_country' ]; t.deepEqual(Object.keys(search.sanitiser_list), expected); t.end(); }); @@ -72,7 +72,7 @@ module.exports.tests.sanitise_valid_text = function(test, common) { }; module.exports.tests.sanitize_text_with_delim = function(test, common) { - var texts = [ 'a,bcd', '123 main st, admin1', ',,,', ' ' ]; + var texts = [ 'a,bcd', '123 main st, region', ',,,', ' ' ]; test('valid texts with a comma', function(t) { texts.forEach( function( text ){ diff --git a/test/unit/service/mget.js b/test/unit/service/mget.js index 9f13d78b..e2723e6e 100644 --- a/test/unit/service/mget.js +++ b/test/unit/service/mget.js @@ -20,14 +20,14 @@ module.exports.tests.functional_success = function(test, common) { value: 1, center_point: { lat: 100.1, lon: -50.5 }, name: { default: 'test name1' }, - admin0: 'country1', admin1: 'state1', admin2: 'city1' + parent: { country: ['country1'], region: ['state1'], county: ['city1'] } }, { _id: 'myid2', _type: 'mytype2', value: 2, center_point: { lat: 100.2, lon: -51.5 }, name: { default: 'test name2' }, - admin0: 'country2', admin1: 'state2', admin2: 'city2' + parent: { country: ['country2'], region: ['state2'], county: ['city2'] } } ]; diff --git a/test/unit/service/search.js b/test/unit/service/search.js index e5bf2cff..0de8e0fa 100644 --- a/test/unit/service/search.js +++ b/test/unit/service/search.js @@ -23,7 +23,7 @@ module.exports.tests.functional_success = function(test, common) { value: 1, center_point: { lat: 100.1, lon: -50.5 }, name: { default: 'test name1' }, - admin0: 'country1', admin1: 'state1', admin2: 'city1' + parent: { country: ['country1'], region: ['state1'], county: ['city1'] } }, { _id: 'myid2', _type: 'mytype2', @@ -31,7 +31,7 @@ module.exports.tests.functional_success = function(test, common) { value: 2, center_point: { lat: 100.2, lon: -51.5 }, name: { default: 'test name2' }, - admin0: 'country2', admin1: 'state2', admin2: 'city2' + parent: { country: ['country2'], region: ['state2'], county: ['city2'] } } ]; diff --git a/test/unit/src/backend.js b/test/unit/src/backend.js new file mode 100644 index 00000000..1b750396 --- /dev/null +++ b/test/unit/src/backend.js @@ -0,0 +1,40 @@ +var proxyquire = require('proxyquire'); + +var stubConfig = { + generate: function generate() { + return { + esclient: { + hosts: [ + 'http://notLocalhost:9200', + 'http://anotherHost:9200' + ] + } + }; + } +}; + + +module.exports.tests = {}; + +module.exports.tests.config_properly_passed = function(test, common) { + test('Elasticsearch config is properly passed to elasticsearch module', function(t) { + var stubElasticsearchClient = { + Client: function(config) { + t.deepEquals(config.hosts, [ 'http://notLocalhost:9200', 'http://anotherHost:9200' ], 'hosts set correctly' ); + t.end(); + } + }; + + proxyquire('../../../src/backend', { 'pelias-config': stubConfig, 'elasticsearch': stubElasticsearchClient } ); + }); +}; + +module.exports.all = function (tape, common) { + function test(name, testFunction) { + return tape('SANTIZE src/backend ' + name, testFunction); + } + + for( var testCase in module.exports.tests ){ + module.exports.tests[testCase](test, common); + } +};