Skip to content

Commit

Permalink
Fix bug loopbackio#1976 querying related models
Browse files Browse the repository at this point in the history
  • Loading branch information
regevbr committed Nov 30, 2019
1 parent bdf4d87 commit 9fe3858
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 138 deletions.
189 changes: 51 additions & 138 deletions lib/scope.js
Original file line number Diff line number Diff line change
Expand Up @@ -468,41 +468,9 @@ function defineScope(cls, targetClass, name, params, methods, options) {
return targetModel.destroyAll(filter.where, options, cb);
}
return targetModel.find(filter, options, function(err, findData) {
// return {inq: [1,2,3]}}
const smartMerge = function(idCollection, qWhere) {
if (!qWhere[IdKey]) return idCollection;
let merged = {};

const idsA = idCollection.inq;
const idsB = qWhere[IdKey].inq ? qWhere[IdKey].inq : [qWhere[IdKey]];

const intersect = _.intersectionWith(idsA, idsB, _.isEqual);
if (intersect.length === 1) merged = intersect[0];
if (intersect.length > 1) merged = {inq: intersect};

return merged;
};

if (queryRelated.where !== undefined) {
// Merge queryRelated filter and targetId filter
const IdKeyCondition = {};
IdKeyCondition[IdKey] = smartMerge(collectTargetIds(findData, keyFrom),
queryRelated.where);

// if the id in filter doesn't exist after the merge,
// return empty result
if (_.isObject(IdKeyCondition[IdKey]) && _.isEmpty(IdKeyCondition[IdKey])) return cb(null, []);

const mergedWhere = {
and: [
IdKeyCondition,
_.omit(queryRelated.where, IdKey),
],
};
queryRelated.where = mergedWhere;
} else {
queryRelated.where = {};
queryRelated.where[IdKey] = collectTargetIds(findData, keyFrom);
const smartMergeSuccessful = smartMergeRelatedModelQuery(findData, queryRelated.scope, keyFrom, IdKey);
if (!smartMergeSuccessful) {
return cb(null, {count: 0});
}
return relatedModel.destroyAll(queryRelated.scope.where, options, cb);
});
Expand Down Expand Up @@ -559,41 +527,9 @@ function defineScope(cls, targetClass, name, params, methods, options) {
return targetModel.updateAll(filter.where, data, options, cb);
}
return targetModel.find(filter, options, function(err, findData) {
// return {inq: [1,2,3]}}
const smartMerge = function(idCollection, qWhere) {
if (!qWhere[IdKey]) return idCollection;
let merged = {};

const idsA = idCollection.inq;
const idsB = qWhere[IdKey].inq ? qWhere[IdKey].inq : [qWhere[IdKey]];

const intersect = _.intersectionWith(idsA, idsB, _.isEqual);
if (intersect.length === 1) merged = intersect[0];
if (intersect.length > 1) merged = {inq: intersect};

return merged;
};

if (queryRelated.where !== undefined) {
// Merge queryRelated filter and targetId filter
const IdKeyCondition = {};
IdKeyCondition[IdKey] = smartMerge(collectTargetIds(findData, keyFrom),
queryRelated.where);

// if the id in filter doesn't exist after the merge,
// return empty result
if (_.isObject(IdKeyCondition[IdKey]) && _.isEmpty(IdKeyCondition[IdKey])) return cb(null, []);

const mergedWhere = {
and: [
IdKeyCondition,
_.omit(queryRelated.where, IdKey),
],
};
queryRelated.where = mergedWhere;
} else {
queryRelated.where = {};
queryRelated.where[IdKey] = collectTargetIds(findData, keyFrom);
const smartMergeSuccessful = smartMergeRelatedModelQuery(findData, queryRelated.scope, keyFrom, IdKey);
if (!smartMergeSuccessful) {
return cb(null, {count: 0});
}
return relatedModel.updateAll(queryRelated.scope.where, data, options, cb);
});
Expand Down Expand Up @@ -679,41 +615,9 @@ function defineScope(cls, targetClass, name, params, methods, options) {
return targetModel.findOne(filter, options, cb);
}
return targetModel.find(filter, options, function(err, findData) {
// return {inq: [1,2,3]}}
const smartMerge = function(idCollection, qWhere) {
if (!qWhere[IdKey]) return idCollection;
let merged = {};

const idsA = idCollection.inq;
const idsB = qWhere[IdKey].inq ? qWhere[IdKey].inq : [qWhere[IdKey]];

const intersect = _.intersectionWith(idsA, idsB, _.isEqual);
if (intersect.length === 1) merged = intersect[0];
if (intersect.length > 1) merged = {inq: intersect};

return merged;
};

if (queryRelated.where !== undefined) {
// Merge queryRelated filter and targetId filter
const IdKeyCondition = {};
IdKeyCondition[IdKey] = smartMerge(collectTargetIds(findData, keyFrom),
queryRelated.where);

// if the id in filter doesn't exist after the merge,
// return empty result
if (_.isObject(IdKeyCondition[IdKey]) && _.isEmpty(IdKeyCondition[IdKey])) return cb(null, []);

const mergedWhere = {
and: [
IdKeyCondition,
_.omit(queryRelated.where, IdKey),
],
};
queryRelated.where = mergedWhere;
} else {
queryRelated.where = {};
queryRelated.where[IdKey] = collectTargetIds(findData, keyFrom);
const smartMergeSuccessful = smartMergeRelatedModelQuery(findData, queryRelated.scope, keyFrom, IdKey);
if (!smartMergeSuccessful) {
return cb(null, null);
}
return relatedModel.findOne(queryRelated.scope, options, cb);
});
Expand Down Expand Up @@ -767,44 +671,53 @@ function defineScope(cls, targetClass, name, params, methods, options) {
return targetModel.count(filter.where, options, cb);
}
return targetModel.find(filter, options, function(err, findData) {
// return {inq: [1,2,3]}}
const smartMerge = function(idCollection, qWhere) {
if (!qWhere[IdKey]) return idCollection;
let merged = {};
const smartMergeSuccessful = smartMergeRelatedModelQuery(findData, queryRelated.scope, keyFrom, IdKey);
if (!smartMergeSuccessful) {
return cb(null, 0);
}
return relatedModel.count(queryRelated.scope.where, options, cb);
});
}

const idsA = idCollection.inq;
const idsB = qWhere[IdKey].inq ? qWhere[IdKey].inq : [qWhere[IdKey]];
function smartMergeRelatedModelQuery(findData, queryRelated, keyFrom, IdKey) {
const smartMerge = function(idCollection, qWhere) {
if (!qWhere[IdKey]) return idCollection;
let merged = {};

const intersect = _.intersectionWith(idsA, idsB, _.isEqual);
if (intersect.length === 1) merged = intersect[0];
if (intersect.length > 1) merged = {inq: intersect};
const idsA = idCollection.inq;
const idsB = qWhere[IdKey].inq ? qWhere[IdKey].inq : [qWhere[IdKey]];

return merged;
};
const intersect = _.intersectionWith(idsA, idsB, _.isEqual);
if (intersect.length === 1) merged = intersect[0];
if (intersect.length > 1) merged = {inq: intersect};

if (queryRelated.where !== undefined) {
// Merge queryRelated filter and targetId filter
const IdKeyCondition = {};
IdKeyCondition[IdKey] = smartMerge(collectTargetIds(findData, keyFrom),
queryRelated.where);

// if the id in filter doesn't exist after the merge,
// return empty result
if (_.isObject(IdKeyCondition[IdKey]) && _.isEmpty(IdKeyCondition[IdKey])) return cb(null, []);

const mergedWhere = {
and: [
IdKeyCondition,
_.omit(queryRelated.where, IdKey),
],
};
queryRelated.where = mergedWhere;
} else {
queryRelated.where = {};
queryRelated.where[IdKey] = collectTargetIds(findData, keyFrom);
return merged;
};

if (queryRelated.where !== undefined) {
// Merge queryRelated filter and targetId filter
const IdKeyCondition = {};
IdKeyCondition[IdKey] = smartMerge(collectTargetIds(findData, keyFrom),
queryRelated.where);

// if the id in filter doesn't exist after the merge,
// return empty result
if (_.isObject(IdKeyCondition[IdKey]) && _.isEmpty(IdKeyCondition[IdKey])) {
return false;
}
return relatedModel.count(queryRelated.scope.where, options, cb);
});

const mergedWhere = {
and: [
IdKeyCondition,
_.omit(queryRelated.where, IdKey),
],
};
queryRelated.where = mergedWhere;
} else {
queryRelated.where = {};
queryRelated.where[IdKey] = collectTargetIds(findData, keyFrom);
}
return true;
}

return definition;
Expand Down
120 changes: 120 additions & 0 deletions test/relations.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,35 @@ describe('relations', function() {
}
});

it('should count scoped record with promises based on related model ' +
'properties with no results', function(done) {
let id;
Physician.create()
.then(function(physician) {
return physician.patients.create({name: 'a'})
.then(function(ch) {
id = ch.id;
return physician.patients.create({name: 'z'});
})
.then(function() {
return physician.patients.create({name: 'c'});
})
.then(function() {
return verify(physician);
});
}).catch(done);

function verify(physician) {
return physician.patients.count({
id: 'bar',
}, function(err, count) {
if (err) return done(err);
count.should.equal(0);
done();
});
}
});

it('should count scoped record with promises based on related model id', function(done) {
let id;
Physician.create()
Expand Down Expand Up @@ -722,6 +751,37 @@ describe('relations', function() {
}
});

it('should find one scoped record with promises based on related model' +
' properties with empty results', function(done) {
let id;
Physician.create()
.then(function(physician) {
return physician.patients.create({name: 'a'})
.then(function(ch) {
id = ch.id;
return physician.patients.create({name: 'z'});
})
.then(function() {
return physician.patients.create({name: 'c'});
})
.then(function() {
return verify(physician);
});
}).catch(done);

function verify(physician) {
return physician.patients.findOne({
where: {
id: 'bar',
},
}, function(err, patient) {
if (err) return done(err);
should.not.exist(patient);
done();
});
}
});

it('should find one scoped record with promises based on related model id', function(done) {
let id;
Physician.create()
Expand Down Expand Up @@ -824,6 +884,36 @@ describe('relations', function() {
}
});

it('should update all scoped record with promises based on related ' +
'model properties no results', function(done) {
let id;
Physician.create()
.then(function(physician) {
return physician.patients.create({name: 'a'})
.then(function(ch) {
id = ch.id;
return physician.patients.create({name: 'z'});
})
.then(function() {
return physician.patients.create({name: 'c'});
})
.then(function() {
return verify(physician);
});
}).catch(done);

function verify(physician) {
return physician.patients.updateAll({
id: 'bar',
}, {age: 5}, function(err, result) {
if (err) return done(err);
should.exist(result);
result.count.should.equal(0);
done();
});
}
});

it('should update all scoped record with promises based on related model id', function(done) {
let id;
Physician.create()
Expand Down Expand Up @@ -931,6 +1021,36 @@ describe('relations', function() {
}
});

it('should destroyAll all scoped record with promises based on related ' +
'model properties no results', function(done) {
let id;
Physician.create()
.then(function(physician) {
return physician.patients.create({name: 'a'})
.then(function(ch) {
id = ch.id;
return physician.patients.create({name: 'z'});
})
.then(function() {
return physician.patients.create({name: 'c'});
})
.then(function() {
return verify(physician);
});
}).catch(done);

function verify(physician) {
return physician.patients.destroyAll({
id: 'foo',
}, function(err, result) {
if (err) return done(err);
should.exist(result);
result.count.should.equal(0);
done();
});
}
});

it('should destroyAll all scoped record with promises based on related model id', function(done) {
let id;
Physician.create()
Expand Down

0 comments on commit 9fe3858

Please sign in to comment.