mirror of https://github.com/pelias/api.git
Browse Source
this commit combines the other sanitiser/_text.js and addressit parser logic from text-analyzer into one module for easier integration until such time that libpostal is ready for autocompletepull/666/head
Stephen Hess
8 years ago
3 changed files with 384 additions and 0 deletions
@ -0,0 +1,107 @@
|
||||
var check = require('check-types'); |
||||
var parser = require('addressit'); |
||||
var extend = require('extend'); |
||||
var _ = require('lodash'); |
||||
var logger = require('pelias-logger').get('api'); |
||||
|
||||
// validate texts, convert types and apply defaults
|
||||
function sanitize( raw, clean ){ |
||||
|
||||
// error & warning messages
|
||||
var messages = { errors: [], warnings: [] }; |
||||
|
||||
// invalid input 'text'
|
||||
if( !check.nonEmptyString( raw.text ) ){ |
||||
messages.errors.push('invalid param \'text\': text length, must be >0'); |
||||
} |
||||
|
||||
// valid input 'text'
|
||||
else { |
||||
|
||||
// valid text
|
||||
clean.text = raw.text; |
||||
|
||||
// parse text with query parser
|
||||
var parsed_text = parse(clean.text); |
||||
if (check.assigned(parsed_text)) { |
||||
clean.parsed_text = parsed_text; |
||||
} |
||||
} |
||||
|
||||
return messages; |
||||
} |
||||
|
||||
// export function
|
||||
module.exports = sanitize; |
||||
|
||||
|
||||
|
||||
// this is the addressit functionality from https://github.com/pelias/text-analyzer/blob/master/src/addressItParser.js
|
||||
var DELIM = ','; |
||||
|
||||
function parse(query) { |
||||
var getAdminPartsBySplittingOnDelim = function(queryParts) { |
||||
// naive approach - for admin matching during query time
|
||||
// split 'flatiron, new york, ny' into 'flatiron' and 'new york, ny'
|
||||
|
||||
var address = {}; |
||||
|
||||
if (queryParts.length > 1) { |
||||
address.name = queryParts[0].trim(); |
||||
|
||||
// 1. slice away all parts after the first one
|
||||
// 2. trim spaces from each part just in case
|
||||
// 3. join the parts back together with appropriate delimiter and spacing
|
||||
address.admin_parts = queryParts.slice(1) |
||||
.map(function (part) { return part.trim(); }) |
||||
.join(DELIM + ' '); |
||||
} |
||||
|
||||
return address; |
||||
}; |
||||
|
||||
var getAddressParts = function(query) { |
||||
// perform full address parsing
|
||||
// except on queries so short they obviously can't contain an address
|
||||
if (query.length > 3) { |
||||
return parser( query ); |
||||
} |
||||
}; |
||||
|
||||
var queryParts = query.split(DELIM); |
||||
|
||||
var addressWithAdminParts = getAdminPartsBySplittingOnDelim(queryParts); |
||||
var addressWithAddressParts= getAddressParts(queryParts.join(DELIM + ' ')); |
||||
|
||||
var parsedAddress = extend(addressWithAdminParts, |
||||
addressWithAddressParts); |
||||
|
||||
var address_parts = [ 'name', |
||||
'number', |
||||
'street', |
||||
'city', |
||||
'state', |
||||
'country', |
||||
'postalcode', |
||||
'regions', |
||||
'admin_parts' |
||||
]; |
||||
|
||||
var parsed_text = {}; |
||||
|
||||
address_parts.forEach(function(part){ |
||||
if (parsedAddress[part]) { |
||||
parsed_text[part] = parsedAddress[part]; |
||||
} |
||||
}); |
||||
|
||||
// if all we found was regions, ignore it as it is not enough information to make smarter decisions
|
||||
if (Object.keys(parsed_text).length === 1 && !_.isUndefined(parsed_text.regions)) |
||||
{ |
||||
logger.info('Ignoring address parser output, regions only'); |
||||
return null; |
||||
} |
||||
|
||||
return parsed_text; |
||||
|
||||
} |
@ -0,0 +1,276 @@
|
||||
var sanitiser = require('../../../sanitiser/_text_autocomplete'); |
||||
var type_mapping = require('../../../helper/type_mapping'); |
||||
|
||||
module.exports.tests = {}; |
||||
|
||||
module.exports.tests.text_parser = function(test, common) { |
||||
test('short input text has admin layers set ', function(t) { |
||||
var raw = { |
||||
text: 'emp' //start of empire state building
|
||||
}; |
||||
var clean = { |
||||
}; |
||||
|
||||
var messages = sanitiser(raw, clean); |
||||
|
||||
t.deepEquals(messages.errors, [], 'no errors'); |
||||
t.deepEquals(messages.warnings, [], 'no warnings'); |
||||
|
||||
t.end(); |
||||
}); |
||||
|
||||
var queries = [ |
||||
{ name: 'soho', admin_parts: 'new york' }, |
||||
{ name: 'chelsea', admin_parts: 'london' }, |
||||
{ name: '123 main', admin_parts: 'new york' } |
||||
]; |
||||
|
||||
queries.forEach(function (query) { |
||||
test('naive parsing ' + query, function(t) { |
||||
var raw = { |
||||
text: query.name + ', ' + query.admin_parts |
||||
}; |
||||
var clean = {}; |
||||
|
||||
var expected_clean = { |
||||
text: query.name + ', ' + query.admin_parts, |
||||
parsed_text: { |
||||
name: query.name, |
||||
regions: [ query.name, query.admin_parts ], |
||||
admin_parts: query.admin_parts |
||||
} |
||||
}; |
||||
|
||||
var messages = sanitiser(raw, clean); |
||||
|
||||
t.deepEqual(messages, { errors: [], warnings: [] } ); |
||||
t.deepEqual(clean, expected_clean); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('naive parsing ' + query + ' without spaces', function(t) { |
||||
var raw = { |
||||
text: query.name + ',' + query.admin_parts |
||||
}; |
||||
var clean = {}; |
||||
|
||||
var expected_clean = { |
||||
text: query.name + ',' + query.admin_parts, |
||||
parsed_text: { |
||||
name: query.name, |
||||
regions: [ query.name, query.admin_parts ], |
||||
admin_parts: query.admin_parts |
||||
} |
||||
}; |
||||
|
||||
var messages = sanitiser(raw, clean); |
||||
|
||||
t.deepEqual(messages, { errors: [], warnings: [] } ); |
||||
t.deepEqual(clean, expected_clean); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
}); |
||||
|
||||
test('query with one token', function (t) { |
||||
var raw = { |
||||
text: 'yugolsavia' |
||||
}; |
||||
var clean = {}; |
||||
|
||||
var expected_clean = { |
||||
text: 'yugolsavia' |
||||
}; |
||||
|
||||
var messages = sanitiser(raw, clean); |
||||
|
||||
t.deepEqual(messages, { errors: [], warnings: [] } ); |
||||
t.deepEqual(clean, expected_clean); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('query with two tokens, no numbers', function (t) { |
||||
var raw = { |
||||
text: 'small town' |
||||
}; |
||||
var clean = {}; |
||||
|
||||
var expected_clean = { |
||||
text: 'small town' |
||||
}; |
||||
|
||||
var messages = sanitiser(raw, clean); |
||||
|
||||
t.deepEqual(messages, { errors: [], warnings: [] } ); |
||||
t.deepEqual(clean, expected_clean); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('query with two tokens, number first', function (t) { |
||||
var raw = { |
||||
text: '123 main' |
||||
}; |
||||
var clean = {}; |
||||
|
||||
var expected_clean = { |
||||
text: '123 main' |
||||
}; |
||||
|
||||
var messages = sanitiser(raw, clean); |
||||
|
||||
t.deepEqual(messages, { errors: [], warnings: [] } ); |
||||
t.deepEqual(clean, expected_clean); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('query with two tokens, number second', function (t) { |
||||
var raw = { |
||||
text: 'main 123' |
||||
}; |
||||
var clean = {}; |
||||
|
||||
var expected_clean = { |
||||
text: 'main 123' |
||||
}; |
||||
|
||||
var messages = sanitiser(raw, clean); |
||||
|
||||
t.deepEqual(messages, { errors: [], warnings: [] } ); |
||||
t.deepEqual(clean, expected_clean); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('query with many tokens', function(t) { |
||||
var raw = { |
||||
text: 'main particle new york' |
||||
}; |
||||
var clean = {}; |
||||
|
||||
var expected_clean = { |
||||
text: 'main particle new york' |
||||
}; |
||||
|
||||
var messages = sanitiser(raw, clean); |
||||
|
||||
t.deepEqual(messages, { errors: [], warnings: [] } ); |
||||
t.deepEqual(clean, expected_clean); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('valid address, house number', function(t) { |
||||
var raw = { |
||||
text: '123 main st new york ny' |
||||
}; |
||||
var clean = {}; |
||||
|
||||
var expected_clean = { |
||||
text: '123 main st new york ny', |
||||
parsed_text: { |
||||
number: '123', |
||||
street: 'main st', |
||||
state: 'NY', |
||||
regions: [ 'new york' ] |
||||
} |
||||
}; |
||||
|
||||
var messages = sanitiser(raw, clean); |
||||
|
||||
t.deepEqual(messages, { errors: [], warnings: [] } ); |
||||
t.deepEqual(clean, expected_clean); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('valid address, zipcode', function(t) { |
||||
var raw = { |
||||
text: '123 main st new york ny 10010' |
||||
}; |
||||
var clean = {}; |
||||
|
||||
var expected_clean = { |
||||
text: '123 main st new york ny 10010', |
||||
parsed_text: { |
||||
number: '123', |
||||
street: 'main st', |
||||
state: 'NY', |
||||
postalcode: '10010', |
||||
regions: [ 'new york' ] |
||||
} |
||||
}; |
||||
|
||||
var messages = sanitiser(raw, clean); |
||||
|
||||
t.deepEqual(messages, { errors: [], warnings: [] } ); |
||||
t.deepEqual(clean, expected_clean); |
||||
t.end(); |
||||
}); |
||||
|
||||
test('valid address with leading 0s in zipcode', function(t) { |
||||
var raw = { |
||||
text: '339 W Main St, Cheshire, 06410' |
||||
}; |
||||
var clean = {}; |
||||
|
||||
var expected_clean = { |
||||
text: '339 W Main St, Cheshire, 06410', |
||||
parsed_text: { |
||||
name: '339 W Main St', |
||||
number: '339', |
||||
street: 'W Main St', |
||||
postalcode: '06410', |
||||
regions: [ 'Cheshire' ], |
||||
admin_parts: 'Cheshire, 06410' |
||||
} |
||||
}; |
||||
|
||||
var messages = sanitiser(raw, clean); |
||||
|
||||
t.deepEqual(messages, { errors: [], warnings: [] } ); |
||||
t.deepEqual(clean, expected_clean); |
||||
t.end(); |
||||
}); |
||||
|
||||
test('valid address without spaces after commas', function(t) { |
||||
var raw = { |
||||
text: '339 W Main St,Lancaster,PA' |
||||
}; |
||||
var clean = {}; |
||||
|
||||
var expected_clean = { |
||||
text: '339 W Main St,Lancaster,PA', |
||||
parsed_text: { |
||||
name: '339 W Main St', |
||||
number: '339', |
||||
street: 'W Main St', |
||||
state: 'PA', |
||||
regions: [ 'Lancaster' ], |
||||
admin_parts: 'Lancaster, PA' |
||||
} |
||||
}; |
||||
|
||||
var messages = sanitiser(raw, clean); |
||||
|
||||
t.deepEqual(messages, { errors: [], warnings: [] } ); |
||||
t.deepEqual(clean, expected_clean); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.all = function (tape, common) { |
||||
function test(name, testFunction) { |
||||
return tape('SANITISER _text: ' + name, testFunction); |
||||
} |
||||
|
||||
for( var testCase in module.exports.tests ){ |
||||
module.exports.tests[testCase](test, common); |
||||
} |
||||
}; |
Loading…
Reference in new issue