fix: accelerate unique id checking
This commit is contained in:
parent
fcac276955
commit
248d57da4e
|
@ -17,6 +17,7 @@ var defineScope = require('./scope.js').defineScope;
|
||||||
var g = require('strong-globalize')();
|
var g = require('strong-globalize')();
|
||||||
var mergeQuery = utils.mergeQuery;
|
var mergeQuery = utils.mergeQuery;
|
||||||
var idEquals = utils.idEquals;
|
var idEquals = utils.idEquals;
|
||||||
|
var idsHaveDuplicates = utils.idsHaveDuplicates;
|
||||||
var ModelBaseClass = require('./model.js');
|
var ModelBaseClass = require('./model.js');
|
||||||
var applyFilter = require('./connectors/memory').applyFilter;
|
var applyFilter = require('./connectors/memory').applyFilter;
|
||||||
var ValidationError = require('./validations.js').ValidationError;
|
var ValidationError = require('./validations.js').ValidationError;
|
||||||
|
@ -2502,10 +2503,7 @@ RelationDefinition.embedsMany = function embedsMany(modelFrom, modelToRef, param
|
||||||
modelFrom.validate(propertyName, function(err) {
|
modelFrom.validate(propertyName, function(err) {
|
||||||
var embeddedList = this[propertyName] || [];
|
var embeddedList = this[propertyName] || [];
|
||||||
var ids = embeddedList.map(function(m) { return m[idName] && m[idName].toString(); }); // mongodb
|
var ids = embeddedList.map(function(m) { return m[idName] && m[idName].toString(); }); // mongodb
|
||||||
var uniqueIds = ids.filter(function(id, pos) {
|
if (idsHaveDuplicates(ids)) {
|
||||||
return utils.findIndexOf(ids, id, idEquals) === pos;
|
|
||||||
});
|
|
||||||
if (ids.length !== uniqueIds.length) {
|
|
||||||
this.errors.add(propertyName, 'contains duplicate `' + idName + '`', 'uniqueness');
|
this.errors.add(propertyName, 'contains duplicate `' + idName + '`', 'uniqueness');
|
||||||
err(false);
|
err(false);
|
||||||
}
|
}
|
||||||
|
@ -3155,10 +3153,7 @@ RelationDefinition.referencesMany = function referencesMany(modelFrom, modelToRe
|
||||||
|
|
||||||
modelFrom.validate(relationName, function(err) {
|
modelFrom.validate(relationName, function(err) {
|
||||||
var ids = this[fk] || [];
|
var ids = this[fk] || [];
|
||||||
var uniqueIds = ids.filter(function(id, pos) {
|
if (idsHaveDuplicates(ids)) {
|
||||||
return utils.findIndexOf(ids, id, idEquals) === pos;
|
|
||||||
});
|
|
||||||
if (ids.length !== uniqueIds.length) {
|
|
||||||
var msg = 'contains duplicate `' + modelTo.modelName + '` instance';
|
var msg = 'contains duplicate `' + modelTo.modelName + '` instance';
|
||||||
this.errors.add(relationName, msg, 'uniqueness');
|
this.errors.add(relationName, msg, 'uniqueness');
|
||||||
err(false);
|
err(false);
|
||||||
|
|
46
lib/utils.js
46
lib/utils.js
|
@ -26,6 +26,7 @@ exports.findIndexOf = findIndexOf;
|
||||||
exports.collectTargetIds = collectTargetIds;
|
exports.collectTargetIds = collectTargetIds;
|
||||||
exports.idName = idName;
|
exports.idName = idName;
|
||||||
exports.rankArrayElements = rankArrayElements;
|
exports.rankArrayElements = rankArrayElements;
|
||||||
|
exports.idsHaveDuplicates = idsHaveDuplicates;
|
||||||
|
|
||||||
var g = require('strong-globalize')();
|
var g = require('strong-globalize')();
|
||||||
var traverse = require('traverse');
|
var traverse = require('traverse');
|
||||||
|
@ -685,3 +686,48 @@ function collectTargetIds(targetData, idPropertyName) {
|
||||||
function idName(m) {
|
function idName(m) {
|
||||||
return m.definition.idName() || 'id';
|
return m.definition.idName() || 'id';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check a list of IDs to see if there are any duplicates.
|
||||||
|
*
|
||||||
|
* @param {Array} The array of IDs to check
|
||||||
|
* @returns {boolean} If any duplicates were found
|
||||||
|
*/
|
||||||
|
function idsHaveDuplicates(ids) {
|
||||||
|
// use Set if available and all ids are of string or number type
|
||||||
|
var hasDuplicates = undefined;
|
||||||
|
var i, j;
|
||||||
|
if (typeof Set === 'function') {
|
||||||
|
var uniqueIds = new Set();
|
||||||
|
for (i = 0; i < ids.length; ++i) {
|
||||||
|
var idType = typeof ids[i];
|
||||||
|
if (idType === 'string' || idType === 'number') {
|
||||||
|
if (uniqueIds.has(ids[i])) {
|
||||||
|
hasDuplicates = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
uniqueIds.add(ids[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ids are not all string/number that can be checked via Set, stop and do the slow test
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasDuplicates === undefined && uniqueIds.length === ids.length) {
|
||||||
|
hasDuplicates = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasDuplicates === undefined) {
|
||||||
|
// fast check was inconclusive or unavailable, do the slow check
|
||||||
|
// can still optimize this by doing 1/2 N^2 instead of the full N^2
|
||||||
|
for (i = 0; i < ids.length && hasDuplicates === undefined; ++i) {
|
||||||
|
for (j = 0; j < i; ++j) {
|
||||||
|
if (idEquals(ids[i], ids[j])) {
|
||||||
|
hasDuplicates = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasDuplicates === true;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue