From 6bde165f31029c8c82d6d3b3d23cc5063849c942 Mon Sep 17 00:00:00 2001 From: Joxit Date: Thu, 4 May 2023 15:17:35 +0200 Subject: [PATCH] feat: add support to `focus.country` --- query/autocomplete.js | 11 +++++++- query/autocomplete_defaults.js | 5 ++++ query/view/focus_country.js | 18 +++++++++++++ .../{_boundary_country.js => _countries.js} | 25 ++++++++++--------- sanitizer/autocomplete.js | 3 ++- sanitizer/nearby.js | 2 +- sanitizer/reverse.js | 2 +- sanitizer/search.js | 2 +- sanitizer/structured_geocoding.js | 2 +- test/unit/run.js | 2 +- .../{_boundary_country.js => _countries.js} | 10 +++++++- test/unit/sanitizer/autocomplete.js | 5 ++-- test/unit/sanitizer/nearby.js | 4 +-- test/unit/sanitizer/reverse.js | 4 +-- test/unit/sanitizer/search.js | 4 +-- test/unit/sanitizer/structured_geocoding.js | 4 +-- 16 files changed, 73 insertions(+), 30 deletions(-) create mode 100644 query/view/focus_country.js rename sanitizer/{_boundary_country.js => _countries.js} (71%) rename test/unit/sanitizer/{_boundary_country.js => _countries.js} (90%) diff --git a/query/autocomplete.js b/query/autocomplete.js index 6e980a75c..47aeb87fd 100644 --- a/query/autocomplete.js +++ b/query/autocomplete.js @@ -17,7 +17,8 @@ var views = { phrase_first_tokens_only: require('./view/phrase_first_tokens_only'), boost_exact_matches: require('./view/boost_exact_matches'), max_character_count_layer_filter: require('./view/max_character_count_layer_filter'), - focus_point_filter: require('./view/focus_point_distance_filter') + focus_point_filter: require('./view/focus_point_distance_filter'), + focus_country: require('./view/focus_country') }; // add abbrevations for the fields pelias/parser is able to detect. @@ -52,6 +53,7 @@ query.score( views.admin_multi_match_last( adminFields ), 'must'); query.score( peliasQuery.view.focus( peliasQuery.view.leaf.match_all ) ); query.score( peliasQuery.view.popularity( peliasQuery.view.leaf.match_all ) ); query.score( peliasQuery.view.population( peliasQuery.view.leaf.match_all ) ); +query.score( views.focus_country ); query.score( views.custom_boosts( config.get('api.customBoosts') ) ); // non-scoring hard filters @@ -92,6 +94,13 @@ function generateQuery( clean ){ }); } + // focus country + if( _.isArray(clean['focus.country']) && !_.isEmpty(clean['focus.country']) ){ + vs.set({ + 'multi_match:focus_country:input': clean['focus.country'].join(' ') + }); + } + // pass the input tokens to the views so they can choose which tokens // are relevant for their specific function. if( _.isArray( clean.tokens ) ){ diff --git a/query/autocomplete_defaults.js b/query/autocomplete_defaults.js index ca8eca7d7..0d7f8027c 100644 --- a/query/autocomplete_defaults.js +++ b/query/autocomplete_defaults.js @@ -76,6 +76,11 @@ module.exports = _.merge({}, peliasQuery.defaults, { 'multi_match:boundary_country:analyzer': 'standard', 'multi_match:boundary_country:fields': ['parent.country_a', 'parent.dependency_a'], + // these options affect the `focus.country` hard filter + 'multi_match:focus_country:analyzer': 'standard', + 'multi_match:focus_country:fields': ['parent.country_a', 'parent.dependency_a'], + 'multi_match:focus_country:boost': 1.5, + 'admin:country:analyzer': 'peliasAdmin', 'admin:country:field': 'parent.country.ngram', 'admin:country:boost': 1, diff --git a/query/view/focus_country.js b/query/view/focus_country.js new file mode 100644 index 000000000..463673b4c --- /dev/null +++ b/query/view/focus_country.js @@ -0,0 +1,18 @@ +const peliasQuery = require('pelias-query'); + +module.exports = function (vs) { + const view_name = 'focus_country'; + + const input = vs.var(`multi_match:${view_name}:input`).get(); + + if (!input || input.length < 1) { + return null; + } + + return { + 'function_score': { + 'query': peliasQuery.view.leaf.multi_match(view_name)(vs), + 'score_mode': 'multiply', + }, + }; +}; diff --git a/sanitizer/_boundary_country.js b/sanitizer/_countries.js similarity index 71% rename from sanitizer/_boundary_country.js rename to sanitizer/_countries.js index 94f1d6693..4c6a12744 100644 --- a/sanitizer/_boundary_country.js +++ b/sanitizer/_countries.js @@ -2,20 +2,21 @@ const _ = require('lodash'); const nonEmptyString = (v) => _.isString(v) && !_.isEmpty(v); const iso3166 = require('../helper/iso3166'); -function _sanitize(raw, clean) { +const _sanitize = (key) => + (raw, clean) => { // error & warning messages const messages = { errors: [], warnings: [] }; // target input param - let countries = raw['boundary.country']; + let countries = raw[`${key}.country`]; - // param 'boundary.country' is optional and should not + // param `*.country` is optional and should not // error when simply not set by the user if (!_.isNil(countries)) { // must be valid string if (!nonEmptyString(countries)) { - messages.errors.push('boundary.country is not a string'); + messages.errors.push(`${key}.country is not a string`); } else { // support for multi countries countries = countries.split(',').filter(nonEmptyString); @@ -23,7 +24,7 @@ function _sanitize(raw, clean) { // country list must contains at least one element if (_.isArray(countries) && _.isEmpty(countries)) { - messages.errors.push('boundary.country is empty'); + messages.errors.push(`${key}.country is empty`); } // must be a valid ISO 3166 code @@ -35,23 +36,23 @@ function _sanitize(raw, clean) { else { // the only way for boundary.country to be assigned is if input is // a string and a known ISO2 or ISO3 - clean['boundary.country'] = countries.map(country => iso3166.iso3Code(country)); + clean[`${key}.country`] = countries.map(country => iso3166.iso3Code(country)); } } } return messages; -} +}; function containsIsoCode(isoCode) { return iso3166.isISO2Code(isoCode) || iso3166.isISO3Code(isoCode); } -function _expected(){ - return [{ name: 'boundary.country' }]; +function _expected(key) { + return () => [{ name: `${key}.country` }]; } -module.exports = () => ({ - sanitize: _sanitize, - expected: _expected +module.exports = (key = 'boundary') => ({ + sanitize: _sanitize(key), + expected: _expected(key) }); diff --git a/sanitizer/autocomplete.js b/sanitizer/autocomplete.js index 34351f072..cce8f9290 100644 --- a/sanitizer/autocomplete.js +++ b/sanitizer/autocomplete.js @@ -41,7 +41,8 @@ module.exports.middleware = (_api_pelias_config) => { sources_and_layers: require('../sanitizer/_sources_and_layers')(), private: require('../sanitizer/_flag_bool')('private', false), geo_autocomplete: require('../sanitizer/_geo_autocomplete')(), - boundary_country: require('../sanitizer/_boundary_country')(), + boundary_country: require('../sanitizer/_countries')('boundary'), + focus_country: require('../sanitizer/_countries')('focus'), categories: require('../sanitizer/_categories')(), request_language: require('../sanitizer/_request_language')(), boundary_gid: require('../sanitizer/_boundary_gid')() diff --git a/sanitizer/nearby.js b/sanitizer/nearby.js index 182c40f8c..f8f1db51b 100644 --- a/sanitizer/nearby.js +++ b/sanitizer/nearby.js @@ -35,7 +35,7 @@ module.exports.middleware = (_api_pelias_config) => { size: require('../sanitizer/_size')(/* use defaults*/), private: require('../sanitizer/_flag_bool')('private', false), geo_reverse: require('../sanitizer/_geo_reverse')(), - boundary_country: require('../sanitizer/_boundary_country')(), + boundary_country: require('../sanitizer/_countries')('boundary'), categories: require('../sanitizer/_categories')(), request_language: require('../sanitizer/_request_language')() }; diff --git a/sanitizer/reverse.js b/sanitizer/reverse.js index 33cfe28ba..59ed27a29 100644 --- a/sanitizer/reverse.js +++ b/sanitizer/reverse.js @@ -35,7 +35,7 @@ module.exports.middleware = (_api_pelias_config) => { size: require('../sanitizer/_size')(/* use defaults*/), private: require('../sanitizer/_flag_bool')('private', false), geo_reverse: require('../sanitizer/_geo_reverse')(), - boundary_country: require('../sanitizer/_boundary_country')(), + boundary_country: require('../sanitizer/_countries')('boundary'), request_language: require('../sanitizer/_request_language')(), boundary_gid: require('../sanitizer/_boundary_gid')() }; diff --git a/sanitizer/search.js b/sanitizer/search.js index 9faf84b98..6e4cc409b 100644 --- a/sanitizer/search.js +++ b/sanitizer/search.js @@ -40,7 +40,7 @@ module.exports.middleware = (_api_pelias_config) => { sources_and_layers: require('../sanitizer/_sources_and_layers')(), private: require('../sanitizer/_flag_bool')('private', false), geo_search: require('../sanitizer/_geo_search')(), - boundary_country: require('../sanitizer/_boundary_country')(), + boundary_country: require('../sanitizer/_countries')('boundary'), categories: require('../sanitizer/_categories')(), // this can go away once geonames has been abrogated geonames_warnings: require('../sanitizer/_geonames_warnings')(), diff --git a/sanitizer/structured_geocoding.js b/sanitizer/structured_geocoding.js index 4a8ddd640..6ed606c5e 100644 --- a/sanitizer/structured_geocoding.js +++ b/sanitizer/structured_geocoding.js @@ -48,7 +48,7 @@ module.exports.middleware = (_api_pelias_config) => { sources_and_layers: require('../sanitizer/_sources_and_layers')(), private: require('../sanitizer/_flag_bool')('private', false), geo_search: require('../sanitizer/_geo_search')(), - boundary_country: require('../sanitizer/_boundary_country')(), + boundary_country: require('../sanitizer/_countries')('boundary'), categories: require('../sanitizer/_categories')(), request_language: require('../sanitizer/_request_language')(), boundary_gid: require('../sanitizer/_boundary_gid')() diff --git a/test/unit/run.js b/test/unit/run.js index 2672804d4..3856cd9df 100644 --- a/test/unit/run.js +++ b/test/unit/run.js @@ -79,7 +79,7 @@ var tests = [ require('./query/text_parser'), require('./query/view/boost_sources_and_layers'), require('./query/view/max_character_count_layer_filter'), - require('./sanitizer/_boundary_country'), + require('./sanitizer/_countries'), require('./sanitizer/_debug'), require('./sanitizer/_default_parameters'), require('./sanitizer/_flag_bool'), diff --git a/test/unit/sanitizer/_boundary_country.js b/test/unit/sanitizer/_countries.js similarity index 90% rename from test/unit/sanitizer/_boundary_country.js rename to test/unit/sanitizer/_countries.js index d97adff0a..54fbc1d05 100644 --- a/test/unit/sanitizer/_boundary_country.js +++ b/test/unit/sanitizer/_countries.js @@ -1,4 +1,5 @@ -var sanitizer = require('../../../sanitizer/_boundary_country')(); +const countries = require('../../../sanitizer/_countries'); +const sanitizer = countries(); module.exports.tests = {}; @@ -73,6 +74,13 @@ module.exports.tests.sanitize_boundary_country = function(test, common) { t.end(); }); + test('return an array of expected custom parameters in object form for validation', (t) => { + const expected = [{ name: 'custom-name.country' }]; + const validParameters = countries('custom-name').expected(); + t.deepEquals(validParameters, expected); + t.end(); + }); + }; module.exports.all = function (tape, common) { diff --git a/test/unit/sanitizer/autocomplete.js b/test/unit/sanitizer/autocomplete.js index f8fe7a41c..e8c9b30eb 100644 --- a/test/unit/sanitizer/autocomplete.js +++ b/test/unit/sanitizer/autocomplete.js @@ -112,10 +112,10 @@ module.exports.tests.sanitizers = function(test, common) { } }; }, - '../sanitizer/_boundary_country': function () { + '../sanitizer/_countries': function (key) { return { sanitize: () => { - called_sanitizers.push('_boundary_country'); + called_sanitizers.push(`_${key}_country`); return { errors: [], warnings: [] }; } }; @@ -160,6 +160,7 @@ module.exports.tests.sanitizers = function(test, common) { '_flag_bool', '_geo_autocomplete', '_boundary_country', + '_focus_country', '_categories', '_request_language', '_boundary_gid' diff --git a/test/unit/sanitizer/nearby.js b/test/unit/sanitizer/nearby.js index 730e5c388..97e999655 100644 --- a/test/unit/sanitizer/nearby.js +++ b/test/unit/sanitizer/nearby.js @@ -86,10 +86,10 @@ module.exports.tests.sanitize = function(test, common) { } }; }, - '../sanitizer/_boundary_country': function () { + '../sanitizer/_countries': function (key) { return { sanitize: () => { - called_sanitizers.push('_boundary_country'); + called_sanitizers.push(`_${key}_country`); return { errors: [], warnings: [] }; } }; diff --git a/test/unit/sanitizer/reverse.js b/test/unit/sanitizer/reverse.js index aba8f9b55..6ef86fe38 100644 --- a/test/unit/sanitizer/reverse.js +++ b/test/unit/sanitizer/reverse.js @@ -86,10 +86,10 @@ module.exports.tests.sanitize = function(test, common) { } }; }, - '../sanitizer/_boundary_country': function () { + '../sanitizer/_countries': function (key) { return { sanitize: () => { - called_sanitizers.push('_boundary_country'); + called_sanitizers.push(`_${key}_country`); return { errors: [], warnings: [] }; } }; diff --git a/test/unit/sanitizer/search.js b/test/unit/sanitizer/search.js index b0da9e5f2..ec092776c 100644 --- a/test/unit/sanitizer/search.js +++ b/test/unit/sanitizer/search.js @@ -102,10 +102,10 @@ module.exports.tests.sanitize = (test, common) => { } }; }, - '../sanitizer/_boundary_country': function () { + '../sanitizer/_countries': function (key) { return { sanitize: () => { - called_sanitizers.push('_boundary_country'); + called_sanitizers.push(`_${key}_country`); return { errors: [], warnings: [] }; } }; diff --git a/test/unit/sanitizer/structured_geocoding.js b/test/unit/sanitizer/structured_geocoding.js index 0fb01ddbd..87894c03d 100644 --- a/test/unit/sanitizer/structured_geocoding.js +++ b/test/unit/sanitizer/structured_geocoding.js @@ -114,10 +114,10 @@ module.exports.tests.sanitize = function(test, common) { } }; }, - '../sanitizer/_boundary_country': function () { + '../sanitizer/_countries': function (key) { return { sanitize: () => { - called_sanitizers.push('_boundary_country'); + called_sanitizers.push(`_${key}_country`); return { errors: [], warnings: [] }; } };