Merge pull request #611 from strongloop/feature/dedupe-in-query
Dedupe ids args of inq for include
This commit is contained in:
commit
8cf899d1b5
|
@ -3,6 +3,7 @@ var utils = require('./utils');
|
|||
var List = require('./list');
|
||||
var isPlainObject = utils.isPlainObject;
|
||||
var defineCachedRelations = utils.defineCachedRelations;
|
||||
var uniq = utils.uniq;
|
||||
|
||||
/*!
|
||||
* Normalize the include to be an array
|
||||
|
@ -308,7 +309,7 @@ Inclusion.include = function (objects, include, options, cb) {
|
|||
where: {}
|
||||
};
|
||||
throughFilter.where[relation.keyTo] = {
|
||||
inq: sourceIds
|
||||
inq: uniq(sourceIds)
|
||||
};
|
||||
if (polymorphic) {
|
||||
//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
|
||||
var modelToIdName = idName(relation.modelTo);
|
||||
filter.where[modelToIdName] = {
|
||||
inq: targetIds
|
||||
inq: uniq(targetIds)
|
||||
};
|
||||
|
||||
//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] = [];
|
||||
}
|
||||
filter.where[relation.keyTo] = {
|
||||
inq: allTargetIds
|
||||
inq: uniq(allTargetIds)
|
||||
};
|
||||
relation.applyScope(null, filter);
|
||||
/**
|
||||
|
@ -496,7 +497,7 @@ Inclusion.include = function (objects, include, options, cb) {
|
|||
obj.__cachedRelations[relationName] = [];
|
||||
}
|
||||
filter.where[relation.keyTo] = {
|
||||
inq: sourceIds
|
||||
inq: uniq(sourceIds)
|
||||
};
|
||||
relation.applyScope(null, filter);
|
||||
relation.modelTo.find(filter, targetFetchHandler);
|
||||
|
@ -574,7 +575,7 @@ Inclusion.include = function (objects, include, options, cb) {
|
|||
utils.mergeQuery(typeFilter, filter);
|
||||
var targetIds = targetIdsByType[modelType];
|
||||
typeFilter.where[relation.keyTo] = {
|
||||
inq: targetIds
|
||||
inq: uniq(targetIds)
|
||||
};
|
||||
var Model = lookupModel(relation.modelFrom.dataSource.modelBuilder.
|
||||
models, modelType);
|
||||
|
@ -650,7 +651,7 @@ Inclusion.include = function (objects, include, options, cb) {
|
|||
obj.__cachedRelations[relationName] = null;
|
||||
}
|
||||
filter.where[relation.keyTo] = {
|
||||
inq: targetIds
|
||||
inq: uniq(targetIds)
|
||||
};
|
||||
relation.applyScope(null, filter);
|
||||
relation.modelTo.find(filter, targetFetchHandler);
|
||||
|
|
23
lib/utils.js
23
lib/utils.js
|
@ -10,9 +10,11 @@ exports.sortObjectsByIds = sortObjectsByIds;
|
|||
exports.setScopeValuesFromWhere = setScopeValuesFromWhere;
|
||||
exports.mergeQuery = mergeQuery;
|
||||
exports.mergeIncludes = mergeIncludes;
|
||||
exports.createPromiseCallback = createPromiseCallback
|
||||
exports.createPromiseCallback = createPromiseCallback;
|
||||
exports.uniq = uniq;
|
||||
|
||||
var traverse = require('traverse');
|
||||
var assert = require('assert');
|
||||
|
||||
function safeRequire(module) {
|
||||
try {
|
||||
|
@ -475,3 +477,22 @@ function throwPromiseNotDefined() {
|
|||
'Your Node runtime does support ES6 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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
Post.find({
|
||||
include: { relation: 'author', scope:{ fields: ['name'] }}
|
||||
|
|
|
@ -5,6 +5,7 @@ var removeUndefined = utils.removeUndefined;
|
|||
var mergeSettings = utils.mergeSettings;
|
||||
var mergeIncludes = utils.mergeIncludes;
|
||||
var sortObjectsByIds = utils.sortObjectsByIds;
|
||||
var uniq = utils.uniq;
|
||||
|
||||
describe('util.fieldsToArray', function () {
|
||||
function sample(fields, excludeUnknown) {
|
||||
|
@ -242,7 +243,7 @@ describe('util.mergeIncludes', function () {
|
|||
'Merged include should match the expectation');
|
||||
}
|
||||
|
||||
it('Merge string values to object', function () {
|
||||
it('Merge string values to object', function() {
|
||||
var baseInclude = 'relation1';
|
||||
var updateInclude = 'relation2';
|
||||
var expectedInclude = [
|
||||
|
@ -252,7 +253,7 @@ describe('util.mergeIncludes', function () {
|
|||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
it('Merge string & array values to object', function () {
|
||||
it('Merge string & array values to object', function() {
|
||||
var baseInclude = 'relation1';
|
||||
var updateInclude = ['relation2'];
|
||||
var expectedInclude = [
|
||||
|
@ -262,7 +263,7 @@ describe('util.mergeIncludes', function () {
|
|||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
it('Merge string & object values to object', function () {
|
||||
it('Merge string & object values to object', function() {
|
||||
var baseInclude = ['relation1'];
|
||||
var updateInclude = {relation2: 'relation2Include'};
|
||||
var expectedInclude = [
|
||||
|
@ -272,7 +273,7 @@ describe('util.mergeIncludes', function () {
|
|||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
it('Merge array & array values to object', function () {
|
||||
it('Merge array & array values to object', function() {
|
||||
var baseInclude = ['relation1'];
|
||||
var updateInclude = ['relation2'];
|
||||
var expectedInclude = [
|
||||
|
@ -282,7 +283,7 @@ describe('util.mergeIncludes', function () {
|
|||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
it('Merge array & object values to object', function () {
|
||||
it('Merge array & object values to object', function() {
|
||||
var baseInclude = ['relation1'];
|
||||
var updateInclude = {relation2: 'relation2Include'};
|
||||
var expectedInclude = [
|
||||
|
@ -292,7 +293,7 @@ describe('util.mergeIncludes', function () {
|
|||
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 updateInclude = {relation2: 'relation2Include'};
|
||||
var expectedInclude = [
|
||||
|
@ -302,7 +303,7 @@ describe('util.mergeIncludes', function () {
|
|||
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 updateInclude = {relation1: 'updateValue'};
|
||||
var expectedInclude = [
|
||||
|
@ -312,7 +313,7 @@ describe('util.mergeIncludes', function () {
|
|||
});
|
||||
|
||||
it('Merge string includes & include with relation syntax properly',
|
||||
function () {
|
||||
function() {
|
||||
var baseInclude = 'relation1';
|
||||
var updateInclude = {relation: 'relation1'};
|
||||
var expectedInclude = [
|
||||
|
@ -321,7 +322,7 @@ describe('util.mergeIncludes', function () {
|
|||
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 updateInclude = {
|
||||
relation: 'relation1',
|
||||
|
@ -334,7 +335,7 @@ describe('util.mergeIncludes', function () {
|
|||
});
|
||||
|
||||
it('Merge includes with and without relation syntax properly',
|
||||
function () {
|
||||
function() {
|
||||
//w & w/o relation syntax - no collision
|
||||
var baseInclude = ['relation2'];
|
||||
var updateInclude = {
|
||||
|
@ -361,7 +362,7 @@ describe('util.mergeIncludes', function () {
|
|||
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},
|
||||
{relation: 'relation3', 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'}}}];
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue