mirror of https://github.com/pelias/api.git
Peter Johnson
6 years ago
committed by
Julian Simioni
10 changed files with 264 additions and 172 deletions
@ -1,51 +1,121 @@
|
||||
//example input
|
||||
//{
|
||||
// "source": {
|
||||
// "openstreetmap": 5
|
||||
// },
|
||||
// "layer": {
|
||||
// "street": 3,
|
||||
// "country": 5
|
||||
// }
|
||||
//}
|
||||
|
||||
function generateTermQuery(field, value, boost) { |
||||
return { |
||||
constant_score: { |
||||
boost: boost, |
||||
query: { |
||||
term: { |
||||
[field]: value, |
||||
} |
||||
/** |
||||
This view allows users to specify a custom boost for sources and layers. |
||||
|
||||
The view is implemented using a 'function_score' query, which enumerates multiple 'functions', each |
||||
function will assign a 'score' to each document when matched. |
||||
|
||||
A document can match more than one function, in this case the 'score_mode' is used to decide how these |
||||
scores are combined, the default is 'sum'. |
||||
|
||||
Likewise, a document can also match zero functions, in this case it is assigned a score of 'min_score'. |
||||
|
||||
The computed score is then multiplied by the 'boost' value in order to come up with the final boost value |
||||
which will be assigned to that document. The 'boost' value is essentially a hard-coded multiplier for the score. |
||||
|
||||
The 'max_boost' property is simply a ceiling for this computed boost, if the computed boosted is higher than |
||||
max_boost it will be assigned the value of max_boost instead. |
||||
|
||||
Note: This is a simple use of the 'function_score' query, as such we don't use the 'boost_mode' property |
||||
(because there is no query section) and the 'weight' values we assign are simply returned verbatim |
||||
(because we use filter queries for the function scoring). |
||||
|
||||
ref: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html
|
||||
|
||||
example config section: |
||||
{ |
||||
"source": { |
||||
"openstreetmap": 5 |
||||
}, |
||||
"layer": { |
||||
"street": 3, |
||||
"country": 5 |
||||
} |
||||
} |
||||
}; |
||||
} |
||||
|
||||
module.exports = function( configuration ) { |
||||
return function( ) { |
||||
if (!configuration) { |
||||
return null; |
||||
example query: |
||||
{ |
||||
"function_score": { |
||||
"query": { |
||||
"match_all": {} |
||||
}, |
||||
"functions": [{ |
||||
"filter": { |
||||
"match": { |
||||
"layer": "intersections" |
||||
} |
||||
const filters = []; |
||||
['source', 'layer'].forEach(function(target) { |
||||
if (configuration[target]) { |
||||
Object.keys(configuration[target]).forEach(function(item) { |
||||
filters.push(generateTermQuery(target, item, configuration[target][item])); |
||||
}); |
||||
}, |
||||
"weight": 1.6 |
||||
},{ |
||||
"filter": { |
||||
"match": { |
||||
"layer": "stops" |
||||
} |
||||
}); |
||||
}, |
||||
"weight": 2.4 |
||||
}], |
||||
"boost": 5, |
||||
"max_boost": 40, |
||||
"score_mode": "sum", |
||||
"boost_mode": "multiply", |
||||
"min_score": 1 |
||||
} |
||||
} |
||||
**/ |
||||
|
||||
if (filters.length === 0) { |
||||
// supported top-level config items
|
||||
const TARGETS = ['source', 'layer']; |
||||
|
||||
module.exports = function( config ) { |
||||
|
||||
// no valid config to use, fail now, don't render this view.
|
||||
if( !config ) { return function(){ return null; }; } |
||||
|
||||
return function( vs ) { |
||||
|
||||
// validate required params
|
||||
if( !vs.isset('custom:boosting:min_score') || |
||||
!vs.isset('custom:boosting:boost') || |
||||
!vs.isset('custom:boosting:max_boost') || |
||||
!vs.isset('custom:boosting:score_mode') || |
||||
!vs.isset('custom:boosting:boost_mode') ){ |
||||
return null; |
||||
} else if (filters.length === 1) { |
||||
return filters[0]; |
||||
} else { |
||||
return { |
||||
bool: { |
||||
should: filters |
||||
} |
||||
|
||||
// base 'function_score' view
|
||||
var view = { |
||||
'function_score': { |
||||
'query': { 'match_all': {} }, // apply to all documents
|
||||
'functions': [], // a list of functions which contribute to a 'score' for each document
|
||||
'min_score': vs.var('custom:boosting:min_score'), |
||||
'boost': vs.var('custom:boosting:boost'), |
||||
'max_boost': vs.var('custom:boosting:max_boost'), |
||||
'score_mode': vs.var('custom:boosting:score_mode'), |
||||
'boost_mode': vs.var('custom:boosting:boost_mode') |
||||
}, |
||||
}; |
||||
|
||||
// iterate over supported targets and their values
|
||||
TARGETS.forEach( function( target ) { |
||||
if( 'object' === typeof config[target] ) { |
||||
Object.keys(config[target]).forEach(function(value) { |
||||
|
||||
// add a scoring function for this target, assigning a weight
|
||||
let weight = config[target][value]; |
||||
view.function_score.functions.push({ |
||||
'weight': isNaN(weight) ? 1 : weight, |
||||
'filter': { |
||||
'match': { |
||||
[target]: value |
||||
} |
||||
} |
||||
}); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
// no functions were generated, fail now, don't render this view.
|
||||
if( view.function_score.functions.length === 0 ) { return null; } |
||||
|
||||
return view; |
||||
}; |
||||
}; |
||||
|
Loading…
Reference in new issue