Merge pull request #1362 from strongloop/include-rework-cassandra

Support include rework for C* connector
This commit is contained in:
Tetsuo Seto 2017-05-15 08:09:11 -07:00 committed by GitHub
commit 21cd515d4d
6 changed files with 563 additions and 102 deletions

View File

@ -251,7 +251,7 @@ Inclusion.include = function(objects, include, options, cb) {
newFilter.where[w] = filter.where[w];
}
}
newFilter.where[fkName] = {
newFilter.where[fkName] = foreignKeys.length === 1 ? foreignKeys[0] : {
inq: foreignKeys,
};
model.find(newFilter, options, function(err, results) {

View File

@ -4,6 +4,7 @@
// License text available at https://opensource.org/licenses/MIT
'use strict';
var _ = require('lodash');
var i8n = require('inflection');
var utils = require('./utils');
var defineCachedRelations = utils.defineCachedRelations;
@ -112,13 +113,20 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres
var relatedModel = targetModel.relations[filter.relation].modelTo;
var IdKey = idName(relatedModel);
var smartMerge = function(idCond, qWhere, idKey) {
var merged = {};
var idsA = idCond[idKey].inq;
var idsB = qWhere[idKey].inq ? qWhere[idKey].inq : [qWhere[idKey]];
var intersect = _.intersectionWith(idsA, idsB, _.isEqual);
if (intersect.length === 1) merged[idKey] = intersect[0];
if (intersect.length > 1) merged[idKey] = {inq: intersect};
return merged;
};
// Merge queryRelated filter and targetId filter
var buildWhere = function() {
var IdKeyCondition = {};
IdKeyCondition[IdKey] = collectTargetIds(data, IdKey);
var mergedWhere = {
and: [IdKeyCondition, queryRelated.where],
};
var mergedWhere = smartMerge(IdKeyCondition, queryRelated.where, IdKey);
return mergedWhere;
};
if (queryRelated.where !== undefined) {

View File

@ -63,7 +63,8 @@ function setScopeValuesFromWhere(data, where, targetModel) {
if (prop) {
var val = where[i];
if (typeof val !== 'object' || val instanceof prop.type ||
prop.type.name === 'ObjectID') { // MongoDB key
prop.type.name === 'ObjectID' || // MongoDB key
prop.type.name === 'uuidFromString') { // C*
// Only pick the {propertyName: propertyValue}
data[i] = where[i];
}

View File

@ -44,6 +44,7 @@
"debug": "^2.1.1",
"depd": "^1.0.0",
"inflection": "^1.6.0",
"lodash": "^4.17.4",
"loopback-connector": "^4.0.0",
"minimatch": "^3.0.3",
"qs": "^6.3.0",

View File

@ -5,15 +5,21 @@
'use strict';
/* global getSchema:false */
var should = require('./init.js');
var async = require('async');
/* global getSchema:false, connectorCapabilities:false */
var assert = require('assert');
var async = require('async');
var bdd = require('./helpers/bdd-if');
var should = require('./init.js');
var DataSource = require('../').DataSource;
var db, User, Profile, AccessToken, Post, Passport, City, Street, Building, Assembly, Part;
var knownUsers = ['User A', 'User B', 'User C', 'User D', 'User E'];
var knownPassports = ['1', '2', '3', '4'];
var knownPosts = ['Post A', 'Post B', 'Post C', 'Post D', 'Post E'];
var knownProfiles = ['Profile A', 'Profile B', 'Profile Z'];
describe('include', function() {
before(setup);
@ -53,7 +59,8 @@ describe('include', function() {
u.__cachedRelations.should.have.property('posts');
u.__cachedRelations.posts.forEach(function(p) {
p.userId.should.eql(u.id);
// FIXME There are cases that p.userId is string
p.userId.toString().should.eql(u.id.toString());
});
});
done();
@ -137,7 +144,8 @@ describe('include', function() {
user.should.have.property('posts');
user.toJSON().should.have.property('posts').and.be.an.Array;
user.__cachedRelations.posts.forEach(function(pp) {
pp.userId.should.eql(user.id);
// FIXME There are cases that pp.userId is string
pp.userId.toString().should.eql(user.id.toString());
});
}
});
@ -172,8 +180,16 @@ describe('include', function() {
should.not.exist(err);
should.exist(passports);
passports.length.should.be.ok;
var posts = passports[0].owner().posts();
posts.should.have.length(3);
var posts;
if (connectorCapabilities.adhocSort !== false) {
posts = passports[0].owner().posts();
posts.should.have.length(3);
} else {
if (passports[0].owner()) {
posts = passports[0].owner().posts();
posts.length.should.be.belowOrEqual(3);
}
}
done();
});
});
@ -196,7 +212,8 @@ describe('include', function() {
user.__cachedRelations.should.have.property('posts');
user.__cachedRelations.posts.forEach(function(pp) {
pp.should.have.property('id');
pp.userId.should.eql(user.id);
// FIXME There are cases that pp.userId is string
pp.userId.toString().should.eql(user.id.toString());
pp.should.have.property('author');
pp.__cachedRelations.should.have.property('author');
var author = pp.__cachedRelations.author;
@ -217,33 +234,56 @@ describe('include', function() {
}, function(err, passports) {
should.not.exist(err);
should.exist(passports);
passports.length.should.equal(4);
var passport, owner, posts;
if (connectorCapabilities.adhocSort !== false) {
passports.length.should.equal(4);
var passport = passports[0];
passport.number.should.equal('1');
passport.owner().name.should.equal('User A');
var owner = passport.owner().toObject();
passport = passports[0];
passport.number.should.equal('1');
passport.owner().name.should.equal('User A');
owner = passport.owner().toObject();
var posts = passport.owner().posts();
posts.should.be.an.array;
posts.should.have.length(3);
posts = passport.owner().posts();
posts.should.be.an.array;
posts.should.have.length(3);
posts[0].title.should.equal('Post C');
posts[0].should.have.property('id', undefined); // omitted
posts[0].author().should.be.instanceOf(User);
posts[0].author().name.should.equal('User A');
posts[0].title.should.equal('Post C');
posts[0].should.have.property('id', undefined); // omitted
posts[0].author().should.be.instanceOf(User);
posts[0].author().name.should.equal('User A');
posts[1].title.should.equal('Post B');
posts[1].author().name.should.equal('User A');
posts[1].title.should.equal('Post B');
posts[1].author().name.should.equal('User A');
posts[2].title.should.equal('Post A');
posts[2].author().name.should.equal('User A');
posts[2].title.should.equal('Post A');
posts[2].author().name.should.equal('User A');
} else {
passports.length.should.be.belowOrEqual(4);
passport = passports[0];
passport.number.should.be.oneOf(knownPassports);
if (passport.owner()) {
passport.owner().name.should.be.oneOf(knownUsers);
owner = passport.owner().toObject();
posts = passport.owner().posts();
posts.should.be.an.array;
posts.length.should.be.belowOrEqual(3);
if (posts[0]) {
posts[0].title.should.be.oneOf(knownPosts);
posts[0].author().should.be.instanceOf(User);
posts[0].author().name.should.be.oneOf(knownUsers);
}
}
}
done();
});
});
it('should support limit', function(done) {
bdd.itIf(connectorCapabilities.adhocSort !== false,
'should support limit', function(done) {
Passport.find({
include: {
owner: {
@ -257,7 +297,6 @@ describe('include', function() {
limit: 2,
}, function(err, passports) {
if (err) return done(err);
passports.length.should.equal(2);
var posts1 = passports[0].toJSON().owner.posts;
posts1.length.should.equal(1);
@ -270,7 +309,44 @@ describe('include', function() {
});
});
describe('inq limit', function() {
bdd.itIf(connectorCapabilities.adhocSort === false,
'should support limit - no sort', function(done) {
Passport.find({
include: {
owner: {
relation: 'posts', scope: {
fields: ['title'], include: ['author'],
order: 'title DESC',
limit: 1,
},
},
},
limit: 2,
}, function(err, passports) {
if (err) return done(err);
passports.length.should.equal(2);
var owner = passports[0].toJSON().owner;
if (owner) {
var posts1 = owner.posts;
posts1.length.should.belowOrEqual(1);
if (posts1.length === 1) {
posts1[0].title.should.be.oneOf(knownPosts);
}
}
owner = passports[1].toJSON().owner;
if (owner) {
var posts2 = owner.posts;
posts2.length.should.belowOrEqual(1);
if (posts2.length === 1) {
posts2[0].title.should.be.oneOf(knownPosts);
}
}
done();
});
});
bdd.describeIf(connectorCapabilities.adhocSort !== false,
'inq limit', function() {
before(function() {
Passport.dataSource.settings.inqLimit = 2;
});
@ -311,7 +387,8 @@ describe('include', function() {
});
});
describe('findWithForeignKeysByPage', function() {
bdd.describeIf(connectorCapabilities.adhocSort !== false,
'findWithForeignKeysByPage', function() {
context('filter', function() {
it('works when using a `where` with a foreign key', function(done) {
User.findOne({
@ -339,7 +416,8 @@ describe('include', function() {
where: {
and: [
{id: createdPosts[0].id},
{userId: createdPosts[0].userId},
// Remove the duplicate userId to avoid Cassandra failure
// {userId: createdPosts[0].userId},
{title: 'Post A'},
],
},
@ -577,7 +655,350 @@ describe('include', function() {
});
});
it('should fetch Users with include scope on Posts - belongsTo',
bdd.describeIf(connectorCapabilities.adhocSort === false,
'findWithForeignKeysByPage', function() {
context('filter', function() {
it('works when using a `where` with a foreign key', function(done) {
User.findOne({
include: {
relation: 'passports',
},
}, function(err, user) {
if (err) return done(err);
var passport = user.passports()[0];
if (passport) {
var knownPassportIds = [];
var knownOwnerIds = [];
createdPassports.forEach(function(p) {
if (p.id) knownPassportIds.push(p.id);
if (p.ownerId) knownOwnerIds.push(p.ownerId.toString());
});
passport.id.should.be.oneOf(knownPassportIds);
// FIXME passport.ownerId may be string
passport.ownerId.toString().should.be.oneOf(knownOwnerIds);
passport.number.should.be.oneOf(knownPassports);
}
done();
});
});
it('works when using a `where` with `and`', function(done) {
User.findOne({
include: {
relation: 'posts',
scope: {
where: {
and: [
{id: createdPosts[0].id},
// Remove the duplicate userId to avoid Cassandra failure
// {userId: createdPosts[0].userId},
{title: createdPosts[0].title},
],
},
},
},
}, function(err, user) {
if (err) return done(err);
var posts, post;
if (connectorCapabilities.adhocSort !== false) {
user.name.should.equal('User A');
user.age.should.equal(21);
user.id.should.eql(createdUsers[0].id);
posts = user.posts();
posts.length.should.equal(1);
post = posts[0];
post.title.should.equal('Post A');
// eql instead of equal because mongo uses object id type
post.userId.should.eql(createdPosts[0].userId);
post.id.should.eql(createdPosts[0].id);
} else {
user.name.should.be.oneOf(knownUsers);
var knownUserIds = [];
createdUsers.forEach(function(u) {
knownUserIds.push(u.id.toString());
});
user.id.toString().should.be.oneOf(knownUserIds);
posts = user.posts();
if (posts && posts.length > 0) {
post = posts[0];
post.title.should.be.oneOf(knownPosts);
post.userId.toString().should.be.oneOf(knownUserIds);
var knownPostIds = [];
createdPosts.forEach(function(p) {
knownPostIds.push(p.id);
});
post.id.should.be.oneOf(knownPostIds);
}
}
done();
});
});
it('works when using `where` with `limit`', function(done) {
User.findOne({
include: {
relation: 'posts',
scope: {
limit: 1,
},
},
}, function(err, user) {
if (err) return done(err);
user.posts().length.should.belowOrEqual(1);
done();
});
});
it('works when using `where` with `skip`', function(done) {
User.findOne({
include: {
relation: 'posts',
scope: {
skip: 1, // will be ignored
},
},
}, function(err, user) {
if (err) return done(err);
var ids = user.posts().map(function(p) { return p.id; });
if (ids.length > 0) {
var knownPosts = [];
createdPosts.forEach(function(p) {
if (p.id) knownPosts.push(p.id);
});
ids.forEach(function(id) {
if (id) id.should.be.oneOf(knownPosts);
});
}
done();
});
});
it('works when using `where` with `offset`', function(done) {
User.findOne({
include: {
relation: 'posts',
scope: {
offset: 1, // will be ignored
},
},
}, function(err, user) {
if (err) return done(err);
var ids = user.posts().map(function(p) { return p.id; });
if (ids.length > 0) {
var knownPosts = [];
createdPosts.forEach(function(p) {
if (p.id) knownPosts.push(p.id);
});
ids.forEach(function(id) {
if (id) id.should.be.oneOf(knownPosts);
});
}
done();
});
});
it('works when using `where` without `limit`, `skip` or `offset`',
function(done) {
User.findOne({include: {relation: 'posts'}}, function(err, user) {
if (err) return done(err);
var posts = user.posts();
var ids = posts.map(function(p) { return p.id; });
if (ids.length > 0) {
var knownPosts = [];
createdPosts.forEach(function(p) {
if (p.id) knownPosts.push(p.id);
});
ids.forEach(function(id) {
if (id) id.should.be.oneOf(knownPosts);
});
}
done();
});
});
});
context('pagination', function() {
it('works with the default page size (0) and `inqlimit` is exceeded',
function(done) {
// inqLimit modifies page size in the impl (there is no way to modify
// page size directly as it is hardcoded (once we decouple the func,
// we can use ctor injection to pass in whatever page size we want).
//
// --superkhau
Post.dataSource.settings.inqLimit = 2;
User.find({include: {relation: 'posts'}}, function(err, users) {
if (err) return done(err);
users.length.should.equal(5);
delete Post.dataSource.settings.inqLimit;
done();
});
});
it('works when page size is set to 0', function(done) {
Post.dataSource.settings.inqLimit = 0;
User.find({include: {relation: 'posts'}}, function(err, users) {
if (err) return done(err);
users.length.should.equal(5);
delete Post.dataSource.settings.inqLimit;
done();
});
});
});
context('relations', function() {
// WARNING
// The code paths for in this suite of tests were verified manually due to
// the tight coupling of the `findWithForeignKeys` in `include.js`.
//
// TODO
// Decouple the utility functions into their own modules and export each
// function individually to allow for unit testing via DI.
//
// --superkhau
it('works when hasOne is called', function(done) {
User.findOne({include: {relation: 'profile'}}, function(err, user) {
if (err) return done(err);
var knownUserIds = [];
var knownProfileIds = [];
createdUsers.forEach(function(u) {
// FIXME user.id below might be string, so knownUserIds should match
knownUserIds.push(u.id.toString());
});
createdProfiles.forEach(function(p) {
// knownProfileIds.push(p.id ? p.id.toString() : '');
knownProfileIds.push(p.id);
});
if (user) {
user.name.should.be.oneOf(knownUsers);
// eql instead of equal because mongo uses object id type
user.id.toString().should.be.oneOf(knownUserIds);
var profile = user.profile();
if (profile) {
profile.profileName.should.be.oneOf(knownProfiles);
// eql instead of equal because mongo uses object id type
if (profile.userId) profile.userId.toString().should.be.oneOf(knownUserIds);
profile.id.should.be.oneOf(knownProfileIds);
}
}
done();
});
});
it('works when hasMany is called', function(done) {
User.findOne({include: {relation: 'posts'}}, function(err, user) {
if (err) return done();
var knownUserIds = [];
createdUsers.forEach(function(u) {
knownUserIds.push(u.id);
});
user.name.should.be.oneOf(knownUsers);
// eql instead of equal because mongo uses object id type
user.id.should.be.oneOf(knownUserIds);
user.posts().length.should.be.belowOrEqual(3);
done();
});
});
it('works when hasManyThrough is called', function(done) {
var Physician = db.define('Physician', {name: String});
var Patient = db.define('Patient', {name: String});
var Appointment = db.define('Appointment', {
date: {
type: Date,
default: function() {
return new Date();
},
},
});
var Address = db.define('Address', {name: String});
Physician.hasMany(Patient, {through: Appointment});
Patient.hasMany(Physician, {through: Appointment});
Patient.belongsTo(Address);
Appointment.belongsTo(Patient);
Appointment.belongsTo(Physician);
db.automigrate(['Physician', 'Patient', 'Appointment', 'Address'],
function() {
Physician.create(function(err, physician) {
physician.patients.create({name: 'a'}, function(err, patient) {
Address.create({name: 'z'}, function(err, address) {
patient.address(address);
patient.save(function() {
physician.patients({include: 'address'},
function(err, patients) {
if (err) return done(err);
patients.should.have.length(1);
var p = patients[0];
p.name.should.equal('a');
p.addressId.should.eql(patient.addressId);
p.address().id.should.eql(address.id);
p.address().name.should.equal('z');
done();
});
});
});
});
});
});
});
it('works when belongsTo is called', function(done) {
Profile.findOne({include: 'user'}, function(err, profile) {
if (err) return done(err);
if (!profile) return done(); // not every user has progile
var knownUserIds = [];
var knownProfileIds = [];
createdUsers.forEach(function(u) {
knownUserIds.push(u.id.toString());
});
createdProfiles.forEach(function(p) {
if (p.id) knownProfileIds.push(p.id.toString());
});
if (profile) {
profile.profileName.should.be.oneOf(knownProfiles);
if (profile.userId) profile.userId.toString().should.be.oneOf(knownUserIds);
if (profile.id) profile.id.toString().should.be.oneOf(knownProfileIds);
var user = profile.user();
if (user) {
user.name.should.be.oneOf(knownUsers);
user.id.toString().should.be.oneOf(knownUserIds);
}
}
done();
});
});
});
});
bdd.itIf(connectorCapabilities.adhocSort !== false,
'should fetch Users with include scope on Posts - belongsTo',
function(done) {
Post.find({include: {relation: 'author', scope: {fields: ['name']}}},
function(err, posts) {
@ -594,6 +1015,26 @@ describe('include', function() {
});
});
bdd.itIf(connectorCapabilities.adhocSort === false,
'should fetch Users with include scope on Posts - belongsTo - no sort',
function(done) {
Post.find({include: {relation: 'author', scope: {fields: ['name']}}},
function(err, posts) {
should.not.exist(err);
should.exist(posts);
posts.length.should.be.belowOrEqual(5);
var author = posts[0].author();
if (author) {
author.name.should.be.oneOf('User A', 'User B', 'User C', 'User D', 'User E');
author.should.have.property('id');
author.should.have.property('age', undefined);
}
done();
});
});
it('should fetch Users with include scope on Posts - hasMany', function(done) {
User.find({
include: {relation: 'posts', scope: {
@ -604,42 +1045,35 @@ describe('include', function() {
should.exist(users);
users.length.should.equal(5);
users[0].name.should.equal('User A');
users[1].name.should.equal('User B');
if (connectorCapabilities.adhocSort !== false) {
users[0].name.should.equal('User A');
users[1].name.should.equal('User B');
var posts = users[0].posts();
posts.should.be.an.array;
posts.should.have.length(3);
var posts = users[0].posts();
posts.should.be.an.array;
posts.should.have.length(3);
posts[0].title.should.equal('Post C');
posts[1].title.should.equal('Post B');
posts[2].title.should.equal('Post A');
posts[0].title.should.equal('Post C');
posts[1].title.should.equal('Post B');
posts[2].title.should.equal('Post A');
posts = users[1].posts();
posts.should.be.an.array;
posts.should.have.length(1);
posts[0].title.should.equal('Post D');
done();
});
});
it('should fetch Users with include scope on Passports - hasMany', function(done) {
User.find({
include: {relation: 'passports', scope: {
where: {number: '2'},
}},
}, function(err, users) {
should.not.exist(err);
should.exist(users);
users.length.should.equal(5);
users[0].name.should.equal('User A');
users[0].passports().should.be.empty;
users[1].name.should.equal('User B');
var passports = users[1].passports();
passports[0].number.should.equal('2');
posts = users[1].posts();
posts.should.be.an.array;
posts.should.have.length(1);
posts[0].title.should.equal('Post D');
} else {
users.forEach(function(u) {
u.name.should.be.oneOf(knownUsers);
var posts = u.posts();
if (posts) {
posts.should.be.an.array;
posts.length.should.be.belowOrEqual(3);
posts.forEach(function(p) {
p.title.should.be.oneOf(knownPosts);
});
}
});
}
done();
});
@ -667,10 +1101,12 @@ describe('include', function() {
user.__cachedRelations.should.have.property('posts');
user.__cachedRelations.should.have.property('passports');
user.__cachedRelations.posts.forEach(function(p) {
p.userId.should.eql(user.id);
// FIXME there are cases that p.userId is string
p.userId.toString().should.eql(user.id.toString());
});
user.__cachedRelations.passports.forEach(function(pp) {
pp.ownerId.should.eql(user.id);
// FIXME there are cases that p.ownerId is string
pp.ownerId.toString().should.eql(user.id.toString());
});
});
done();
@ -705,11 +1141,13 @@ describe('include', function() {
user.__cachedRelations.should.have.property('posts');
user.__cachedRelations.should.have.property('passports');
user.__cachedRelations.posts.forEach(function(p) {
p.userId.should.eql(user.id);
// FIXME there are cases that p.userId is string
p.userId.toString().should.eql(user.id.toString());
p.title.should.be.equal('Post A');
});
user.__cachedRelations.passports.forEach(function(pp) {
pp.ownerId.should.eql(user.id);
// FIXME there are cases that p.ownerId is string
pp.ownerId.toString().should.eql(user.id.toString());
});
});
done();
@ -774,7 +1212,8 @@ describe('include', function() {
userObj.should.not.have.property('__cachedRelations');
user.__cachedRelations.should.have.property('profile');
if (user.__cachedRelations.profile) {
user.__cachedRelations.profile.userId.should.eql(user.id);
// FIXME there are cases that profile.userId is string
user.__cachedRelations.profile.userId.toString().should.eql(user.id.toString());
usersWithProfile++;
}
});
@ -803,7 +1242,11 @@ describe('include', function() {
})
.then(function(users) {
var posts = users[0].posts();
posts.should.have.length(3);
if (connectorCapabilities.adhocSort !== false) {
posts.should.have.length(3);
} else {
if (posts) posts.length.should.be.belowOrEqual(3);
}
return users[0].save();
})
.then(function(updatedUser) {
@ -813,7 +1256,11 @@ describe('include', function() {
})
.then(function(user) {
var posts = user.posts();
posts.should.have.length(3);
if (connectorCapabilities.adhocSort !== false) {
posts.should.have.length(3);
} else {
if (posts) posts.length.should.be.belowOrEqual(3);
}
})
.then(done)
.catch(done);
@ -833,7 +1280,9 @@ describe('include', function() {
afterEach(function() {
db.connector.all = all;
});
it('including belongsTo should make only 2 db calls', function(done) {
var nDBCalls = connectorCapabilities.supportTwoOrMoreInq !== false ? 2 : 4;
it('including belongsTo should make only ' + nDBCalls + ' db calls', function(done) {
var self = this;
Passport.find({include: 'owner'}, function(err, passports) {
passports.length.should.be.ok;
@ -851,7 +1300,7 @@ describe('include', function() {
owner.id.should.eql(p.ownerId);
}
});
self.called.should.eql(2);
self.called.should.eql(nDBCalls);
done();
});
});
@ -878,30 +1327,31 @@ describe('include', function() {
});
}, next);
}, function(err) {
var autos = connectorCapabilities.supportTwoOrMoreInq !== false ?
['sedan', 'hatchback', 'SUV'] : ['sedan'];
var resultLength = connectorCapabilities.supportTwoOrMoreInq !== false ? 3 : 1;
var dbCalls = connectorCapabilities.supportTwoOrMoreInq !== false ? 3 : 5;
self.called = 0;
Assembly.find({
where: {
name: {
inq: ['sedan', 'hatchback', 'SUV'],
inq: autos,
},
},
include: 'parts',
}, function(err, result) {
should.not.exist(err);
should.exists(result);
result.length.should.equal(3);
result.length.should.equal(resultLength);
// Please note the order of assemblies is random
var assemblies = {};
result.forEach(function(r) {
assemblies[r.name] = r;
});
// sedan
assemblies.sedan.parts().should.have.length(3);
// hatchback
assemblies.hatchback.parts().should.have.length(2);
// SUV
assemblies.SUV.parts().should.have.length(0);
self.called.should.eql(3);
if (autos.indexOf('sedan') >= 0) assemblies.sedan.parts().should.have.length(3);
if (autos.indexOf('hatchback') >= 0) assemblies.hatchback.parts().should.have.length(2);
if (autos.indexOf('SUV') >= 0) assemblies.SUV.parts().should.have.length(0);
self.called.should.eql(dbCalls);
done();
});
});
@ -909,7 +1359,8 @@ describe('include', function() {
});
});
it('including hasMany should make only 2 db calls', function(done) {
var dbCalls = connectorCapabilities.supportTwoOrMoreInq !== false ? 3 : 11;
it('including hasMany should make only ' + dbCalls + ' db calls', function(done) {
var self = this;
User.find({include: ['posts', 'passports']}, function(err, users) {
should.not.exist(err);
@ -932,13 +1383,15 @@ describe('include', function() {
user.__cachedRelations.should.have.property('posts');
user.__cachedRelations.should.have.property('passports');
user.__cachedRelations.posts.forEach(function(p) {
p.userId.should.eql(user.id);
// FIXME p.userId is string in some cases.
if (p.userId) p.userId.toString().should.eql(user.id.toString());
});
user.__cachedRelations.passports.forEach(function(pp) {
pp.ownerId.should.eql(user.id);
// FIXME pp.owerId is string in some cases.
if (pp.owerId) pp.ownerId.toString().should.eql(user.id.toString());
});
});
self.called.should.eql(3);
self.called.should.eql(dbCalls);
done();
});
});
@ -969,14 +1422,16 @@ describe('include', function() {
user.__cachedRelations.should.have.property('posts');
user.__cachedRelations.should.have.property('passports');
user.__cachedRelations.posts.forEach(function(p) {
p.userId.should.eql(user.id);
// FIXME p.userId is string in some cases.
p.userId.toString().should.eql(user.id.toString());
p.title.should.be.equal('Post A');
});
user.__cachedRelations.passports.forEach(function(pp) {
pp.ownerId.should.eql(user.id);
// FIXME p.userId is string in some cases.
pp.ownerId.toString().should.eql(user.id.toString());
});
});
self.called.should.eql(3);
self.called.should.eql(dbCalls);
done();
});
});
@ -1066,6 +1521,7 @@ function setup(done) {
{name: 'User D', age: 24},
{name: 'User E', age: 25},
],
function(items) {
createdUsers = items;
createPassports();
@ -1073,7 +1529,6 @@ function setup(done) {
}
);
}
function createAccessTokens() {
clearAndCreate(
AccessToken,

View File

@ -588,8 +588,7 @@ describe('relations', function() {
db.automigrate(['Physician', 'Patient', 'Appointment', 'Address'], done);
});
bdd.itIf(connectorCapabilities.supportInclude !== false,
'should build record on scope', function(done) {
it('should build record on scope', function(done) {
Physician.create(function(err, physician) {
var patient = physician.patients.build();
patient.physicianId.should.eql(physician.id);
@ -799,8 +798,7 @@ describe('relations', function() {
});
});
context('with filter include', function() {
bdd.itIf(connectorCapabilities.supportNonPrimaryKeyIN !== false,
'returns physicians included in patient', function(done) {
it('returns physicians included in patient', function(done) {
var includeFilter = {include: 'physicians'};
physician.patients(includeFilter, function(err, ch) {
if (err) return done(err);
@ -811,8 +809,7 @@ describe('relations', function() {
});
});
context('with filter where', function() {
bdd.itIf(connectorCapabilities.supportNonPrimaryKeyIN !== false,
'returns patient where id equal to samplePatientId', function(done) {
it('returns patient where id equal to samplePatientId', function(done) {
var whereFilter = {where: {id: samplePatientId}};
physician.patients(whereFilter, function(err, ch) {
if (err) return done(err);
@ -822,8 +819,7 @@ describe('relations', function() {
done();
});
});
bdd.itIf(connectorCapabilities.supportNonPrimaryKeyIN !== false,
'returns patients where id in an array', function(done) {
it('returns patients where id in an array', function(done) {
var idArr = [];
var whereFilter;
physician.patients.create({name: 'b'}, function(err, p) {