Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Boost responses by country code #1650

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion query/autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -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_multi_match: require('./view/focus_multi_match')
};

// add abbrevations for the fields pelias/parser is able to detect.
Expand Down Expand Up @@ -52,6 +53,8 @@ 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_multi_match('focus_country') );
query.score( views.focus_multi_match('focus_gid') );
query.score( views.custom_boosts( config.get('api.customBoosts') ) );

// non-scoring hard filters
Expand Down Expand Up @@ -92,6 +95,20 @@ 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(' ')
});
}

// focus gid
if( _.isString(clean['focus.gid']) && !_.isEmpty(clean['focus.gid']) ){
vs.set({
'multi_match:focus_gid:input': clean['focus.gid']
});
}

// pass the input tokens to the views so they can choose which tokens
// are relevant for their specific function.
if( _.isArray( clean.tokens ) ){
Expand Down
10 changes: 10 additions & 0 deletions query/autocomplete_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ 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,

// these options affect the `focus.gid` hard filter
'multi_match:focus_gid:analyzer': 'standard',
'multi_match:focus_gid:fields': ['parent.*_id'],
'multi_match:focus_gid:boost': 1.5,

'admin:country:analyzer': 'peliasAdmin',
'admin:country:field': 'parent.country.ngram',
'admin:country:boost': 1,
Expand Down
11 changes: 11 additions & 0 deletions query/view/focus_multi_match.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const peliasQuery = require('pelias-query');

module.exports = (view_name) => (vs) => {
const input = vs.var(`multi_match:${view_name}:input`).get();

if (!input || input.length < 1) {
return null;
}

return peliasQuery.view.leaf.multi_match(view_name)(vs);
};
24 changes: 12 additions & 12 deletions sanitizer/_boundary_country.js → sanitizer/_countries.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,28 @@ 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);
const invalidIsoCodes = countries.filter(country => !containsIsoCode(country));

// 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
Expand All @@ -35,23 +35,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)
});
22 changes: 11 additions & 11 deletions sanitizer/_boundary_gid.js → sanitizer/_gids.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
const _ = require('lodash');

function _sanitize(raw, clean) {
const _sanitize = (key) => (raw, clean) => {
// error & warning messages
var messages = { errors: [], warnings: [] };

// target input param
var boundary_gid = raw['boundary.gid'];
var boundary_gid = raw[`${key}.gid`];

// param 'boundary.gid' is optional and should not
// param `${key}.gid` is optional and should not
// error when simply not set by the user
// must be valid string
if (!_.isNil(boundary_gid)) {
if (!_.isString(boundary_gid) || _.isEmpty(boundary_gid)) {
messages.errors.push('boundary.gid is not a string');
messages.errors.push(`${key}.gid is not a string`);
}
else {
// boundary gid should take the form of source:layer:id,
Expand All @@ -22,7 +22,7 @@ function _sanitize(raw, clean) {
return x !== '';
});
if ( _.inRange(fields.length, 3, 5) ) {
clean['boundary.gid'] = fields.slice(2).join(':');
clean[`${key}.gid`] = fields.slice(2).join(':');
}
else {
messages.errors.push(boundary_gid + ' does not follow source:layer:id format');
Expand All @@ -31,13 +31,13 @@ function _sanitize(raw, clean) {
}

return messages;
}
};

function _expected(){
return [{ name: 'boundary.gid' }];
function _expected(key) {
return () => [{ name: `${key}.gid` }];
}

module.exports = () => ({
sanitize: _sanitize,
expected: _expected
module.exports = (key = 'boundary') => ({
sanitize: _sanitize(key),
expected: _expected(key)
});
10 changes: 7 additions & 3 deletions sanitizer/autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ const paramGroups = [
['boundary.circle.lon', 'boundary.circle.lat'],
['boundary.circle.radius'],
['boundary.country'],
['boundary.gid']
['boundary.gid'],
['focus.country'],
['focus.gid']
];

// middleware
Expand All @@ -41,10 +43,12 @@ 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')()
boundary_gid: require('../sanitizer/_gids')('boundary'),
focus_gid: require('../sanitizer/_gids')('focus')
};

return ( req, res, next ) => {
Expand Down
2 changes: 1 addition & 1 deletion sanitizer/nearby.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')()
};
Expand Down
4 changes: 2 additions & 2 deletions sanitizer/reverse.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ 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')()
boundary_gid: require('../sanitizer/_gids')('boundary')
};

// middleware
Expand Down
4 changes: 2 additions & 2 deletions sanitizer/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ 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')(),
request_language: require('../sanitizer/_request_language')(),
boundary_gid: require('../sanitizer/_boundary_gid')()
boundary_gid: require('../sanitizer/_gids')('boundary')
};

return ( req, res, next ) => {
Expand Down
4 changes: 2 additions & 2 deletions sanitizer/structured_geocoding.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ 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')()
boundary_gid: require('../sanitizer/_gids')('boundary')
};

return ( req, res, next ) => {
Expand Down
58 changes: 58 additions & 0 deletions test/unit/fixture/autocomplete_focus_country.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
module.exports = {
'query': {
'bool': {
'must': [
{
'constant_score': {
'filter': {
'multi_match': {
'type': 'phrase',
'query': 'test',
'fields': ['name.default', 'name.en'],
'analyzer': 'peliasQuery',
'boost': 100,
'slop': 3,
},
},
},
},
],
'should': [
{
'function_score': {
'query': { 'match_all': {} },
'max_boost': 20,
'functions': [
{ 'field_value_factor': { 'modifier': 'log1p', 'field': 'popularity', 'missing': 1 }, 'weight': 1 },
],
'score_mode': 'first',
'boost_mode': 'replace',
},
},
{
'function_score': {
'query': { 'match_all': {} },
'max_boost': 20,
'functions': [
{ 'field_value_factor': { 'modifier': 'log1p', 'field': 'population', 'missing': 1 }, 'weight': 3 },
],
'score_mode': 'first',
'boost_mode': 'replace',
},
},
{
'multi_match': {
'type': 'best_fields',
'query': 'ABC',
'fields': ['parent.country_a', 'parent.dependency_a'],
'analyzer': 'standard',
'boost': 1.5,
},
},
],
},
},
'size': 20,
'track_scores': true,
'sort': ['_score'],
};
58 changes: 58 additions & 0 deletions test/unit/fixture/autocomplete_focus_gid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
module.exports = {
'query': {
'bool': {
'must': [
{
'constant_score': {
'filter': {
'multi_match': {
'type': 'phrase',
'query': 'test',
'fields': ['name.default', 'name.en'],
'analyzer': 'peliasQuery',
'boost': 100,
'slop': 3,
},
},
},
},
],
'should': [
{
'function_score': {
'query': { 'match_all': {} },
'max_boost': 20,
'functions': [
{ 'field_value_factor': { 'modifier': 'log1p', 'field': 'popularity', 'missing': 1 }, 'weight': 1 },
],
'score_mode': 'first',
'boost_mode': 'replace',
},
},
{
'function_score': {
'query': { 'match_all': {} },
'max_boost': 20,
'functions': [
{ 'field_value_factor': { 'modifier': 'log1p', 'field': 'population', 'missing': 1 }, 'weight': 3 },
],
'score_mode': 'first',
'boost_mode': 'replace',
},
},
{
'multi_match': {
'type': 'best_fields',
'query': '123',
'fields': ['parent.*_id'],
'analyzer': 'standard',
'boost': 1.5,
},
},
],
},
},
'size': 20,
'track_scores': true,
'sort': ['_score'],
};
Loading