Merge pull request #626 from walkonsocial/include-issues

Fix for issues #622 & #623
This commit is contained in:
Raymond Feng 2015-06-16 08:55:29 -07:00
commit bbe9cb350c
2 changed files with 137 additions and 6 deletions

View File

@ -272,7 +272,10 @@ Inclusion.include = function (objects, include, options, cb) {
}
else {
if (polymorphic) {
return includePolymorphic(cb);
if (relation.type === 'hasOne') {
return includePolymorphicHasOne(cb);
}
return includePolymorphicBelongsTo(cb);
}
if (relation.type === 'embedsOne') {
return includeEmbeds(cb);
@ -387,6 +390,7 @@ Inclusion.include = function (objects, include, options, cb) {
var targetId = target[modelToIdName];
var objList = targetObjsMap[targetId.toString()];
async.each(objList, function (obj, next) {
if (!obj) return next();
obj.__cachedRelations[relationName].push(target);
processTargetObj(obj, next);
}, next);
@ -464,6 +468,7 @@ Inclusion.include = function (objects, include, options, cb) {
function linkManyToMany(target, next) {
var objList = targetObjsMap[target[relation.keyTo].toString()];
async.each(objList, function (obj, next) {
if (!obj) return next();
obj.__cachedRelations[relationName].push(target);
processTargetObj(obj, next);
}, next);
@ -523,9 +528,14 @@ Inclusion.include = function (objects, include, options, cb) {
function targetLinkingTask(next) {
async.each(targets, linkManyToOne, next);
function linkManyToOne(target, next) {
var obj = objIdMap[target[relation.keyTo].toString()];
obj.__cachedRelations[relationName].push(target);
processTargetObj(obj, next);
//fix for bug in hasMany with referencesMany
var targetIds = [].concat(target[relation.keyTo]);
async.each(targetIds, function (targetId, next) {
var obj = objIdMap[targetId.toString()];
if (!obj) return next();
obj.__cachedRelations[relationName].push(target);
processTargetObj(obj, next);
}, next);
}
}
@ -534,10 +544,10 @@ Inclusion.include = function (objects, include, options, cb) {
}
/**
* Handle Inclusion of Polymorphic BelongsTo/HasOne relation
* Handle Inclusion of Polymorphic BelongsTo relation
* @param callback
*/
function includePolymorphic(callback) {
function includePolymorphicBelongsTo(callback) {
var targetIdsByType = {};
//Map for Indexing objects by their type and targetId for faster retrieval
var targetObjMapByType = {};
@ -612,6 +622,7 @@ Inclusion.include = function (objects, include, options, cb) {
function linkOneToMany(target, next) {
var objList = targetObjsMap[target[relation.keyTo].toString()];
async.each(objList, function (obj, next) {
if (!obj) return next();
obj.__cachedRelations[relationName] = target;
processTargetObj(obj, next);
}, next);
@ -623,6 +634,68 @@ Inclusion.include = function (objects, include, options, cb) {
}
}
/**
* Handle Inclusion of Polymorphic HasOne relation
* @param callback
*/
function includePolymorphicHasOne(callback) {
var sourceIds = [];
//Map for Indexing objects by their id for faster retrieval
var objIdMap = {};
for (var i = 0; i < objs.length; i++) {
var obj = objs[i];
// one-to-one: foreign key reference is modelTo -> modelFrom.
// use modelFrom.keyFrom in where filter later
var sourceId = obj[relation.keyFrom];
if (sourceId) {
sourceIds.push(sourceId);
objIdMap[sourceId.toString()] = obj;
}
// sourceId can be null. but cache empty data as result
defineCachedRelations(obj);
obj.__cachedRelations[relationName] = null;
}
filter.where[relation.keyTo] = {
inq: uniq(sourceIds)
};
relation.applyScope(null, filter);
relation.modelTo.find(filter, targetFetchHandler);
/**
* Process fetched related objects
* @param err
* @param {Array<Model>} targets
* @returns {*}
*/
function targetFetchHandler(err, targets) {
if (err) {
return callback(err);
}
var tasks = [];
//simultaneously process subIncludes
if (subInclude && targets) {
tasks.push(function subIncludesTask(next) {
relation.modelTo.include(targets, subInclude, options, next);
});
}
//process each target object
tasks.push(targetLinkingTask);
function targetLinkingTask(next) {
async.each(targets, linkOneToOne, next);
function linkOneToOne(target, next) {
var sourceId = target[relation.keyTo];
if (!sourceId) return next();
var obj = objIdMap[sourceId.toString()];
if (!obj) return next();
obj.__cachedRelations[relationName] = target;
processTargetObj(obj, next);
}
}
execTasksWithInterLeave(tasks, callback);
}
}
/**
* Handle Inclusion of BelongsTo/HasOne relation
* @param callback
@ -680,6 +753,7 @@ Inclusion.include = function (objects, include, options, cb) {
var targetId = target[relation.keyTo];
var objList = objTargetIdMap[targetId.toString()];
async.each(objList, function (obj, next) {
if (!obj) return next();
obj.__cachedRelations[relationName] = target;
processTargetObj(obj, next);
}, next);

View File

@ -1663,6 +1663,16 @@ describe('relations', function () {
});
});
it('should include polymorphic relation - author', function (done) {
Author.findOne({include: 'avatar'}, function (err, author) {
should.not.exists(err);
var avatar = author.avatar();
should.exist(avatar);
avatar.name.should.equal('Avatar');
done();
});
});
it('should find polymorphic relation with promises - reader', function (done) {
Reader.findOne()
.then(function (reader) {
@ -1688,6 +1698,18 @@ describe('relations', function () {
});
});
it('should include inverse polymorphic relation - author', function (done) {
Picture.findOne({where: {name: 'Avatar'}, include: 'imageable'},
function (err, p) {
should.not.exists(err);
var imageable = p.imageable();
should.exist(imageable);
imageable.should.be.instanceof(Author);
imageable.name.should.equal('Author 1');
done();
});
});
it('should find inverse polymorphic relation - reader', function (done) {
Picture.findOne({ where: { name: 'Mugshot' } }, function (err, p) {
should.not.exists(err);
@ -1843,6 +1865,28 @@ describe('relations', function () {
});
});
it('should include polymorphic relation - reader', function (done) {
Reader.findOne({include: 'mugshot'},
function (err, reader) {
should.not.exists(err);
var mugshot = reader.mugshot();
should.exist(mugshot);
mugshot.name.should.equal('Mugshot');
done();
});
});
it('should include inverse polymorphic relation - reader', function (done) {
Picture.findOne({where: {name: 'Mugshot'}, include: 'owner'},
function (err, p) {
should.not.exists(err);
var owner = p.owner();
should.exist(owner);
owner.should.be.instanceof(Reader);
owner.name.should.equal('Reader 1');
done();
});
});
});
describe('polymorphic hasMany', function () {
@ -2030,6 +2074,19 @@ describe('relations', function () {
});
});
it('should include the inverse of polymorphic relation - author',
function (done) {
Picture.findOne({where: {name: 'Sample'}, include: 'imageable'},
function (err, p) {
should.not.exist(err);
var imageable = p.imageable();
should.exist(imageable);
imageable.should.be.instanceof(Author);
imageable.name.should.equal('Author 2');
done();
});
});
});
describe('polymorphic hasAndBelongsToMany through', function () {