mirror of https://github.com/pelias/api.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
5.7 KiB
170 lines
5.7 KiB
const _ = require('lodash'); |
|
const placeTypes = require('./placeTypes'); |
|
const canonicalLayers = require('../helper/type_mapping').getCanonicalLayers(); |
|
const field = require('../helper/fieldValue'); |
|
|
|
/** |
|
* Compare the layer properties if they exist. |
|
* Returns false if the objects are the same, else true. |
|
*/ |
|
function isLayerDifferent(item1, item2){ |
|
if( isPropertyDifferent(item1, item2, 'layer') ){ |
|
// consider all custom layers to be analogous to a venue |
|
if( ( item1.layer === 'venue' || !_.includes( canonicalLayers, item1.layer ) ) && |
|
( item2.layer === 'venue' || !_.includes( canonicalLayers, item2.layer ) ) ){ |
|
return false; |
|
} |
|
// consider some layers to be synonymous |
|
if( _.includes( placeTypes, item1.layer ) && _.includes( placeTypes, item2.layer ) ){ |
|
return false; |
|
} |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* Compare the parent properties if they exist. |
|
* Returns false if the objects are the same, else true. |
|
*/ |
|
function isParentHierarchyDifferent(item1, item2){ |
|
let parent1 = _.get(item1, 'parent'); |
|
let parent2 = _.get(item2, 'parent'); |
|
|
|
// check if these are plain 'ol javascript objects |
|
let isPojo1 = _.isPlainObject(parent1); |
|
let isPojo2 = _.isPlainObject(parent2); |
|
|
|
// if neither object has parent info, we consider them the same |
|
if( !isPojo1 && !isPojo2 ){ return false; } |
|
|
|
// if only one has parent info, we consider them the same |
|
// note: this really shouldn't happen as at least on parent should exist |
|
if( !isPojo1 || !isPojo2 ){ return false; } |
|
|
|
// else both have parent info |
|
|
|
// iterate over all the placetypes, comparing between items |
|
|
|
// we only want to check parent items representing places less granular |
|
// than the highest matched layer. |
|
// eg. if we are comparing layer=address & layer=country then we only |
|
// check for differences in layers above country, so continent, planet etc. |
|
let highestLayerIndex = Math.min( |
|
placeTypes.findIndex(el => el === item1.layer), |
|
placeTypes.findIndex(el => el === item2.layer) |
|
); |
|
|
|
// in the case where we couldn't find either later in the $placeTypes array |
|
// we will enforce that all parent fields are checked. |
|
if( highestLayerIndex === -1 ){ highestLayerIndex = Infinity; } |
|
|
|
return placeTypes.some((placeType, pos) => { |
|
|
|
// skip layers that are less granular than the highest matched layer |
|
if( pos > highestLayerIndex ){ return false; } |
|
|
|
// ensure the parent ids are the same for all placetypes |
|
return isPropertyDifferent( item1.parent, item2.parent, placeType + '_id' ); |
|
}); |
|
} |
|
|
|
/** |
|
* Compare the name properties if they exist. |
|
* Returns false if the objects are the same, else true. |
|
*/ |
|
function isNameDifferent(item1, item2){ |
|
let names1 = _.get(item1, 'name'); |
|
let names2 = _.get(item2, 'name'); |
|
|
|
// check if these are plain 'ol javascript objects |
|
let isPojo1 = _.isPlainObject(names1); |
|
let isPojo2 = _.isPlainObject(names2); |
|
|
|
// if neither object has name info, we consider them the same |
|
if( !isPojo1 && !isPojo2 ){ return false; } |
|
|
|
// if only one has name info, we consider them the same |
|
// note: this really shouldn't happen as name is a mandatory field |
|
if( !isPojo1 || !isPojo2 ){ return false; } |
|
|
|
// else both have name info |
|
// iterate over all the languages in item1, comparing between items |
|
return Object.keys(names1).some( lang => { |
|
|
|
// do not consider absence of an additional name as a difference |
|
// but strictly enfore that 'default' must be present and match |
|
if( _.has(names2, lang) || lang === 'default' ){ |
|
|
|
// do not consider absence of an additional name as a difference |
|
return isPropertyDifferent(names1, names2, lang); |
|
} |
|
}); |
|
} |
|
|
|
/** |
|
* Compare the address_parts properties if they exist. |
|
* Returns false if the objects are the same, else true. |
|
*/ |
|
function isAddressDifferent(item1, item2){ |
|
let address1 = _.get(item1, 'address_parts'); |
|
let address2 = _.get(item2, 'address_parts'); |
|
|
|
// check if these are plain 'ol javascript objects |
|
let isPojo1 = _.isPlainObject(address1); |
|
let isPojo2 = _.isPlainObject(address2); |
|
|
|
// if neither object has address info, we consider them the same |
|
if( !isPojo1 && !isPojo2 ){ return false; } |
|
|
|
// if only one has address info, we consider them the same |
|
if( !isPojo1 || !isPojo2 ){ return false; } |
|
|
|
// else both have address info |
|
if( isPropertyDifferent(address1, address2, 'number') ){ return true; } |
|
if( isPropertyDifferent(address1, address2, 'street') ){ return true; } |
|
|
|
// only compare zip if both records have it, otherwise just ignore and assume it's the same |
|
// since by this time we've already compared parent hierarchies |
|
if( _.has(address1, 'zip') && _.has(address2, 'zip') ){ |
|
if( isPropertyDifferent(address1, address2, 'zip') ){ return true; } |
|
} |
|
|
|
return false; |
|
} |
|
|
|
/** |
|
* Compare the two records and return true if they differ and false if same. |
|
*/ |
|
function isDifferent(item1, item2){ |
|
if( isLayerDifferent( item1, item2 ) ){ return true; } |
|
if( isParentHierarchyDifferent( item1, item2 ) ){ return true; } |
|
if( isNameDifferent( item1, item2 ) ){ return true; } |
|
if( isAddressDifferent( item1, item2 ) ){ return true; } |
|
return false; |
|
} |
|
|
|
/** |
|
* return true if properties are different |
|
*/ |
|
function isPropertyDifferent(item1, item2, prop ){ |
|
|
|
// if neither item has prop, we consider them the same |
|
if( !_.has(item1, prop) && !_.has(item2, prop) ){ return false; } |
|
|
|
// handle arrays and other non-string values |
|
var prop1 = field.getStringValue( _.get( item1, prop ) ); |
|
var prop2 = field.getStringValue( _.get( item2, prop ) ); |
|
|
|
// compare strings |
|
return normalizeString(prop1) !== normalizeString(prop2); |
|
} |
|
|
|
/** |
|
* lowercase characters and remove some punctuation |
|
*/ |
|
function normalizeString(str){ |
|
return str.toLowerCase().split(/[ ,-]+/).join(' '); |
|
} |
|
|
|
module.exports.isDifferent = isDifferent; |