Skip to content

Commit

Permalink
Merge pull request #8 from 2do2go/feature/lazy-hooks
Browse files Browse the repository at this point in the history
Feature: lazy hooks
  • Loading branch information
lbeschastny authored Feb 9, 2021
2 parents c0e72b7 + 5dbc27c commit 3481daa
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 26 deletions.
14 changes: 9 additions & 5 deletions lib/embeddedDocument.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@ var EmbeddedDocument = function(options) {
};

EmbeddedDocument.prototype.getUniqGroupId = function() {
return [
this.collection.collectionName,
this.key,
utils.stringifyProjection(this.projection)
].join('.');
return utils.getUniqGroupId(this);
};

EmbeddedDocument.prototype.getString = function() {
Expand All @@ -47,4 +43,12 @@ EmbeddedDocument.prototype.toJSON = function() {
return this.identifier;
};

EmbeddedDocument.prototype.setEmbeddedValue = function(embeddedValue) {
if (embeddedValue && embeddedValue[this.key] === this.identifier) {
this.embeddedValue = embeddedValue;
} else {
throw new Error('Invalid embedded value');
}
};

module.exports = EmbeddedDocument;
6 changes: 5 additions & 1 deletion lib/embedderHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ var getFindParamsHash = function(object) {
if (!Array.isArray(value) && !utils.isSimpleObject(value)) return;

if (value instanceof EmbeddedDocument) {
if (value.embeddedValue) return;

var uniqGroupId = value.getUniqGroupId();

var params = findParamsHash[uniqGroupId];
Expand Down Expand Up @@ -149,7 +151,9 @@ var replaceEmbeddedDocuments = function(object, documentsHash) {
if (value instanceof EmbeddedDocument) {
var uniqGroupId = value.getUniqGroupId();

var embeddedDocument = documentsHash[uniqGroupId][value.identifier];
var embeddedDocument = value.embeddedValue
? value.embeddedValue
: documentsHash[uniqGroupId][value.identifier];

if (!embeddedDocument) {
throw new Error(
Expand Down
92 changes: 73 additions & 19 deletions lib/relationHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,54 @@ var setupRelationHooks = function(relatedCollection, relation) {
});
};

var getEmbeddedDocuments = function(identifiers, callback) {
var projectionKeys = Object.keys(relation.projection);
if (projectionKeys.length === 1 && relation.projection[relation.key]) {
var documents = identifiers.map(function(identifier) {
return utils.createObject(relation.key, identifier);
});
callback(null, documents);
} else {
var condition = utils.createObject(relation.key, {$in: identifiers});
relation.collection.find(condition, relation.projection).toArray(callback);
}
};

var getEmbeddedDocumentsHash = function(identifiers, callback) {
getEmbeddedDocuments(identifiers, function(err, documents) {
if (err) return callback(err);

var documentsHash = utils.indexBy(documents, relation.key);

callback(null, documentsHash);
});
};

var beforeUpdate = function(params, callback) {
if (relation.onUpdate === 'cascade') {
var uniqGroupId = utils.getUniqGroupId(relation);
params.meta.cascadeUpdateParamsHash = params.meta.cascadeUpdateParamsHash || {};

if (
relation.onUpdate === 'cascade' &&
!params.meta.cascadeUpdateParamsHash[uniqGroupId]
) {
var cascadeUpdateParams = params.meta.cascadeUpdateParamsHash[uniqGroupId] = {};

getModifiedIdentifiers(params.condition, function(err, identifiers) {
if (err) return callback(err);

params.meta.modifiedIdentifiers = identifiers;
cascadeUpdateParams.modifiedIdentifiers = identifiers;

if (!identifiers.length) return callback();

// fetch current embedded documents to skip unnecessary updates later
getEmbeddedDocumentsHash(identifiers, function(err, documentsHash) {
if (err) return callback(err);

cascadeUpdateParams.originalEmbeddedDocumentsHash = documentsHash;

callback();
callback();
});
});
} else {
callback();
Expand All @@ -39,36 +79,50 @@ var setupRelationHooks = function(relatedCollection, relation) {
relation.collection.on('beforeUpdateMany', beforeUpdate);

var afterUpdate = function(params, callback) {
var identifiers = params.meta.modifiedIdentifiers || [];
var uniqGroupId = utils.getUniqGroupId(relation);
var cascadeUpdateParams = params.meta.cascadeUpdateParamsHash[uniqGroupId] || {};
var identifiers = cascadeUpdateParams.modifiedIdentifiers || [];
var originalDocumentsHash = cascadeUpdateParams.originalEmbeddedDocumentsHash || {};

if (!identifiers.length) return callback();

if (relation.onUpdate === 'cascade') {
// in cascade mode we need to update each updated identifier
var funcs = identifiers.map(function(identifier) {
return function(callback) {
var projectionKeys = Object.keys(relation.projection);
if (projectionKeys.length === 1 && relation.projection[relation.key]) {
callback();
} else {
getEmbeddedDocumentsHash(identifiers, function(err, newDocumentsHash) {
if (err) return callback(err);

// in cascade mode we need to update each updated identifier
var funcs = identifiers.map(function(identifier) {
return function(callback) {
var originalDocument = originalDocumentsHash[identifier];
var newDocument = newDocumentsHash[identifier];
if (
originalDocument && newDocument &&
utils.isDeepStrictEqual(originalDocument, newDocument)
) {
return callback();
}

var condition = utils.createObject(
relation.paths.identifier,
identifier
);

var embeddedDocument = relation.embedder(identifier);

if (newDocument) {
embeddedDocument.setEmbeddedValue(newDocument);
}

var modifier = {
$set: utils.createObject(
relation.paths.modifier,
relation.embedder(identifier)
)
$set: utils.createObject(relation.paths.modifier, embeddedDocument)
};

relatedCollection.updateMany(condition, modifier, callback);
}
};
});
};
});

utils.asyncParallel(funcs, callback);
utils.asyncParallel(funcs, callback);
});
} else {
callback();
}
Expand Down
19 changes: 18 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
'use strict';

var util = require('util');
var defaults = require('./defaults');

var isObject = exports.isObject = function(obj) {
var type = typeof obj;
return type === 'function' || type === 'object' && !!obj;
Expand Down Expand Up @@ -47,6 +50,12 @@ exports.deepSet = function(object, field, value) {
return object;
};

exports.isDeepStrictEqual = function(val1, val2) {
return util.isDeepStrictEqual
? util.isDeepStrictEqual(val1, val2)
: JSON.stringify(val1) === JSON.stringify(val2);
};

exports.isModifier = function(modifier) {
var keys = Object.keys(modifier);
return keys.length && (/^\$/).test(keys[0]);
Expand Down Expand Up @@ -99,7 +108,7 @@ exports.asyncParallel = function(funcs, callback, context) {
});
};

exports.stringifyProjection = function(projection) {
var stringifyProjection = exports.stringifyProjection = function(projection) {
var parts = [];

Object.keys(projection).sort().forEach(function(key) {
Expand All @@ -118,3 +127,11 @@ exports.stringifyProjection = function(projection) {

return '{' + parts.join(',') + '}';
};

exports.getUniqGroupId = function(relation) {
return [
relation.collection.collectionName,
relation.key || defaults.key,
stringifyProjection(relation.projection || defaults.projection)
].join('.');
};

0 comments on commit 3481daa

Please sign in to comment.