Skip to content

Commit

Permalink
feat(aliases): allow aliases for address fields
Browse files Browse the repository at this point in the history
  • Loading branch information
missinglink authored and orangejulius committed Sep 11, 2018
1 parent ab71c81 commit e6e68fa
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 18 deletions.
48 changes: 45 additions & 3 deletions Document.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,24 +370,66 @@ Document.prototype.clearAllParents = function() {
// address
Document.prototype.setAddress = function( prop, value ){

validate.type('string', value);
validate.truthy(value);
validate.property(addressFields, prop);

if( Array.isArray( this.address_parts[ prop ] ) ){
this.address_parts[ prop ][ 0 ] = value;
} else {
this.address_parts[ prop ] = value;
}

return this;
};

Document.prototype.setAddressAlias = function( prop, value ){

validate.type('string', value);
validate.truthy(value);
validate.property(addressFields, prop);

// is this the first time setting this prop? ensure it's an array
if( !this.hasAddress( prop ) ){
this.address_parts[ prop ] = [];
}

// is casting required to convert a scalar field to an array?
else if( 'string' === typeof this.address_parts[ prop ] ){
var stringValue = this.address_parts[ prop ];
this.address_parts[ prop ] = [ stringValue ];
}

// is the array empty? ie. no prior call to setAddress()
// in this case we will also set element 0 (the element used for display)
if( !this.address_parts[ prop ].length ){
this.setAddress( prop, value );
}

// set the alias as the second, third, fourth, etc value in the array
this.address_parts[ prop ].push( value );

this.address_parts[ prop ] = value;
return this;
};

Document.prototype.getAddress = function( prop ){
return this.address_parts[ prop ];
return Array.isArray( this.address_parts[ prop ] ) ?
this.address_parts[ prop ][ 0 ] :
this.address_parts[ prop ];
};

Document.prototype.getAddressAliases = function( prop ){
return Array.isArray( this.address_parts[ prop ] ) ?
this.address_parts[ prop ].slice( 1 ) :
[];
};

Document.prototype.hasAddress = function( prop ){
return this.address_parts.hasOwnProperty( prop );
};

Document.prototype.delAddress = function( prop ){
if( this.hasName( prop ) ){
if( this.hasAddress( prop ) ){
delete this.address_parts[ prop ];
return true;
}
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var poi = new Document( 'geoname', 'venue', 1003 )
.addParent( 'neighbourhood', 'Shoreditch', '2002' )
.setAddress( 'number', '10' )
.setAddress( 'street', 'pelias place' )
.setAddressAlias( 'street', 'pelias pl' )
.addCategory( 'foo' )
.addCategory( 'bar' )
.removeCategory( 'foo' )
Expand Down
112 changes: 97 additions & 15 deletions test/document/address.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,92 @@ module.exports.tests.setAddress = function(test) {
t.equal(doc.address_parts.zip, 'bar', 'setter works');
t.end();
});
test('setAddress - validate', function(t) {
var doc = new Document('mysource','mylayer','myid');
t.throws( doc.setAddress.bind(doc, 1), null, 'invalid type' );
t.throws( doc.setAddress.bind(doc, ''), null, 'invalid length' );
t.throws( doc.setAddress.bind(doc, 'foo', 1), null, 'invalid property' );
t.throws( doc.setAddress.bind(doc, '4', 2), null, 'invalid property' );
t.throws( doc.setAddress.bind(doc, 'zip', 2), null, 'invalid property' );
t.throws( doc.setAddress.bind(doc, 'unit', 2), null, 'invalid property' );
t.throws( doc.setAddress.bind(doc, 'street', true), null, 'invalid property' );
t.throws( doc.setAddress.bind(doc, 'street', null), null, 'invalid property' );
t.throws( doc.setAddress.bind(doc, 'street', '\n'), null, 'invalid property' );
t.equal(doc.address_parts.street, undefined, 'property unchanged');
t.doesNotThrow( doc.setAddress.bind(doc, 'zip', 'foo'), null, 'invalid property' );
t.doesNotThrow( doc.setAddress.bind(doc, 'unit', 'foo'), null, 'invalid property' );
t.doesNotThrow( doc.setAddress.bind(doc, 'street', '1'), null, 'invalid property' );
test('setAddress - validate key', function(t) {
var doc = new Document('mysource','mylayer','myid');
t.throws( doc.setAddress.bind(doc,1), null, 'invalid type' );
t.throws( doc.setAddress.bind(doc,''), null, 'invalid length' );
t.throws( doc.setAddress.bind(doc,' '), null, 'invalid length' );
t.throws( doc.setAddress.bind(doc,null), null, 'invalid length' );
t.equal(doc.getAddress('test'), undefined, 'property not set');
t.end();
});
test('setAddress - validate key in property list', function(t) {
var doc = new Document('mysource','mylayer','myid');
t.throws( doc.setAddress.bind(doc, 'invalid', 'foo'), null, 'invalid property' );
t.doesNotThrow( doc.setAddress.bind(doc, 'name', 'foo'), null, 'valid property' );
t.doesNotThrow( doc.setAddress.bind(doc, 'number', 'foo'), null, 'valid property' );
t.doesNotThrow( doc.setAddress.bind(doc, 'unit', 'foo'), null, 'valid property' );
t.doesNotThrow( doc.setAddress.bind(doc, 'street', '1'), null, 'valid property' );
t.doesNotThrow( doc.setAddress.bind(doc, 'zip', 'foo'), null, 'valid property' );
t.end();
});
test('setAddress - validate val', function(t) {
var doc = new Document('mysource','mylayer','myid');
t.throws( doc.setAddress.bind(doc,'zip',1), null, 'invalid value' );
t.throws( doc.setAddress.bind(doc,'zip',''), null, 'invalid value' );
t.throws( doc.setAddress.bind(doc,'zip',' '), null, 'invalid value' );
t.throws( doc.setAddress.bind(doc,'zip',null), null, 'invalid value' );
t.throws( doc.setAddress.bind(doc,'zip','\t'), null, 'invalid value' );
t.equal(doc.getAddress('test'), undefined, 'property not set');
t.end();
});
};

module.exports.tests.getAddressAliases = function(test) {
test('getAddressAliases', function(t) {
var doc = new Document('mysource','mylayer','myid');
t.deepEqual(doc.getAddressAliases('zip'), [], 'getter works');
doc.address_parts = { 'zip': 'bar' };
t.deepEqual(doc.getAddressAliases('zip'), [], 'getter works');
doc.address_parts = { 'zip': ['bar'] };
t.deepEqual(doc.getAddressAliases('zip'), [], 'getter works');
doc.address_parts = { 'zip': ['bar','baz','boo'] };
t.deepEqual(doc.getAddressAliases('zip'), ['baz','boo'], 'getter works');
t.end();
});
};

module.exports.tests.setAddressAlias = function(test) {
test('setAddressAlias - no prior call to setAddress', function(t) {
var doc = new Document('mysource','mylayer','myid');
t.equal(doc.setAddressAlias('zip','bar'), doc, 'chainable');
t.equal(doc.setAddressAlias('zip','baz'), doc, 'chainable');
t.equal(doc.address_parts.zip[0], 'bar', 'setter works');
t.equal(doc.address_parts.zip[1], 'bar', 'setter works');
t.equal(doc.address_parts.zip[2], 'baz', 'setter works');
t.equal(doc.getAddress('zip'), 'bar', 'name set');
t.deepEqual(doc.getAddressAliases('zip'), ['bar','baz'], 'aliases set');
t.end();
});
test('setAddressAlias', function(t) {
var doc = new Document('mysource','mylayer','myid');
t.equal(doc.setAddress('zip','bar'), doc, 'chainable');
t.equal(doc.setAddressAlias('zip','baz'), doc, 'chainable');
t.equal(doc.setAddressAlias('zip','boo'), doc, 'chainable');
t.equal(doc.address_parts.zip[0], 'bar', 'setter works');
t.equal(doc.address_parts.zip[1], 'baz', 'setter works');
t.equal(doc.address_parts.zip[2], 'boo', 'setter works');
t.equal(doc.getAddress('zip'), 'bar', 'name set');
t.deepEqual(doc.getAddressAliases('zip'), ['baz','boo'], 'aliases set');
t.end();
});
test('setAddressAlias - validate key', function(t) {
var doc = new Document('mysource','mylayer','myid');
t.throws( doc.setAddressAlias.bind(doc,1), null, 'invalid type' );
t.throws( doc.setAddressAlias.bind(doc,''), null, 'invalid length' );
t.throws( doc.setAddressAlias.bind(doc,' '), null, 'invalid length' );
t.throws( doc.setAddressAlias.bind(doc,null), null, 'invalid length' );
t.deepEqual(doc.getAddressAliases('test'), [], 'property not set');
t.end();
});
test('setAddressAlias - validate val', function(t) {
var doc = new Document('mysource','mylayer','myid');
t.throws( doc.setAddressAlias.bind(doc,'zip',1), null, 'invalid value' );
t.throws( doc.setAddressAlias.bind(doc,'zip',''), null, 'invalid value' );
t.throws( doc.setAddressAlias.bind(doc,'zip',' '), null, 'invalid value' );
t.throws( doc.setAddressAlias.bind(doc,'zip',null), null, 'invalid value' );
t.throws( doc.setAddressAlias.bind(doc,'zip','\t'), null, 'invalid value' );
t.deepEqual(doc.getAddressAliases('test'), [], 'property not set');
t.end();
});
};
Expand All @@ -49,6 +120,17 @@ module.exports.tests.hasAddress = function(test) {
});
};

module.exports.tests.delAddress = function(test) {
test('delAddress', function(t) {
var doc = new Document('mysource','mylayer','myid');
t.equal(doc.delAddress('zip'), false, 'deller works');
doc.address_parts.zip = 'bar';
t.equal(doc.delAddress('zip'), true, 'deller works');
t.equal(doc.address_parts.zip, undefined, 'deller works');
t.end();
});
};

module.exports.all = function (tape, common) {

function test(name, testFunction) {
Expand Down
57 changes: 57 additions & 0 deletions test/document/toESDocument.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,63 @@ module.exports.tests.toESDocument = function(test) {

});

test('toESDocumentWithAddressAliases', function(t) {
var Document = proxyquire('../../Document', { 'pelias-config': fakeConfig });

var doc = new Document('mysource','mylayer','myid');
doc.setAddress('name', 'address name');
doc.setAddress('number', 'address number');
doc.setAddressAlias('street', 'astreet');
doc.setAddress('street', 'address street');
doc.setAddress('zip', 'address zip');
doc.setAddressAlias('zip', 'azip');
doc.setAddress('unit', 'address unit');

var esDoc = doc.toESDocument();

var expected = {
_index: 'pelias',
_type: 'mylayer',
_id: 'myid',
data: {
source: 'mysource',
layer: 'mylayer',
source_id: 'myid',
address_parts: {
name: 'address name',
number: 'address number',
street: ['address street','astreet'],
zip: ['address zip','azip'],
unit: 'address unit'
},
name: {},
phrase: {}
}
};

t.deepEqual(esDoc, expected, 'creates correct elasticsearch document');
t.end();
});

test('unset properties should not output in toESDocument', (t) => {
const Document = proxyquire('../../Document', { 'pelias-config': fakeConfig });

const esDoc = new Document('mysource','mylayer','myid').toESDocument();

// test that empty arrays/object are stripped from the doc before sending it
// downstream to elasticsearch.
t.false(esDoc.data.hasOwnProperty('address_parts'), 'does not include empty top-level maps');
t.false(esDoc.data.hasOwnProperty('category'), 'does not include empty top-level arrays');
t.false(esDoc.data.hasOwnProperty('parent'), 'does not include empty parent arrays');
t.false(esDoc.data.hasOwnProperty('bounding_box'), 'should not include bounding_box');
t.false(esDoc.data.hasOwnProperty('center_point'), 'should not include center');
t.false(esDoc.data.hasOwnProperty('population'), ' should not include population');
t.false(esDoc.data.hasOwnProperty('popularity'), ' should not include popularity');
t.false(esDoc.data.hasOwnProperty('polygon'), ' should not include polygon');
t.end();

});

};

module.exports.tests.toESDocumentWithCustomConfig = function(test) {
Expand Down

0 comments on commit e6e68fa

Please sign in to comment.