Dedupe ids args of inq for include

This commit is contained in:
Raymond Feng 2015-05-29 10:50:37 -07:00
parent 56344fe99e
commit 493d98f66b
4 changed files with 104 additions and 18 deletions

View File

@ -3,6 +3,7 @@ var utils = require('./utils');
var List = require('./list'); var List = require('./list');
var isPlainObject = utils.isPlainObject; var isPlainObject = utils.isPlainObject;
var defineCachedRelations = utils.defineCachedRelations; var defineCachedRelations = utils.defineCachedRelations;
var uniq = utils.uniq;
/*! /*!
* Normalize the include to be an array * Normalize the include to be an array
@ -308,7 +309,7 @@ Inclusion.include = function (objects, include, options, cb) {
where: {} where: {}
}; };
throughFilter.where[relation.keyTo] = { throughFilter.where[relation.keyTo] = {
inq: sourceIds inq: uniq(sourceIds)
}; };
if (polymorphic) { if (polymorphic) {
//handle polymorphic hasMany (reverse) in which case we need to filter //handle polymorphic hasMany (reverse) in which case we need to filter
@ -351,7 +352,7 @@ Inclusion.include = function (objects, include, options, cb) {
//Polymorphic relation does not have idKey of modelTo. Find it manually //Polymorphic relation does not have idKey of modelTo. Find it manually
var modelToIdName = idName(relation.modelTo); var modelToIdName = idName(relation.modelTo);
filter.where[modelToIdName] = { filter.where[modelToIdName] = {
inq: targetIds inq: uniq(targetIds)
}; };
//make sure that the modelToIdName is included if fields are specified //make sure that the modelToIdName is included if fields are specified
@ -432,7 +433,7 @@ Inclusion.include = function (objects, include, options, cb) {
obj.__cachedRelations[relationName] = []; obj.__cachedRelations[relationName] = [];
} }
filter.where[relation.keyTo] = { filter.where[relation.keyTo] = {
inq: allTargetIds inq: uniq(allTargetIds)
}; };
relation.applyScope(null, filter); relation.applyScope(null, filter);
/** /**
@ -496,7 +497,7 @@ Inclusion.include = function (objects, include, options, cb) {
obj.__cachedRelations[relationName] = []; obj.__cachedRelations[relationName] = [];
} }
filter.where[relation.keyTo] = { filter.where[relation.keyTo] = {
inq: sourceIds inq: uniq(sourceIds)
}; };
relation.applyScope(null, filter); relation.applyScope(null, filter);
relation.modelTo.find(filter, targetFetchHandler); relation.modelTo.find(filter, targetFetchHandler);
@ -574,7 +575,7 @@ Inclusion.include = function (objects, include, options, cb) {
utils.mergeQuery(typeFilter, filter); utils.mergeQuery(typeFilter, filter);
var targetIds = targetIdsByType[modelType]; var targetIds = targetIdsByType[modelType];
typeFilter.where[relation.keyTo] = { typeFilter.where[relation.keyTo] = {
inq: targetIds inq: uniq(targetIds)
}; };
var Model = lookupModel(relation.modelFrom.dataSource.modelBuilder. var Model = lookupModel(relation.modelFrom.dataSource.modelBuilder.
models, modelType); models, modelType);
@ -650,7 +651,7 @@ Inclusion.include = function (objects, include, options, cb) {
obj.__cachedRelations[relationName] = null; obj.__cachedRelations[relationName] = null;
} }
filter.where[relation.keyTo] = { filter.where[relation.keyTo] = {
inq: targetIds inq: uniq(targetIds)
}; };
relation.applyScope(null, filter); relation.applyScope(null, filter);
relation.modelTo.find(filter, targetFetchHandler); relation.modelTo.find(filter, targetFetchHandler);

View File

@ -10,9 +10,11 @@ exports.sortObjectsByIds = sortObjectsByIds;
exports.setScopeValuesFromWhere = setScopeValuesFromWhere; exports.setScopeValuesFromWhere = setScopeValuesFromWhere;
exports.mergeQuery = mergeQuery; exports.mergeQuery = mergeQuery;
exports.mergeIncludes = mergeIncludes; exports.mergeIncludes = mergeIncludes;
exports.createPromiseCallback = createPromiseCallback exports.createPromiseCallback = createPromiseCallback;
exports.uniq = uniq;
var traverse = require('traverse'); var traverse = require('traverse');
var assert = require('assert');
function safeRequire(module) { function safeRequire(module) {
try { try {
@ -475,3 +477,22 @@ function throwPromiseNotDefined() {
'Your Node runtime does support ES6 Promises. ' + 'Your Node runtime does support ES6 Promises. ' +
'Set "global.Promise" to your preferred implementation of promises.'); 'Set "global.Promise" to your preferred implementation of promises.');
} }
/**
* Dedupe an array
* @param {Array} an array
* @returns {Array} an array with unique items
*/
function uniq(a) {
var uniqArray = [];
if (!a) {
return uniqArray;
}
assert(Array.isArray(a), 'array argument is required');
for (var i = 0, n = a.length; i < n; i++) {
if (a.indexOf(a[i]) === i) {
uniqArray.push(a[i]);
}
}
return uniqArray;
}

View File

@ -160,6 +160,26 @@ describe('include', function () {
}); });
}); });
it('should support limit', function(done) {
Passport.find({
include: {
owner: {
relation: 'posts', scope: {
fields: ['title'], include: ['author'],
order: 'title DESC',
limit: 2
}
}
},
limit: 1
}, function(err, passports) {
if (err) return done(err);
passports.length.should.equal(1);
passports[0].toJSON().owner.posts.length.should.equal(2);
done();
});
});
it('should fetch Users with include scope on Posts - belongsTo', function (done) { it('should fetch Users with include scope on Posts - belongsTo', function (done) {
Post.find({ Post.find({
include: { relation: 'author', scope:{ fields: ['name'] }} include: { relation: 'author', scope:{ fields: ['name'] }}

View File

@ -5,6 +5,7 @@ var removeUndefined = utils.removeUndefined;
var mergeSettings = utils.mergeSettings; var mergeSettings = utils.mergeSettings;
var mergeIncludes = utils.mergeIncludes; var mergeIncludes = utils.mergeIncludes;
var sortObjectsByIds = utils.sortObjectsByIds; var sortObjectsByIds = utils.sortObjectsByIds;
var uniq = utils.uniq;
describe('util.fieldsToArray', function () { describe('util.fieldsToArray', function () {
function sample(fields, excludeUnknown) { function sample(fields, excludeUnknown) {
@ -242,7 +243,7 @@ describe('util.mergeIncludes', function () {
'Merged include should match the expectation'); 'Merged include should match the expectation');
} }
it('Merge string values to object', function () { it('Merge string values to object', function() {
var baseInclude = 'relation1'; var baseInclude = 'relation1';
var updateInclude = 'relation2'; var updateInclude = 'relation2';
var expectedInclude = [ var expectedInclude = [
@ -252,7 +253,7 @@ describe('util.mergeIncludes', function () {
checkInputOutput(baseInclude, updateInclude, expectedInclude); checkInputOutput(baseInclude, updateInclude, expectedInclude);
}); });
it('Merge string & array values to object', function () { it('Merge string & array values to object', function() {
var baseInclude = 'relation1'; var baseInclude = 'relation1';
var updateInclude = ['relation2']; var updateInclude = ['relation2'];
var expectedInclude = [ var expectedInclude = [
@ -262,7 +263,7 @@ describe('util.mergeIncludes', function () {
checkInputOutput(baseInclude, updateInclude, expectedInclude); checkInputOutput(baseInclude, updateInclude, expectedInclude);
}); });
it('Merge string & object values to object', function () { it('Merge string & object values to object', function() {
var baseInclude = ['relation1']; var baseInclude = ['relation1'];
var updateInclude = {relation2: 'relation2Include'}; var updateInclude = {relation2: 'relation2Include'};
var expectedInclude = [ var expectedInclude = [
@ -272,7 +273,7 @@ describe('util.mergeIncludes', function () {
checkInputOutput(baseInclude, updateInclude, expectedInclude); checkInputOutput(baseInclude, updateInclude, expectedInclude);
}); });
it('Merge array & array values to object', function () { it('Merge array & array values to object', function() {
var baseInclude = ['relation1']; var baseInclude = ['relation1'];
var updateInclude = ['relation2']; var updateInclude = ['relation2'];
var expectedInclude = [ var expectedInclude = [
@ -282,7 +283,7 @@ describe('util.mergeIncludes', function () {
checkInputOutput(baseInclude, updateInclude, expectedInclude); checkInputOutput(baseInclude, updateInclude, expectedInclude);
}); });
it('Merge array & object values to object', function () { it('Merge array & object values to object', function() {
var baseInclude = ['relation1']; var baseInclude = ['relation1'];
var updateInclude = {relation2: 'relation2Include'}; var updateInclude = {relation2: 'relation2Include'};
var expectedInclude = [ var expectedInclude = [
@ -292,7 +293,7 @@ describe('util.mergeIncludes', function () {
checkInputOutput(baseInclude, updateInclude, expectedInclude); checkInputOutput(baseInclude, updateInclude, expectedInclude);
}); });
it('Merge object & object values to object', function () { it('Merge object & object values to object', function() {
var baseInclude = {relation1: 'relation1Include'}; var baseInclude = {relation1: 'relation1Include'};
var updateInclude = {relation2: 'relation2Include'}; var updateInclude = {relation2: 'relation2Include'};
var expectedInclude = [ var expectedInclude = [
@ -302,7 +303,7 @@ describe('util.mergeIncludes', function () {
checkInputOutput(baseInclude, updateInclude, expectedInclude); checkInputOutput(baseInclude, updateInclude, expectedInclude);
}); });
it('Override property collision with update value', function () { it('Override property collision with update value', function() {
var baseInclude = {relation1: 'baseValue'}; var baseInclude = {relation1: 'baseValue'};
var updateInclude = {relation1: 'updateValue'}; var updateInclude = {relation1: 'updateValue'};
var expectedInclude = [ var expectedInclude = [
@ -312,7 +313,7 @@ describe('util.mergeIncludes', function () {
}); });
it('Merge string includes & include with relation syntax properly', it('Merge string includes & include with relation syntax properly',
function () { function() {
var baseInclude = 'relation1'; var baseInclude = 'relation1';
var updateInclude = {relation: 'relation1'}; var updateInclude = {relation: 'relation1'};
var expectedInclude = [ var expectedInclude = [
@ -321,7 +322,7 @@ describe('util.mergeIncludes', function () {
checkInputOutput(baseInclude, updateInclude, expectedInclude); checkInputOutput(baseInclude, updateInclude, expectedInclude);
}); });
it('Merge string includes & include with scope properly', function () { it('Merge string includes & include with scope properly', function() {
var baseInclude = 'relation1'; var baseInclude = 'relation1';
var updateInclude = { var updateInclude = {
relation: 'relation1', relation: 'relation1',
@ -334,7 +335,7 @@ describe('util.mergeIncludes', function () {
}); });
it('Merge includes with and without relation syntax properly', it('Merge includes with and without relation syntax properly',
function () { function() {
//w & w/o relation syntax - no collision //w & w/o relation syntax - no collision
var baseInclude = ['relation2']; var baseInclude = ['relation2'];
var updateInclude = { var updateInclude = {
@ -361,7 +362,7 @@ describe('util.mergeIncludes', function () {
checkInputOutput(baseInclude, updateInclude, expectedInclude); checkInputOutput(baseInclude, updateInclude, expectedInclude);
}); });
it('Merge includes with mixture of strings, arrays & objects properly', function () { it('Merge includes with mixture of strings, arrays & objects properly', function() {
var baseInclude = ['relation1', {relation2: true}, var baseInclude = ['relation1', {relation2: true},
{relation: 'relation3', scope: {where: {id: 'some id'}}}, {relation: 'relation3', scope: {where: {id: 'some id'}}},
{relation: 'relation5', scope: {where: {id: 'some id'}}} {relation: 'relation5', scope: {where: {id: 'some id'}}}
@ -374,5 +375,48 @@ describe('util.mergeIncludes', function () {
{relation: 'relation5', scope: {where: {id: 'some id'}}}]; {relation: 'relation5', scope: {where: {id: 'some id'}}}];
checkInputOutput(baseInclude, updateInclude, expectedInclude); checkInputOutput(baseInclude, updateInclude, expectedInclude);
}); });
});
describe('util.uniq', function() {
it('should dedupe an array with duplicate number entries', function() {
var a = [1, 2, 1, 3];
var b = uniq(a);
b.should.eql([1, 2, 3]);
});
it('should dedupe an array with duplicate string entries', function() {
var a = ['a', 'a', 'b', 'a'];
var b = uniq(a);
b.should.eql(['a', 'b']);
});
it('should dedupe an array without duplicate number entries', function() {
var a = [1, 3, 2];
var b = uniq(a);
b.should.eql([1, 3, 2]);
});
it('should dedupe an array without duplicate string entries', function() {
var a = ['a', 'c', 'b'];
var b = uniq(a);
b.should.eql(['a', 'c', 'b']);
});
it('should allow null/undefined array', function() {
var a = null;
var b = uniq(a);
b.should.eql([]);
});
it('should report error for non-array arg', function() {
var a = '1';
try {
var b = uniq(a);
throw new Error('The test should have thrown an error');
} catch (err) {
err.should.be.instanceof(Error);
}
});
}); });