Merge pull request #476 from strongloop/feature/fix-lb-1076

Make sure inclusion filter is applied to the target model
This commit is contained in:
Raymond Feng 2015-03-02 11:04:12 -08:00
commit 4bbbf7906e
3 changed files with 92 additions and 19 deletions

View File

@ -1141,17 +1141,22 @@ DataAccessObject.find = function find(query, options, cb) {
} }
var allCb = function(err, data) { var allCb = function(err, data) {
if (data && data.forEach) { var results = [];
data.forEach(function (d, i) { if (Array.isArray(data)) {
for (var i = 0, n = data.length; i < n; i++) {
var d = data[i];
var Model = self.lookupModel(d); var Model = self.lookupModel(d);
var obj = new Model(d, {fields: query.fields, applySetters: false, persisted: true}); var obj = new Model(d, {fields: query.fields, applySetters: false, persisted: true});
if (query && query.include) { if (query && query.include) {
if (query.collect) { if (query.collect) {
// The collect property indicates that the query is to return the // The collect property indicates that the query is to return the
// standlone items for a related model, not as child of the parent object // standalone items for a related model, not as child of the parent object
// For example, article.tags // For example, article.tags
obj = obj.__cachedRelations[query.collect]; obj = obj.__cachedRelations[query.collect];
if (obj === null) {
obj = undefined;
}
} else { } else {
// This handles the case to return parent items including the related // This handles the case to return parent items including the related
// models. For example, Article.find({include: 'tags'}, ...); // models. For example, Article.find({include: 'tags'}, ...);
@ -1164,30 +1169,32 @@ DataAccessObject.find = function find(query, options, cb) {
} }
// Promote the included model as a direct property // Promote the included model as a direct property
var data = obj.__cachedRelations[relationName]; var included = obj.__cachedRelations[relationName];
if(Array.isArray(data)) { if (Array.isArray(included)) {
data = new List(data, null, obj); included = new List(included, null, obj);
} }
if (data) obj.__data[relationName] = data; if (included) obj.__data[relationName] = included;
}); });
delete obj.__data.__cachedRelations; delete obj.__data.__cachedRelations;
} }
} }
data[i] = obj; if (obj !== undefined) {
}); results.push(obj);
}
}
if (data && data.countBeforeLimit) { if (data && data.countBeforeLimit) {
data.countBeforeLimit = data.countBeforeLimit; results.countBeforeLimit = data.countBeforeLimit;
} }
if (!supportsGeo && near) { if (!supportsGeo && near) {
data = geo.filter(data, near); results = geo.filter(results, near);
} }
cb(err, data); cb(err, results);
} }
else else
cb(err, []); cb(err, []);
} };
if (options.notify === false) { if (options.notify === false) {
self.getDataSource().connector.all(self.modelName, query, allCb); self.getDataSource().connector.all(self.modelName, query, allCb);

View File

@ -156,11 +156,25 @@ function defineScope(cls, targetClass, name, params, methods, options) {
} else if (self.__cachedRelations) { } else if (self.__cachedRelations) {
return self.__cachedRelations[name]; return self.__cachedRelations[name];
} }
} else if (arguments.length === 1) { } else {
// Check if there is a through model
// see https://github.com/strongloop/loopback/issues/1076
if (f._scope.collect &&
condOrRefresh !== null && typeof condOrRefresh === 'object') {
// Adjust the include so that the condition will be applied to
// the target model
f._scope.include = {
relation: f._scope.collect,
scope: condOrRefresh
};
condOrRefresh = {};
}
if (arguments.length === 1) {
return definition.related(self, f._scope, condOrRefresh); return definition.related(self, f._scope, condOrRefresh);
} else { } else {
return definition.related(self, f._scope, condOrRefresh, cb); return definition.related(self, f._scope, condOrRefresh, cb);
} }
}
}; };
f._receiver = this; f._receiver = this;

View File

@ -2066,7 +2066,7 @@ describe('relations', function () {
var Article, TagName, ArticleTag; var Article, TagName, ArticleTag;
it('can be declared', function (done) { it('can be declared', function (done) {
Article = db.define('Article', {title: String}); Article = db.define('Article', {title: String});
TagName = db.define('TagName', {name: String}); TagName = db.define('TagName', {name: String, flag: String});
Article.hasAndBelongsToMany('tagNames'); Article.hasAndBelongsToMany('tagNames');
ArticleTag = db.models.ArticleTagName; ArticleTag = db.models.ArticleTagName;
db.automigrate(function () { db.automigrate(function () {
@ -2139,6 +2139,58 @@ describe('relations', function () {
it('should set targetClass on scope property', function() { it('should set targetClass on scope property', function() {
should.equal(Article.prototype.tagNames._targetClass, 'TagName'); should.equal(Article.prototype.tagNames._targetClass, 'TagName');
}); });
it('should apply inclusion fields to the target model', function(done) {
Article.create({title: 'a1'}, function (e, article) {
should.not.exist(e);
article.tagNames.create({name: 't1', flag: '1'}, function(e, t) {
should.not.exist(e);
Article.find({
where: {id: article.id},
include: {relation: 'tagNames', scope: {fields: ['name']}}},
function(e, articles) {
should.not.exist(e);
articles.should.have.property('length', 1);
var a = articles[0].toJSON();
a.should.have.property('title', 'a1');
a.should.have.property('tagNames');
a.tagNames.should.have.property('length', 1);
var n = a.tagNames[0];
n.should.have.property('name', 't1');
n.should.have.property('flag', undefined);
n.id.should.eql(t.id);
done();
});
});
});
});
it('should apply inclusion where to the target model', function(done) {
Article.create({title: 'a2'}, function (e, article) {
should.not.exist(e);
article.tagNames.create({name: 't2', flag: '2'}, function(e, t2) {
should.not.exist(e);
article.tagNames.create({name: 't3', flag: '3'}, function(e, t3) {
Article.find({
where: {id: article.id},
include: {relation: 'tagNames', scope: {where: {flag: '2'}}}},
function(e, articles) {
should.not.exist(e);
articles.should.have.property('length', 1);
var a = articles[0].toJSON();
a.should.have.property('title', 'a2');
a.should.have.property('tagNames');
a.tagNames.should.have.property('length', 1);
var n = a.tagNames[0];
n.should.have.property('name', 't2');
n.should.have.property('flag', '2');
n.id.should.eql(t2.id);
done();
});
});
});
});
});
}); });
describe('embedsOne', function () { describe('embedsOne', function () {