Browse Source

Merge pull request #229 from pelias/query

merge query changes
pull/242/head
Peter Johnson a.k.a. insertcoffee 9 years ago
parent
commit
5004532c13
  1. 3
      controller/search.js
  2. 2
      helper/types.js
  3. 1
      package.json
  4. 59
      query/reverse.js
  5. 285
      query/search.js
  6. 5
      sanitiser/_text.js
  7. 43
      test/unit/fixture/reverse_standard.js
  8. 206
      test/unit/fixture/search_full_address.js
  9. 50
      test/unit/fixture/search_linguistic_bbox.js
  10. 68
      test/unit/fixture/search_linguistic_focus.js
  11. 79
      test/unit/fixture/search_linguistic_focus_bbox.js
  12. 39
      test/unit/fixture/search_linguistic_only.js
  13. 98
      test/unit/fixture/search_partial_address.js
  14. 190
      test/unit/fixture/search_regions_address.js
  15. 59
      test/unit/fixture/sort_default.js
  16. 189
      test/unit/query/reverse.js
  17. 726
      test/unit/query/search.js
  18. 2
      test/unit/sanitiser/search.js

3
controller/search.js

@ -15,7 +15,8 @@ function setup( backend, query ){
body: query( req.clean ) body: query( req.clean )
}; };
if ( req.clean.hasOwnProperty('type') ) { // ?
if( req.clean.hasOwnProperty('type') ){
cmd.type = req.clean.type; cmd.type = req.clean.type;
} }

2
helper/types.js

@ -5,7 +5,7 @@ var valid_types = require( '../query/types' );
*/ */
var intersection = function intersection(set1, set2) { var intersection = function intersection(set1, set2) {
return set2.filter(function(value) { return set2.filter(function(value) {
return set1.indexOf(value) !== -1; return set1.indexOf(value) !== -1;
}); });
}; };

1
package.json

@ -49,6 +49,7 @@
"pelias-config": "^1.0.1", "pelias-config": "^1.0.1",
"pelias-esclient": "0.0.25", "pelias-esclient": "0.0.25",
"pelias-logger": "^0.0.8", "pelias-logger": "^0.0.8",
"pelias-query": "^1.1.0",
"pelias-schema": "1.0.0", "pelias-schema": "1.0.0",
"pelias-suggester-pipeline": "2.0.2", "pelias-suggester-pipeline": "2.0.2",
"stats-lite": "^1.0.3", "stats-lite": "^1.0.3",

59
query/reverse.js

@ -1,33 +1,52 @@
var queries = require('geopipes-elasticsearch-backend').queries, var peliasQuery = require('pelias-query'),
sort = require('./sort'); sort = require('./sort');
function generate( params ){ //------------------------------
// reverse geocode query
//------------------------------
var query = new peliasQuery.layout.FilteredBooleanQuery();
var centroid = { // scoring boost
lat: params.lat, query.sort( peliasQuery.view.sort_distance );
lon: params.lon
};
var query = queries.distance( centroid, { // non-scoring hard filters
size: params.size || 1, query.filter( peliasQuery.view.boundary_circle );
sort: true,
distance: '500km' // --------------------------------
function generateQuery( clean ){
var vs = new peliasQuery.Vars( peliasQuery.defaults );
// set defaults
vs.set({
'size': 1,
'boundary:circle:radius': '500km'
}); });
query.sort = query.sort.concat( sort( params ) ); // set size
if( clean.size ){
vs.var( 'size', clean.size );
}
if ( params.categories && params.categories.length > 0 ) { // focus point centroid
addCategoriesFilter( query, params.categories ); if( clean.lat && clean.lon ){
vs.set({
// focus point to score by distance
'focus:point:lat': clean.lat,
'focus:point:lon': clean.lon,
// bounding circle
'boundary:circle:lat': clean.lat,
'boundary:circle:lon': clean.lon,
});
} }
return query; var result = query.render( vs );
}
function addCategoriesFilter( query, categories ) { // @todo: remove this hack
query.query.filtered.filter.bool.must.push({ return JSON.parse( JSON.stringify( result ) );
terms: { category: categories }
});
} }
module.exports = generate; module.exports = generateQuery;

285
query/search.js

@ -1,188 +1,161 @@
var queries = require('geopipes-elasticsearch-backend').queries, var peliasQuery = require('pelias-query'),
sort = require('../query/sort'), sort = require('../query/sort'),
adminFields = require('../helper/adminFields')(), adminFields = require('../helper/adminFields')();
addressWeights = require('../helper/address_weights');
//------------------------------
// general-purpose search query
//------------------------------
var query = new peliasQuery.layout.FilteredBooleanQuery();
function generate( params ){ // mandatory matches
var centroid = null; query.score( peliasQuery.view.boundary_country, 'must' );
query.score( peliasQuery.view.ngrams, 'must' );
if ( params.lat && params.lon ){ // scoring boost
centroid = { query.score( peliasQuery.view.phrase );
lat: params.lat, query.score( peliasQuery.view.focus );
lon: params.lon
}; // address components
} query.score( peliasQuery.view.address('housenumber') );
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('locality') );
query.score( peliasQuery.view.admin('neighborhood') );
// non-scoring hard filters
query.filter( peliasQuery.view.boundary_circle );
query.filter( peliasQuery.view.boundary_rect );
// --------------------------------
var query = queries.distance( centroid, { size: params.size } ); function generateQuery( clean ){
var text = params.text;
if (params.bbox) { var vs = new peliasQuery.Vars( peliasQuery.defaults );
query = queries.bbox ( centroid, { size: params.size, bbox: params.bbox } );
// set input text
vs.var( 'input:name', clean.text );
// set size
if( clean.size ){
vs.var( 'size', clean.size );
} }
query.query.filtered.query = { // focus point
'bool': { if( clean.lat && clean.lon ){
'must': [], vs.set({
'should': [] 'focus:point:lat': clean.lat,
} 'focus:point:lon': clean.lon
}; });
}
if (params.parsed_text) {
// update text
if (params.parsed_text.number && params.parsed_text.street) {
text = params.parsed_text.number + ' ' + params.parsed_text.street;
} else if (params.parsed_text.admin_parts) {
text = params.parsed_text.name;
}
addParsedMatch(query, text, params.parsed_text); // bbox
if( clean.bbox ){
vs.set({
'boundary:rect:top': clean.bbox.top,
'boundary:rect:right': clean.bbox.right,
'boundary:rect:bottom': clean.bbox.bottom,
'boundary:rect:left': clean.bbox.left
});
} }
// add search condition to distance query // address parsing
query.query.filtered.query.bool.must.push({ if( clean.parsed_text ){
'match': {
'name.default': { // is it a street address?
'query': text, var isStreetAddress = clean.parsed_text.hasOwnProperty('number') && clean.parsed_text.hasOwnProperty('street');
'analyzer': 'peliasOneEdgeGram' if( isStreetAddress ){
} vs.var( 'input:name', clean.parsed_text.number + ' ' + clean.parsed_text.street );
} }
});
// I don't understand this
// add phrase matching query else if( clean.parsed_text.admin_parts ) {
// note: this is required for shingle/phrase matching vs.var( 'input:name', clean.parsed_text.name );
query.query.filtered.query.bool.should.push({
'match': {
'phrase.default': {
'query': text,
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2
}
} }
});
query.sort = query.sort.concat( sort( params ) ); // or this..
else {
console.warn( 'chaos monkey asks: what happens now?' );
console.log( clean );
try{ throw new Error(); } catch(e){ console.error( e.stack ); } // print a stack trace
}
return query; // ==== add parsed matches [address components] ====
}
/** // house number
* Traverse the parsed text object, containing all the address parts detected in query string. if( clean.parsed_text.hasOwnProperty('number') ){
* Add matches to query for each identifiable component. vs.var( 'input:housenumber', clean.parsed_text.number );
* }
* @param {Object} query
* @param {string} defaultText
* @param {Object} parsedText
*/
function addParsedMatch(query, defaultText, parsedText) {
query.query.filtered.query.bool.should = query.query.filtered.query.bool.should || [];
// copy expected admin fields so we can remove them as we parse the address
var unmatchedAdminFields = adminFields.slice();
// address
// number, street, postalcode
addMatch(query, unmatchedAdminFields, 'address.number', parsedText.number, addressWeights.number);
addMatch(query, unmatchedAdminFields, 'address.street', parsedText.street, addressWeights.street);
addMatch(query, unmatchedAdminFields, 'address.zip', parsedText.postalcode, addressWeights.zip);
// city
// admin2, locality, local_admin, neighborhood
addMatch(query, unmatchedAdminFields, 'admin2', parsedText.city, addressWeights.admin2);
// state
// admin1, admin1_abbr
addMatch(query, unmatchedAdminFields, 'admin1_abbr', parsedText.state, addressWeights.admin1_abbr);
// country
// admin0, alpha3
addMatch(query, unmatchedAdminFields, 'alpha3', parsedText.country, addressWeights.alpha3);
addUnmatchedAdminFieldsToQuery(query, unmatchedAdminFields, parsedText, defaultText);
}
/** // street name
* Check for additional admin fields in the parsed input, and if any was found if( clean.parsed_text.hasOwnProperty('street') ){
* combine into single string and match against all unmatched admin fields. vs.var( 'input:street', clean.parsed_text.street );
* }
* @param {Object} query
* @param {Array} unmatchedAdminFields
* @param {Object} parsedInput
* @param {string} defaultInput
*/
function addUnmatchedAdminFieldsToQuery(query, unmatchedAdminFields, parsedInput, defaultInput) {
if (unmatchedAdminFields.length === 0 ) {
return;
}
var leftovers = []; // postal code
if( clean.parsed_text.hasOwnProperty('postalcode') ){
vs.var( 'input:postcode', clean.parsed_text.postalcode );
}
if (parsedInput.admin_parts) { // ==== add parsed matches [admin components] ====
leftovers.push(parsedInput.admin_parts);
}
else if (parsedInput.regions) {
leftovers.push(parsedInput.regions);
}
if (leftovers.length === 0) { // city
return; if( clean.parsed_text.hasOwnProperty('city') ){
} vs.var( 'input:admin2', clean.parsed_text.city );
}
leftovers = leftovers.join(' '); // state
if( clean.parsed_text.hasOwnProperty('state') ){
vs.var( 'input:admin1_abbr', clean.parsed_text.state );
}
// if there are additional regions/admin_parts found // country
if (leftovers !== defaultInput) { if( clean.parsed_text.hasOwnProperty('country') ){
unmatchedAdminFields.forEach(function (key) { vs.var( 'input:alpha3', clean.parsed_text.country );
// combine all the leftover parts into one string }
addMatch(query, [], key, leftovers);
});
}
}
/** // ==== deal with the 'leftover' components ====
* Add key:value match to query. Apply boost if specified. // @todo: clean up this code
*
* @param {Object} query
* @param {Array} unmatched
* @param {string} key
* @param {string|number|undefined} value
* @param {number|undefined} [boost] optional
*/
function addMatch(query, unmatched, key, value, boost) { // jshint ignore:line
if (typeof value === 'undefined') {
return;
}
var match = {}; // a concept called 'leftovers' which is just 'admin_parts' /or 'regions'.
var leftoversString = '';
if( clean.parsed_text.hasOwnProperty('admin_parts') ){
leftoversString = clean.parsed_text.admin_parts;
}
else if( clean.parsed_text.hasOwnProperty('regions') ){
leftoversString = clean.parsed_text.regions.join(' ');
}
if (boost) { // if we have 'leftovers' then assign them to any fields which
match[key] = { // currently don't have a value assigned.
query: value, if( leftoversString.length ){
boost: boost var unmatchedAdminFields = adminFields.slice();
};
} // cycle through fields and set fields which
else { // are still currently unset
match[key] = value; unmatchedAdminFields.forEach( function( key ){
if( !vs.isset( 'input:' + key ) ){
vs.var( 'input:' + key, leftoversString );
}
});
}
} }
query.query.filtered.query.bool.should.push({ 'match': match }); var result = query.render( vs );
removeFromUnmatched(unmatched, key); // @todo: remove unnessesary sort conditions
} result.sort = result.sort.concat( sort( clean ) );
/** // @todo: remove this hack
* If key is found in unmatched list, remove it from the array return JSON.parse( JSON.stringify( result ) );
*
* @param {Array} unmatched
* @param {string} key
*/
function removeFromUnmatched(unmatched, key) {
var index = unmatched.indexOf(key);
if (index !== -1) {
unmatched.splice(index, 1);
}
} }
module.exports = generate; module.exports = generateQuery;

5
sanitiser/_text.js

@ -1,16 +1,17 @@
var isObject = require('is-object'); var isObject = require('is-object');
var query_parser = require('../helper/query_parser'); var query_parser = require('../helper/query_parser');
// validate inputs, convert types and apply defaults // validate texts, convert types and apply defaults
function sanitize( req ){ function sanitize( req ){
req.clean = req.clean || {}; req.clean = req.clean || {};
var params= req.query; var params= req.query;
// ensure the input params are a valid object // ensure the text params are a valid object
if( !isObject( params ) ){ if( !isObject( params ) ){
params = {}; params = {};
} }
// text text
if('string' !== typeof params.text || !params.text.length){ if('string' !== typeof params.text || !params.text.length){
return { return {
'error': true, 'error': true,

43
test/unit/fixture/reverse_standard.js

@ -0,0 +1,43 @@
module.exports = {
'query': {
'filtered': {
'query': {
'bool': {}
},
'filter': {
'bool': {
'must': [
{
'geo_distance': {
'distance': '500km',
'distance_type': 'plane',
'optimize_bbox': 'indexed',
'_cache': true,
'center_point': {
'lat': 29.49136,
'lon': -82.50622
}
}
}
]
}
}
}
},
'sort': [
'_score',
{
'_geo_distance': {
'center_point': {
'lat': 29.49136,
'lon': -82.50622
},
'order': 'asc',
'distance_type': 'plane'
}
}
],
'size': 1,
'track_scores': true
};

206
test/unit/fixture/search_full_address.js

@ -0,0 +1,206 @@
var peliasQuery = require('pelias-query'),
vs = new peliasQuery.Vars( peliasQuery.defaults );
module.exports = {
'query': {
'filtered': {
'query': {
'bool': {
'must': [{
'match': {
'name.default': {
'query': '123 main st',
'analyzer': 'peliasOneEdgeGram',
'boost': 1
}
}
}],
'should': [{
'match': {
'phrase.default': {
'query': '123 main st',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2,
'boost': 1
}
}
},{
'match': {
'address.number': {
'query': 123,
'boost': vs.var('address:housenumber:boost').get(),
'analyzer': vs.var('address:housenumber:analyzer').get()
}
}
}, {
'match': {
'address.street': {
'query': 'main st',
'boost': vs.var('address:street:boost').get(),
'analyzer': vs.var('address:street:analyzer').get()
}
}
}, {
'match': {
'address.zip': {
'query': 10010,
'boost': vs.var('address:postcode:boost').get(),
'analyzer': vs.var('address:postcode:analyzer').get()
}
}
}, {
'match': {
'alpha3': {
'query': 'USA',
'boost': vs.var('admin:alpha3:boost').get(),
'analyzer': vs.var('admin:alpha3:analyzer').get()
}
}
}, {
'match': {
'admin0': {
'query': 'new york',
'boost': vs.var('admin:admin0:boost').get(),
'analyzer': vs.var('admin:admin0:analyzer').get()
}
}
}, {
'match': {
'admin1': {
'query': 'new york',
'boost': vs.var('admin:admin1:boost').get(),
'analyzer': vs.var('admin:admin1:analyzer').get()
}
}
}, {
'match': {
'admin1_abbr': {
'query': 'NY',
'boost': vs.var('admin:admin1_abbr:boost').get(),
'analyzer': vs.var('admin:admin1_abbr:analyzer').get()
}
}
}, {
'match': {
'admin2': {
'query': 'new york',
'boost': vs.var('admin:admin2:boost').get(),
'analyzer': vs.var('admin:admin2:analyzer').get()
}
}
}, {
'match': {
'local_admin': {
'query': 'new york',
'boost': vs.var('admin:local_admin:boost').get(),
'analyzer': vs.var('admin:local_admin:analyzer').get()
}
}
}, {
'match': {
'locality': {
'query': 'new york',
'boost': vs.var('admin:locality:boost').get(),
'analyzer': vs.var('admin:locality:analyzer').get()
}
}
}, {
'match': {
'neighborhood': {
'query': 'new york',
'boost': vs.var('admin:neighborhood:boost').get(),
'analyzer': vs.var('admin:neighborhood:analyzer').get()
}
}
}]
}
},
'filter': {
'bool': {
'must': []
}
}
}
},
'size': 10,
'sort': [
'_score',
{
'_script': {
'file': 'admin_boost',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': 'popularity',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': 'population',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': {
'admin0': 4,
'admin1': 3,
'admin2': 2,
'local_admin': 1,
'locality': 1,
'neighborhood': 1
}
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'category_weights': {
'transport:air': 2,
'transport:air:aerodrome': 2,
'transport:air:airport': 2
}
},
'file': 'category',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': {
'geoname': 0,
'address': 4,
'osmnode': 6,
'osmway': 6,
'poi-address': 8,
'neighborhood': 10,
'local_admin': 12,
'locality': 12,
'admin2': 12,
'admin1': 14,
'admin0': 2
}
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
}
],
'track_scores': true
};

50
test/unit/fixture/search_linguistic_bbox.js

@ -0,0 +1,50 @@
module.exports = {
'query': {
'filtered': {
'query': {
'bool': {
'must': [{
'match': {
'name.default': {
'query': 'test',
'boost': 1,
'analyzer': 'peliasOneEdgeGram'
}
}
}],
'should': [{
'match': {
'phrase.default': {
'query': 'test',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'boost': 1,
'slop': 2
}
}
}]
}
},
'filter': {
'bool': {
'must': [{
'geo_bounding_box': {
'center_point': {
'top': 47.47,
'right': -61.84,
'bottom': 11.51,
'left': -103.16
},
'_cache': true,
'type': 'indexed'
}
}]
}
}
}
},
'sort': [ '_sort' ],
'size': 10,
'track_scores': true
};

68
test/unit/fixture/search_linguistic_focus.js

@ -0,0 +1,68 @@
module.exports = {
'query': {
'filtered': {
'query': {
'bool': {
'must': [{
'match': {
'name.default': {
'query': 'test',
'boost': 1,
'analyzer': 'peliasOneEdgeGram'
}
}
}],
'should': [{
'match': {
'phrase.default': {
'query': 'test',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'boost': 1,
'slop': 2
}
}
}, {
'function_score': {
'query': {
'match': {
'phrase.default': {
'analyzer': 'peliasPhrase',
'type': 'phrase',
'boost': 1,
'slop': 2,
'query': 'test'
}
}
},
'functions': [{
'linear': {
'center_point': {
'origin': {
'lat': 29.49136,
'lon': -82.50622
},
'offset': '1km',
'scale': '50km',
'decay': 0.5
}
}
}],
'score_mode': 'avg',
'boost_mode': 'replace'
}
}]
}
},
'filter': {
'bool': {
'must': []
}
}
}
},
'sort': [ '_sort' ],
'size': 10,
'track_scores': true
};

79
test/unit/fixture/search_linguistic_focus_bbox.js

@ -0,0 +1,79 @@
module.exports = {
'query': {
'filtered': {
'query': {
'bool': {
'must': [{
'match': {
'name.default': {
'query': 'test',
'boost': 1,
'analyzer': 'peliasOneEdgeGram'
}
}
}],
'should': [{
'match': {
'phrase.default': {
'query': 'test',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'boost': 1,
'slop': 2
}
}
}, {
'function_score': {
'query': {
'match': {
'phrase.default': {
'analyzer': 'peliasPhrase',
'type': 'phrase',
'boost': 1,
'slop': 2,
'query': 'test'
}
}
},
'functions': [{
'linear': {
'center_point': {
'origin': {
'lat': 29.49136,
'lon': -82.50622
},
'offset': '1km',
'scale': '50km',
'decay': 0.5
}
}
}],
'score_mode': 'avg',
'boost_mode': 'replace'
}
}]
}
},
'filter': {
'bool': {
'must': [{
'geo_bounding_box': {
'center_point': {
'top': 47.47,
'right': -61.84,
'bottom': 11.51,
'left': -103.16
},
'_cache': true,
'type': 'indexed'
}
}]
}
}
}
},
'sort': [ '_sort' ],
'size': 10,
'track_scores': true
};

39
test/unit/fixture/search_linguistic_only.js

@ -0,0 +1,39 @@
module.exports = {
'query': {
'filtered': {
'query': {
'bool': {
'must': [{
'match': {
'name.default': {
'query': 'test',
'boost': 1,
'analyzer': 'peliasOneEdgeGram'
}
}
}],
'should': [{
'match': {
'phrase.default': {
'query': 'test',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'boost': 1,
'slop': 2
}
}
}]
}
},
'filter': {
'bool': {
'must': []
}
}
}
},
'sort': [ '_score' ],
'size': 10,
'track_scores': true
};

98
test/unit/fixture/search_partial_address.js

@ -0,0 +1,98 @@
var peliasQuery = require('pelias-query'),
vs = new peliasQuery.Vars( peliasQuery.defaults );
module.exports = {
'query': {
'filtered': {
'query': {
'bool': {
'must': [{
'match': {
'name.default': {
'query': 'soho grand',
'analyzer': 'peliasOneEdgeGram',
'boost': 1
}
}
}],
'should': [{
'match': {
'phrase.default': {
'query': 'soho grand',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2,
'boost': 1
}
}
},{
'match': {
'admin0': {
'query': 'new york',
'boost': vs.var('admin:admin0:boost').get(),
'analyzer': vs.var('admin:admin0:analyzer').get()
}
}
}, {
'match': {
'admin1': {
'query': 'new york',
'boost': vs.var('admin:admin1:boost').get(),
'analyzer': vs.var('admin:admin1:analyzer').get()
}
}
}, {
'match': {
'admin1_abbr': {
'query': 'new york',
'boost': vs.var('admin:admin1_abbr:boost').get(),
'analyzer': vs.var('admin:admin1_abbr:analyzer').get()
}
}
}, {
'match': {
'admin2': {
'query': 'new york',
'boost': vs.var('admin:admin2:boost').get(),
'analyzer': vs.var('admin:admin2:analyzer').get()
}
}
}, {
'match': {
'local_admin': {
'query': 'new york',
'boost': vs.var('admin:local_admin:boost').get(),
'analyzer': vs.var('admin:local_admin:analyzer').get()
}
}
}, {
'match': {
'locality': {
'query': 'new york',
'boost': vs.var('admin:locality:boost').get(),
'analyzer': vs.var('admin:locality:analyzer').get()
}
}
}, {
'match': {
'neighborhood': {
'query': 'new york',
'boost': vs.var('admin:neighborhood:boost').get(),
'analyzer': vs.var('admin:neighborhood:analyzer').get()
}
}
}]
}
},
'filter': {
'bool': {
'must': []
}
}
}
},
'size': 10,
'sort': [ '_score' ],
'track_scores': true
};

190
test/unit/fixture/search_regions_address.js

@ -0,0 +1,190 @@
var peliasQuery = require('pelias-query'),
vs = new peliasQuery.Vars( peliasQuery.defaults );
module.exports = {
'query': {
'filtered': {
'query': {
'bool': {
'must': [{
'match': {
'name.default': {
'query': '1 water st',
'analyzer': 'peliasOneEdgeGram',
'boost': 1
}
}
}],
'should': [{
'match': {
'phrase.default': {
'query': '1 water st',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2,
'boost': 1
}
}
},{
'match': {
'address.number': {
'query': 1,
'boost': vs.var('address:housenumber:boost').get(),
'analyzer': vs.var('address:housenumber:analyzer').get()
}
}
}, {
'match': {
'address.street': {
'query': 'water st',
'boost': vs.var('address:street:boost').get(),
'analyzer': vs.var('address:street:analyzer').get()
}
}
}, {
'match': {
'admin0': {
'query': 'manhattan',
'boost': vs.var('admin:admin0:boost').get(),
'analyzer': vs.var('admin:admin0:analyzer').get()
}
}
}, {
'match': {
'admin1': {
'query': 'manhattan',
'boost': vs.var('admin:admin1:boost').get(),
'analyzer': vs.var('admin:admin1:analyzer').get()
}
}
}, {
'match': {
'admin1_abbr': {
'query': 'NY',
'boost': vs.var('admin:admin1_abbr:boost').get(),
'analyzer': vs.var('admin:admin1_abbr:analyzer').get()
}
}
}, {
'match': {
'admin2': {
'query': 'manhattan',
'boost': vs.var('admin:admin2:boost').get(),
'analyzer': vs.var('admin:admin2:analyzer').get()
}
}
}, {
'match': {
'local_admin': {
'query': 'manhattan',
'boost': vs.var('admin:local_admin:boost').get(),
'analyzer': vs.var('admin:local_admin:analyzer').get()
}
}
}, {
'match': {
'locality': {
'query': 'manhattan',
'boost': vs.var('admin:locality:boost').get(),
'analyzer': vs.var('admin:locality:analyzer').get()
}
}
}, {
'match': {
'neighborhood': {
'query': 'manhattan',
'boost': vs.var('admin:neighborhood:boost').get(),
'analyzer': vs.var('admin:neighborhood:analyzer').get()
}
}
}]
}
},
'filter': {
'bool': {
'must': []
}
}
}
},
'size': 10,
'sort': [
'_score',
{
'_script': {
'file': 'admin_boost',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': 'popularity',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': 'population',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': {
'admin0': 4,
'admin1': 3,
'admin2': 2,
'local_admin': 1,
'locality': 1,
'neighborhood': 1
}
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'category_weights': {
'transport:air': 2,
'transport:air:aerodrome': 2,
'transport:air:airport': 2
}
},
'file': 'category',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': {
'geoname': 0,
'address': 4,
'osmnode': 6,
'osmway': 6,
'poi-address': 8,
'neighborhood': 10,
'local_admin': 12,
'locality': 12,
'admin2': 12,
'admin1': 14,
'admin0': 2
}
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
}
],
'track_scores': true
};

59
test/unit/fixture/sort_default.js

@ -0,0 +1,59 @@
var category_weights = require('../../../helper/category_weights');
var admin_weights = require('../../../helper/admin_weights');
var weights = require('pelias-suggester-pipeline').weights;
module.exports = [
'_score',
{
'_script': {
'file': 'admin_boost',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': 'popularity',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': 'population',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': admin_weights
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'category_weights': category_weights.default
},
'file': 'category',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': weights
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
}
];

189
test/unit/query/reverse.js

@ -1,15 +1,16 @@
var generate = require('../../../query/reverse'); var generate = require('../../../query/reverse');
var admin_boost = 'admin_boost';
var population = 'population';
var popularity = 'popularity';
var category = 'category';
var category_weights = require('../../../helper/category_weights');
var admin_weights = require('../../../helper/admin_weights');
var weights = require('pelias-suggester-pipeline').weights;
module.exports.tests = {}; module.exports.tests = {};
function debug( a,b ){
console.log( '----------------------' );
console.log( JSON.stringify( a, null, 2 ) );
console.log( '----------------------' );
console.log( JSON.stringify( b, null, 2 ) );
console.log( '----------------------' );
}
module.exports.tests.interface = function(test, common) { module.exports.tests.interface = function(test, common) {
test('valid interface', function(t) { test('valid interface', function(t) {
t.equal(typeof generate, 'function', 'valid function'); t.equal(typeof generate, 'function', 'valid function');
@ -17,185 +18,25 @@ module.exports.tests.interface = function(test, common) {
}); });
}; };
var sort = [
'_score',
{
'_script': {
'file': admin_boost,
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': popularity,
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': population,
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': admin_weights
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'category_weights': category_weights.default
},
'file': category,
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': weights
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
}
];
module.exports.tests.query = function(test, common) { module.exports.tests.query = function(test, common) {
test('valid query', function(t) { test('valid query', function(t) {
var query = generate({ var query = generate({
lat: 29.49136, lon: -82.50622 lat: 29.49136, lon: -82.50622
}); });
var expected = { var expected = require('../fixture/reverse_standard');
'query': {
'filtered': {
'query': {
'match_all': {}
},
'filter': {
'bool': {
'must': [
{
'geo_distance': {
'distance': '500km',
'distance_type': 'plane',
'optimize_bbox': 'indexed',
'_cache': true,
'center_point': {
'lat': '29.49',
'lon': '-82.51'
}
}
}
]
}
}
}
},
'sort': [
'_score',
{
'_geo_distance': {
'center_point': {
'lat': 29.49136,
'lon': -82.50622
},
'order': 'asc',
'unit': 'km'
}
}
].concat(sort.slice(1)),
'size': 1,
'track_scores': true
};
t.deepEqual(query, expected, 'valid reverse query'); t.deepEqual(query, expected, 'valid reverse query');
t.end();
});
test('size fuzz test', function(t) {
// test different sizes // test different sizes
var sizes = [1,2,10,undefined,null]; var sizes = [1,2,10,undefined,null];
sizes.forEach( function(size) { sizes.forEach( function( size ){
query = generate({ var query = generate({
lat: 29.49136, lon: -82.50622, size: size lat: 29.49136, lon: -82.50622, size: size
}); });
expected.size = size ? size : 1;
t.deepEqual(query, expected, 'valid reverse query for size: '+ size);
});
t.end();
});
test('valid query with categories', function(t) {
var params = { lat: 29.49136, lon: -82.50622, categories: ['food', 'education', 'entertainment'] };
var query = generate(params);
var expected = {
'query': {
'filtered': {
'query': {
'match_all': {}
},
'filter': {
'bool': {
'must': [
{
'geo_distance': {
'distance': '500km',
'distance_type': 'plane',
'optimize_bbox': 'indexed',
'_cache': true,
'center_point': {
'lat': '29.49',
'lon': '-82.51'
}
}
},
{
'terms': {
'category': params.categories
}
}
]
}
}
}
},
'sort': [
'_score',
{
'_geo_distance': {
'center_point': {
'lat': 29.49136,
'lon': -82.50622
},
'order': 'asc',
'unit': 'km'
}
}
].concat(sort.slice(1)),
'size': 1,
'track_scores': true
};
t.deepEqual(query, expected, 'valid reverse query with categories'); t.equal( query.size, size ? size : 1, 'valid reverse query for size: '+ size);
// test different sizes
var sizes = [1,2,10,undefined,null];
sizes.forEach( function(size) {
params.size = size;
query = generate(params);
expected.size = size ? size : 1;
t.deepEqual(query, expected, 'valid reverse query for size: '+ size);
}); });
t.end(); t.end();
}); });

726
test/unit/query/search.js

@ -20,113 +20,10 @@ module.exports.tests.interface = function(test, common) {
}); });
}; };
var sort = [ var sort = require('../fixture/sort_default');
'_score',
{
'_script': {
'file': admin_boost,
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': popularity,
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': population,
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': admin_weights
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'category_weights': category_weights.default
},
'file': category,
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': weights
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
}
];
var expected = {
'query': {
'filtered': {
'query': {
'bool': {
'must': [{
'match': {
'name.default': {
'query': 'test',
'analyzer': 'peliasOneEdgeGram'
}
}
}],
'should': [{
'match': {
'phrase.default': {
'query': 'test',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2
}
}
}]
}
},
'filter': {
'bool': {
'must': [
{
'geo_bounding_box': {
'center_point': {
'top': '47.47',
'right': '-61.84',
'bottom':'11.51',
'left': '-103.16'
},
'_cache': true,
'type': 'indexed'
}
}
]
}
}
}
},
'sort': sort,
'size': 10,
'track_scores': true
};
module.exports.tests.query = function(test, common) { module.exports.tests.query = function(test, common) {
test('valid query', function(t) { test('valid search + focus + bbox', function(t) {
var query = generate({ var query = generate({
text: 'test', size: 10, text: 'test', size: 10,
lat: 29.49136, lon: -82.50622, lat: 29.49136, lon: -82.50622,
@ -139,11 +36,14 @@ module.exports.tests.query = function(test, common) {
layers: ['test'] layers: ['test']
}); });
var expected = require('../fixture/search_linguistic_focus_bbox');
expected.sort = sort;
t.deepEqual(query, expected, 'valid search query'); t.deepEqual(query, expected, 'valid search query');
t.end(); t.end();
}); });
test('valid query without lat/lon', function(t) { test('valid search + bbox', function(t) {
var query = generate({ var query = generate({
text: 'test', size: 10, text: 'test', size: 10,
bbox: { bbox: {
@ -155,113 +55,35 @@ module.exports.tests.query = function(test, common) {
layers: ['test'] layers: ['test']
}); });
var expected = require('../fixture/search_linguistic_bbox');
expected.sort = sort;
t.deepEqual(query, expected, 'valid search query'); t.deepEqual(query, expected, 'valid search query');
t.end(); t.end();
}); });
test('valid query with no lat/lon and no bbox', function(t) { test('valid lingustic-only search', function(t) {
var query = generate({ var query = generate({
text: 'test', size: 10, text: 'test', size: 10,
layers: ['test'] layers: ['test']
}); });
var expected = { var expected = require('../fixture/search_linguistic_only');
'query': { expected.sort = sort;
'filtered': {
'query': {
'bool': {
'must': [{
'match': {
'name.default': {
'query': 'test',
'analyzer': 'peliasOneEdgeGram'
}
}
}],
'should': [{
'match': {
'phrase.default': {
'query': 'test',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2
}
}
}]
}
},
'filter': {
'bool': {
'must': []
}
}
}
},
'size': 10,
'sort': sort,
'track_scores': true
};
t.deepEqual(query, expected, 'valid search query'); t.deepEqual(query, expected, 'valid search query');
t.end(); t.end();
}); });
test('valid query without bbox', function(t) { test('search search + focus', function(t) {
var query = generate({ var query = generate({
text: 'test', size: 10, text: 'test', size: 10,
lat: 29.49136, lon: -82.50622, lat: 29.49136, lon: -82.50622,
layers: ['test'] layers: ['test']
}); });
var expected = { var expected = require('../fixture/search_linguistic_focus');
'query': { expected.sort = sort;
'filtered': {
'query': {
'bool': {
'must': [{
'match': {
'name.default': {
'query': 'test',
'analyzer': 'peliasOneEdgeGram'
}
}
}],
'should': [{
'match': {
'phrase.default': {
'query': 'test',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2
}
}
}]
}
},
'filter': {
'bool': {
'must': [
{
'geo_distance': {
'distance': '50km',
'distance_type': 'plane',
'optimize_bbox': 'indexed',
'_cache': true,
'center_point': {
'lat': '29.49',
'lon': '-82.51'
}
}
}
]
}
}
}
},
'sort': ['_score'].concat(sort.slice(1)),
'size': 10,
'track_scores': true
};
t.deepEqual(query, expected, 'valid search query'); t.deepEqual(query, expected, 'valid search query');
t.end(); t.end();
@ -274,195 +96,10 @@ module.exports.tests.query = function(test, common) {
'locality', 'local_admin', 'osmaddress', 'openaddresses' ], 'locality', 'local_admin', 'osmaddress', 'openaddresses' ],
size: 10, size: 10,
details: true, details: true,
parsed_text: parser.get_parsed_address(address) parsed_text: parser.get_parsed_address(address),
}); });
var expected = { var expected = require('../fixture/search_full_address');
'query': {
'filtered': {
'query': {
'bool': {
'must': [
{
'match': {
'name.default': {
'query': '123 main st',
'analyzer': 'peliasOneEdgeGram'
}
}
}
],
'should': [
{
'match': {
'address.number': {
'query': 123,
'boost': address_weights.number
}
}
},
{
'match': {
'address.street': {
'query': 'main st',
'boost': address_weights.street
}
}
},
{
'match': {
'address.zip': {
'query': 10010,
'boost': address_weights.zip
}
}
},
{
'match': {
'admin1_abbr': {
'query': 'NY',
'boost': address_weights.admin1_abbr
}
}
},
{
'match': {
'alpha3': {
'query': 'USA',
'boost': address_weights.alpha3
}
}
},
{
match: {
admin0: 'new york'
}
},
{
match: {
admin1: 'new york'
}
},
{
match: {
admin2: 'new york'
}
},
{
match: {
local_admin: 'new york'
}
},
{
match: {
locality: 'new york'
}
},
{
match: {
neighborhood: 'new york'
}
},
{
'match': {
'phrase.default': {
'query': '123 main st',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2
}
}
}
]
}
},
'filter': {
'bool': {
'must': []
}
}
}
},
'size': 10,
'sort': [
'_score',
{
'_script': {
'file': 'admin_boost',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': 'popularity',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': 'population',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': {
'admin0': 4,
'admin1': 3,
'admin2': 2,
'local_admin': 1,
'locality': 1,
'neighborhood': 1
}
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'category_weights': {
'transport:air': 2,
'transport:air:aerodrome': 2,
'transport:air:airport': 2
}
},
'file': 'category',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': {
'geoname': 0,
'address': 4,
'osmnode': 6,
'osmway': 6,
'poi-address': 8,
'neighborhood': 10,
'local_admin': 12,
'locality': 12,
'admin2': 12,
'admin1': 14,
'admin0': 2
}
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
}
],
'track_scores': true
};
t.deepEqual(query, expected, 'valid search query'); t.deepEqual(query, expected, 'valid search query');
t.end(); t.end();
@ -475,161 +112,11 @@ module.exports.tests.query = function(test, common) {
'locality', 'local_admin', 'osmaddress', 'openaddresses' ], 'locality', 'local_admin', 'osmaddress', 'openaddresses' ],
size: 10, size: 10,
details: true, details: true,
parsed_text: parser.get_parsed_address(partial_address) parsed_text: parser.get_parsed_address(partial_address),
}); });
var expected = { var expected = require('../fixture/search_partial_address');
'query': { expected.sort = sort;
'filtered': {
'query': {
'bool': {
'must': [
{
'match': {
'name.default': {
'query': 'soho grand',
'analyzer': 'peliasOneEdgeGram'
}
}
}
],
'should': [
{
'match': {
'admin0': 'new york'
}
},
{
'match': {
'admin1': 'new york'
}
},
{
'match': {
'admin1_abbr': 'new york'
}
},
{
'match': {
'admin2': 'new york'
}
},
{
'match': {
'local_admin': 'new york'
}
},
{
'match': {
'locality': 'new york'
}
},
{
'match': {
'neighborhood': 'new york'
}
},
{
'match': {
'phrase.default': {
'query': 'soho grand',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2
}
}
}
]
}
},
'filter': {
'bool': {
'must': []
}
}
}
},
'size': 10,
'sort': [
'_score',
{
'_script': {
'file': 'admin_boost',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': 'popularity',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': 'population',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': {
'admin0': 4,
'admin1': 3,
'admin2': 2,
'local_admin': 1,
'locality': 1,
'neighborhood': 1
}
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'category_weights': {
'transport:air': 2,
'transport:air:aerodrome': 2,
'transport:air:airport': 2,
'admin': 2
}
},
'file': 'category',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': {
'geoname': 0,
'address': 4,
'osmnode': 6,
'osmway': 6,
'poi-address': 8,
'neighborhood': 10,
'local_admin': 12,
'locality': 12,
'admin2': 12,
'admin1': 14,
'admin0': 2
}
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
}
],
'track_scores': true
};
t.deepEqual(query, expected, 'valid search query'); t.deepEqual(query, expected, 'valid search query');
t.end(); t.end();
@ -642,179 +129,10 @@ module.exports.tests.query = function(test, common) {
'locality', 'local_admin', 'osmaddress', 'openaddresses' ], 'locality', 'local_admin', 'osmaddress', 'openaddresses' ],
size: 10, size: 10,
details: true, details: true,
parsed_text: parser.get_parsed_address(partial_address) parsed_text: parser.get_parsed_address(partial_address),
}); });
var expected = { var expected = require('../fixture/search_regions_address');
'query': {
'filtered': {
'query': {
'bool': {
'must': [
{
'match': {
'name.default': {
'query': '1 water st',
'analyzer': 'peliasOneEdgeGram'
}
}
}
],
'should': [
{
'match': {
'address.number': {
'query': 1,
'boost': address_weights.number
}
}
},
{
'match': {
'address.street': {
'query': 'water st',
'boost': address_weights.street
}
}
},
{
'match': {
'admin1_abbr': {
'query': 'NY',
'boost': address_weights.admin1_abbr
}
}
},
{
'match': {
'admin0': 'manhattan'
}
},
{
'match': {
'admin1': 'manhattan'
}
},
{
'match': {
'admin2': 'manhattan'
}
},
{
'match': {
'local_admin': 'manhattan'
}
},
{
'match': {
'locality': 'manhattan'
}
},
{
'match': {
'neighborhood': 'manhattan'
}
},
{
'match': {
'phrase.default': {
'query': '1 water st',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2
}
}
}
]
}
},
'filter': {
'bool': {
'must': []
}
}
}
},
'size': 10,
'sort': [
'_score',
{
'_script': {
'file': 'admin_boost',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': 'popularity',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'file': 'population',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': {
'admin0': 4,
'admin1': 3,
'admin2': 2,
'local_admin': 1,
'locality': 1,
'neighborhood': 1
}
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'category_weights': {
'transport:air': 2,
'transport:air:aerodrome': 2,
'transport:air:airport': 2
}
},
'file': 'category',
'type': 'number',
'order': 'desc'
}
},
{
'_script': {
'params': {
'weights': {
'geoname': 0,
'address': 4,
'osmnode': 6,
'osmway': 6,
'poi-address': 8,
'neighborhood': 10,
'local_admin': 12,
'locality': 12,
'admin2': 12,
'admin1': 14,
'admin0': 2
}
},
'file': 'weights',
'type': 'number',
'order': 'desc'
}
}
],
'track_scores': true
};
t.deepEqual(query, expected, 'valid search query'); t.deepEqual(query, expected, 'valid search query');
t.end(); t.end();

2
test/unit/sanitiser/search.js

@ -12,7 +12,7 @@ var search = require('../../../sanitiser/search'),
}, },
size: 10, size: 10,
details: true, details: true,
parsed_text: defaultParsed parsed_text: defaultParsed,
}, },
sanitize = function(query, cb) { _sanitize({'query':query}, cb); }; sanitize = function(query, cb) { _sanitize({'query':query}, cb); };

Loading…
Cancel
Save