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
|
/** |
||||||
//{
|
This view allows users to specify a custom boost for sources and layers. |
||||||
// "source": {
|
|
||||||
// "openstreetmap": 5
|
The view is implemented using a 'function_score' query, which enumerates multiple 'functions', each |
||||||
// },
|
function will assign a 'score' to each document when matched. |
||||||
// "layer": {
|
|
||||||
// "street": 3,
|
A document can match more than one function, in this case the 'score_mode' is used to decide how these |
||||||
// "country": 5
|
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'. |
||||||
|
|
||||||
function generateTermQuery(field, value, boost) { |
The computed score is then multiplied by the 'boost' value in order to come up with the final boost value |
||||||
return { |
which will be assigned to that document. The 'boost' value is essentially a hard-coded multiplier for the score. |
||||||
constant_score: { |
|
||||||
boost: boost, |
The 'max_boost' property is simply a ceiling for this computed boost, if the computed boosted is higher than |
||||||
query: { |
max_boost it will be assigned the value of max_boost instead. |
||||||
term: { |
|
||||||
[field]: value, |
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 ) { |
example query: |
||||||
return function( ) { |
{ |
||||||
if (!configuration) { |
"function_score": { |
||||||
return null; |
"query": { |
||||||
|
"match_all": {} |
||||||
|
}, |
||||||
|
"functions": [{ |
||||||
|
"filter": { |
||||||
|
"match": { |
||||||
|
"layer": "intersections" |
||||||
} |
} |
||||||
const filters = []; |
}, |
||||||
['source', 'layer'].forEach(function(target) { |
"weight": 1.6 |
||||||
if (configuration[target]) { |
},{ |
||||||
Object.keys(configuration[target]).forEach(function(item) { |
"filter": { |
||||||
filters.push(generateTermQuery(target, item, configuration[target][item])); |
"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; |
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