2016-04-01 22:25:16 +00:00
|
|
|
// Copyright IBM Corp. 2013,2015. All Rights Reserved.
|
|
|
|
// Node module: loopback-datasource-juggler
|
|
|
|
// This file is licensed under the MIT License.
|
|
|
|
// License text available at https://opensource.org/licenses/MIT
|
|
|
|
|
2016-08-22 19:55:22 +00:00
|
|
|
'use strict';
|
|
|
|
|
2016-12-05 14:14:09 +00:00
|
|
|
/* global getSchema:false */
|
2013-04-06 10:57:12 +00:00
|
|
|
var should = require('./init.js');
|
2015-05-13 18:49:43 +00:00
|
|
|
var async = require('async');
|
2015-05-22 18:39:37 +00:00
|
|
|
var assert = require('assert');
|
|
|
|
|
|
|
|
var DataSource = require('../').DataSource;
|
2013-04-06 10:57:12 +00:00
|
|
|
|
2015-05-13 18:49:43 +00:00
|
|
|
var db, User, Profile, AccessToken, Post, Passport, City, Street, Building, Assembly, Part;
|
2013-03-27 00:46:59 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
describe('include', function() {
|
2014-01-24 17:09:53 +00:00
|
|
|
|
|
|
|
before(setup);
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('should fetch belongsTo relation', function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
Passport.find({include: 'owner'}, function(err, passports) {
|
2014-01-24 17:09:53 +00:00
|
|
|
passports.length.should.be.ok;
|
2016-04-01 11:48:17 +00:00
|
|
|
passports.forEach(function(p) {
|
2014-01-24 17:09:53 +00:00
|
|
|
p.__cachedRelations.should.have.property('owner');
|
2014-01-28 00:04:37 +00:00
|
|
|
|
|
|
|
// The relation should be promoted as the 'owner' property
|
|
|
|
p.should.have.property('owner');
|
|
|
|
// The __cachedRelations should be removed from json output
|
|
|
|
p.toJSON().should.not.have.property('__cachedRelations');
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var owner = p.__cachedRelations.owner;
|
|
|
|
if (!p.ownerId) {
|
|
|
|
should.not.exist(owner);
|
|
|
|
} else {
|
|
|
|
should.exist(owner);
|
2015-02-08 19:14:51 +00:00
|
|
|
owner.id.should.eql(p.ownerId);
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
done();
|
2013-03-27 00:46:59 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('should fetch hasMany relation', function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
User.find({include: 'posts'}, function(err, users) {
|
2014-01-24 17:09:53 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(users);
|
|
|
|
users.length.should.be.ok;
|
2016-04-01 11:48:17 +00:00
|
|
|
users.forEach(function(u) {
|
2014-01-28 00:04:37 +00:00
|
|
|
// The relation should be promoted as the 'owner' property
|
|
|
|
u.should.have.property('posts');
|
|
|
|
// The __cachedRelations should be removed from json output
|
|
|
|
u.toJSON().should.not.have.property('__cachedRelations');
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
u.__cachedRelations.should.have.property('posts');
|
2016-04-01 11:48:17 +00:00
|
|
|
u.__cachedRelations.posts.forEach(function(p) {
|
2015-02-08 19:14:51 +00:00
|
|
|
p.userId.should.eql(u.id);
|
2013-03-27 00:46:59 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
|
|
|
done();
|
2013-03-27 00:46:59 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('should fetch Passport - Owner - Posts', function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
Passport.find({include: {owner: 'posts'}}, function(err, passports) {
|
2015-08-24 13:07:43 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(passports);
|
|
|
|
passports.length.should.be.ok;
|
2016-04-01 11:48:17 +00:00
|
|
|
passports.forEach(function(p) {
|
2014-01-24 17:09:53 +00:00
|
|
|
p.__cachedRelations.should.have.property('owner');
|
2014-01-28 00:04:37 +00:00
|
|
|
|
|
|
|
// The relation should be promoted as the 'owner' property
|
|
|
|
p.should.have.property('owner');
|
|
|
|
// The __cachedRelations should be removed from json output
|
|
|
|
p.toJSON().should.not.have.property('__cachedRelations');
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var user = p.__cachedRelations.owner;
|
|
|
|
if (!p.ownerId) {
|
|
|
|
should.not.exist(user);
|
|
|
|
} else {
|
|
|
|
should.exist(user);
|
2015-02-08 19:14:51 +00:00
|
|
|
user.id.should.eql(p.ownerId);
|
2014-01-24 17:09:53 +00:00
|
|
|
user.__cachedRelations.should.have.property('posts');
|
2014-03-24 04:07:04 +00:00
|
|
|
user.should.have.property('posts');
|
2015-07-31 21:47:22 +00:00
|
|
|
user.toJSON().should.have.property('posts').and.be.an.Array;
|
2016-04-01 11:48:17 +00:00
|
|
|
user.__cachedRelations.posts.forEach(function(pp) {
|
2015-02-08 19:14:51 +00:00
|
|
|
pp.userId.should.eql(user.id);
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
done();
|
2013-03-27 00:46:59 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('should fetch Passport - Owner - empty Posts', function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
Passport.findOne({where: {number: '4'}, include: {owner: 'posts'}}, function(err, passport) {
|
2015-07-31 21:47:22 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(passport);
|
|
|
|
passport.__cachedRelations.should.have.property('owner');
|
|
|
|
|
|
|
|
// The relation should be promoted as the 'owner' property
|
|
|
|
passport.should.have.property('owner');
|
|
|
|
// The __cachedRelations should be removed from json output
|
|
|
|
passport.toJSON().should.not.have.property('__cachedRelations');
|
|
|
|
|
|
|
|
var user = passport.__cachedRelations.owner;
|
|
|
|
should.exist(user);
|
|
|
|
user.id.should.eql(passport.ownerId);
|
|
|
|
user.__cachedRelations.should.have.property('posts');
|
|
|
|
user.should.have.property('posts');
|
2015-12-22 23:03:50 +00:00
|
|
|
user.toJSON().should.have.property('posts').and.be.an.Array().with
|
|
|
|
.length(0);
|
2015-07-31 21:47:22 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('should fetch Passport - Owner - Posts - alternate syntax', function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
Passport.find({include: {owner: {relation: 'posts'}}}, function(err, passports) {
|
2014-10-10 15:17:36 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(passports);
|
|
|
|
passports.length.should.be.ok;
|
|
|
|
var posts = passports[0].owner().posts();
|
|
|
|
posts.should.have.length(3);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('should fetch Passports - User - Posts - User', function(done) {
|
2014-03-13 23:43:38 +00:00
|
|
|
Passport.find({
|
2016-08-19 17:46:59 +00:00
|
|
|
include: {owner: {posts: 'author'}},
|
2016-04-01 11:48:17 +00:00
|
|
|
}, function(err, passports) {
|
2014-01-24 17:09:53 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(passports);
|
|
|
|
passports.length.should.be.ok;
|
2016-04-01 11:48:17 +00:00
|
|
|
passports.forEach(function(p) {
|
2014-01-24 17:09:53 +00:00
|
|
|
p.__cachedRelations.should.have.property('owner');
|
|
|
|
var user = p.__cachedRelations.owner;
|
|
|
|
if (!p.ownerId) {
|
|
|
|
should.not.exist(user);
|
|
|
|
} else {
|
|
|
|
should.exist(user);
|
2015-02-08 19:14:51 +00:00
|
|
|
user.id.should.eql(p.ownerId);
|
2014-01-24 17:09:53 +00:00
|
|
|
user.__cachedRelations.should.have.property('posts');
|
2016-04-01 11:48:17 +00:00
|
|
|
user.__cachedRelations.posts.forEach(function(pp) {
|
2014-10-10 10:28:39 +00:00
|
|
|
pp.should.have.property('id');
|
2015-02-08 19:14:51 +00:00
|
|
|
pp.userId.should.eql(user.id);
|
2014-03-24 04:07:04 +00:00
|
|
|
pp.should.have.property('author');
|
2014-01-24 17:09:53 +00:00
|
|
|
pp.__cachedRelations.should.have.property('author');
|
|
|
|
var author = pp.__cachedRelations.author;
|
2015-02-08 19:14:51 +00:00
|
|
|
author.id.should.eql(user.id);
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
done();
|
2013-03-27 00:46:59 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('should fetch Passports with include scope on Posts', function(done) {
|
2014-10-10 10:28:39 +00:00
|
|
|
Passport.find({
|
2016-08-19 17:46:59 +00:00
|
|
|
include: {owner: {relation: 'posts', scope: {
|
2014-10-10 10:28:39 +00:00
|
|
|
fields: ['title'], include: ['author'],
|
2016-04-01 11:48:17 +00:00
|
|
|
order: 'title DESC',
|
|
|
|
}}},
|
|
|
|
}, function(err, passports) {
|
2014-10-10 10:28:39 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(passports);
|
2015-07-31 21:47:22 +00:00
|
|
|
passports.length.should.equal(4);
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-10 10:28:39 +00:00
|
|
|
var passport = passports[0];
|
|
|
|
passport.number.should.equal('1');
|
|
|
|
passport.owner().name.should.equal('User A');
|
|
|
|
var owner = passport.owner().toObject();
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-10 10:28:39 +00:00
|
|
|
var posts = passport.owner().posts();
|
|
|
|
posts.should.be.an.array;
|
|
|
|
posts.should.have.length(3);
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-10 10:28:39 +00:00
|
|
|
posts[0].title.should.equal('Post C');
|
2015-02-03 10:37:43 +00:00
|
|
|
posts[0].should.have.property('id', undefined); // omitted
|
2014-10-10 10:28:39 +00:00
|
|
|
posts[0].author().should.be.instanceOf(User);
|
|
|
|
posts[0].author().name.should.equal('User A');
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-10 10:28:39 +00:00
|
|
|
posts[1].title.should.equal('Post B');
|
|
|
|
posts[1].author().name.should.equal('User A');
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-10 10:28:39 +00:00
|
|
|
posts[2].title.should.equal('Post A');
|
|
|
|
posts[2].author().name.should.equal('User A');
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-10 10:28:39 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2015-05-29 17:50:37 +00:00
|
|
|
it('should support limit', function(done) {
|
|
|
|
Passport.find({
|
|
|
|
include: {
|
|
|
|
owner: {
|
|
|
|
relation: 'posts', scope: {
|
|
|
|
fields: ['title'], include: ['author'],
|
|
|
|
order: 'title DESC',
|
2015-12-12 00:59:41 +00:00
|
|
|
limit: 1,
|
2016-04-01 11:48:17 +00:00
|
|
|
},
|
|
|
|
},
|
2015-05-29 17:50:37 +00:00
|
|
|
},
|
2015-12-12 00:59:41 +00:00
|
|
|
limit: 2,
|
2015-05-29 17:50:37 +00:00
|
|
|
}, function(err, passports) {
|
|
|
|
if (err) return done(err);
|
2015-12-12 00:59:41 +00:00
|
|
|
|
|
|
|
passports.length.should.equal(2);
|
|
|
|
var posts1 = passports[0].toJSON().owner.posts;
|
|
|
|
posts1.length.should.equal(1);
|
|
|
|
posts1[0].title.should.equal('Post C');
|
|
|
|
var posts2 = passports[1].toJSON().owner.posts;
|
|
|
|
posts2.length.should.equal(1);
|
|
|
|
posts2[0].title.should.equal('Post D');
|
|
|
|
|
2015-05-29 17:50:37 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2015-12-12 00:59:41 +00:00
|
|
|
describe('inq limit', function() {
|
|
|
|
before(function() {
|
|
|
|
Passport.dataSource.settings.inqLimit = 2;
|
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2015-12-12 00:59:41 +00:00
|
|
|
after(function() {
|
|
|
|
delete Passport.dataSource.settings.inqLimit;
|
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2015-12-12 00:59:41 +00:00
|
|
|
it('should support include by pagination', function(done) {
|
|
|
|
// `pagination` in this case is inside the implementation and set by
|
|
|
|
// `inqLimit = 2` in the before block. This will need to be reworked once
|
|
|
|
// we decouple `findWithForeignKeysByPage`.
|
|
|
|
//
|
|
|
|
// --superkhau
|
|
|
|
Passport.find({
|
|
|
|
include: {
|
|
|
|
owner: {
|
|
|
|
relation: 'posts',
|
|
|
|
scope: {
|
|
|
|
fields: ['title'], include: ['author'],
|
|
|
|
order: 'title ASC',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, function(err, passports) {
|
|
|
|
if (err) return done(err);
|
|
|
|
|
|
|
|
passports.length.should.equal(4);
|
|
|
|
var posts1 = passports[0].toJSON().owner.posts;
|
|
|
|
posts1.length.should.equal(3);
|
|
|
|
posts1[0].title.should.equal('Post A');
|
|
|
|
var posts2 = passports[1].toJSON().owner.posts;
|
|
|
|
posts2.length.should.equal(1);
|
|
|
|
posts2[0].title.should.equal('Post D');
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('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];
|
2016-09-08 20:05:48 +00:00
|
|
|
// eql instead of equal because mongo uses object id type
|
|
|
|
passport.id.should.eql(createdPassports[0].id);
|
|
|
|
passport.ownerId.should.eql(createdPassports[0].ownerId);
|
|
|
|
passport.number.should.eql(createdPassports[0].number);
|
2015-12-12 00:59:41 +00:00
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('works when using a `where` with `and`', function(done) {
|
|
|
|
User.findOne({
|
|
|
|
include: {
|
|
|
|
relation: 'posts',
|
|
|
|
scope: {
|
|
|
|
where: {
|
|
|
|
and: [
|
2016-09-08 20:05:48 +00:00
|
|
|
{id: createdPosts[0].id},
|
|
|
|
{userId: createdPosts[0].userId},
|
2016-08-19 17:46:59 +00:00
|
|
|
{title: 'Post A'},
|
2015-12-12 00:59:41 +00:00
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, function(err, user) {
|
|
|
|
if (err) return done(err);
|
|
|
|
|
|
|
|
user.name.should.equal('User A');
|
|
|
|
user.age.should.equal(21);
|
2016-09-08 20:05:48 +00:00
|
|
|
user.id.should.eql(createdUsers[0].id);
|
2015-12-12 00:59:41 +00:00
|
|
|
var posts = user.posts();
|
|
|
|
posts.length.should.equal(1);
|
|
|
|
var post = posts[0];
|
|
|
|
post.title.should.equal('Post A');
|
2016-09-08 20:05:48 +00:00
|
|
|
// eql instead of equal because mongo uses object id type
|
|
|
|
post.userId.should.eql(createdPosts[0].userId);
|
|
|
|
post.id.should.eql(createdPosts[0].id);
|
2015-12-12 00:59:41 +00:00
|
|
|
|
|
|
|
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.equal(1);
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('works when using `where` with `skip`', function(done) {
|
|
|
|
User.findOne({
|
|
|
|
include: {
|
|
|
|
relation: 'posts',
|
|
|
|
scope: {
|
|
|
|
skip: 1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, function(err, user) {
|
|
|
|
if (err) return done(err);
|
|
|
|
|
|
|
|
var ids = user.posts().map(function(p) { return p.id; });
|
2016-09-08 20:05:48 +00:00
|
|
|
ids.should.eql([createdPosts[1].id, createdPosts[2].id]);
|
2015-12-12 00:59:41 +00:00
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('works when using `where` with `offset`', function(done) {
|
|
|
|
User.findOne({
|
|
|
|
include: {
|
|
|
|
relation: 'posts',
|
|
|
|
scope: {
|
|
|
|
offset: 1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, function(err, user) {
|
|
|
|
if (err) return done(err);
|
|
|
|
|
|
|
|
var ids = user.posts().map(function(p) { return p.id; });
|
2016-09-08 20:05:48 +00:00
|
|
|
ids.should.eql([createdPosts[1].id, createdPosts[2].id]);
|
2015-12-12 00:59:41 +00:00
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('works when using `where` without `limit`, `skip` or `offset`',
|
|
|
|
function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
User.findOne({include: {relation: 'posts'}}, function(err, user) {
|
2015-12-12 00:59:41 +00:00
|
|
|
if (err) return done(err);
|
|
|
|
|
|
|
|
var posts = user.posts();
|
|
|
|
var ids = posts.map(function(p) { return p.id; });
|
2016-09-08 20:05:48 +00:00
|
|
|
ids.should.eql([
|
|
|
|
createdPosts[0].id,
|
|
|
|
createdPosts[1].id,
|
|
|
|
createdPosts[2].id,
|
|
|
|
]);
|
2015-12-12 00:59:41 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
User.find({include: {relation: 'posts'}}, function(err, users) {
|
2015-12-12 00:59:41 +00:00
|
|
|
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;
|
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
User.find({include: {relation: 'posts'}}, function(err, users) {
|
2015-12-12 00:59:41 +00:00
|
|
|
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) {
|
2016-08-19 17:46:59 +00:00
|
|
|
User.findOne({include: {relation: 'profile'}}, function(err, user) {
|
2015-12-12 00:59:41 +00:00
|
|
|
if (err) return done(err);
|
|
|
|
|
|
|
|
user.name.should.equal('User A');
|
|
|
|
user.age.should.equal(21);
|
2016-09-08 20:05:48 +00:00
|
|
|
// eql instead of equal because mongo uses object id type
|
|
|
|
user.id.should.eql(createdUsers[0].id);
|
2016-08-22 19:55:22 +00:00
|
|
|
var profile = user.profile();
|
2015-12-12 00:59:41 +00:00
|
|
|
profile.profileName.should.equal('Profile A');
|
2016-09-08 20:05:48 +00:00
|
|
|
// eql instead of equal because mongo uses object id type
|
|
|
|
profile.userId.should.eql(createdProfiles[0].userId);
|
|
|
|
profile.id.should.eql(createdProfiles[0].id);
|
2015-12-12 00:59:41 +00:00
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('works when hasMany is called', function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
User.findOne({include: {relation: 'posts'}}, function(err, user) {
|
2015-12-12 00:59:41 +00:00
|
|
|
if (err) return done();
|
|
|
|
|
|
|
|
user.name.should.equal('User A');
|
|
|
|
user.age.should.equal(21);
|
2016-09-08 20:05:48 +00:00
|
|
|
// eql instead of equal because mongo uses object id type
|
|
|
|
user.id.should.eql(createdUsers[0].id);
|
2015-12-12 00:59:41 +00:00
|
|
|
user.posts().length.should.equal(3);
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('works when hasManyThrough is called', function(done) {
|
2016-08-22 19:55:22 +00:00
|
|
|
var Physician = db.define('Physician', {name: String});
|
|
|
|
var Patient = db.define('Patient', {name: String});
|
|
|
|
var Appointment = db.define('Appointment', {
|
2015-12-12 00:59:41 +00:00
|
|
|
date: {
|
|
|
|
type: Date,
|
|
|
|
default: function() {
|
|
|
|
return new Date();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
2016-08-22 19:55:22 +00:00
|
|
|
var Address = db.define('Address', {name: String});
|
2015-12-12 00:59:41 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
Physician.hasMany(Patient, {through: Appointment});
|
|
|
|
Patient.hasMany(Physician, {through: Appointment});
|
2015-12-12 00:59:41 +00:00
|
|
|
Patient.belongsTo(Address);
|
|
|
|
Appointment.belongsTo(Patient);
|
|
|
|
Appointment.belongsTo(Physician);
|
|
|
|
|
|
|
|
db.automigrate(['Physician', 'Patient', 'Appointment', 'Address'],
|
|
|
|
function() {
|
|
|
|
Physician.create(function(err, physician) {
|
2016-08-19 17:46:59 +00:00
|
|
|
physician.patients.create({name: 'a'}, function(err, patient) {
|
|
|
|
Address.create({name: 'z'}, function(err, address) {
|
2015-12-12 00:59:41 +00:00
|
|
|
patient.address(address);
|
|
|
|
patient.save(function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
physician.patients({include: 'address'},
|
2016-09-08 20:05:48 +00:00
|
|
|
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();
|
|
|
|
});
|
2015-12-12 00:59:41 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('works when belongsTo is called', function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
Profile.findOne({include: 'user'}, function(err, profile) {
|
2015-12-12 00:59:41 +00:00
|
|
|
if (err) return done(err);
|
|
|
|
|
|
|
|
profile.profileName.should.equal('Profile A');
|
2016-09-08 20:05:48 +00:00
|
|
|
profile.userId.should.eql(createdProfiles[0].userId);
|
|
|
|
profile.id.should.eql(createdProfiles[0].id);
|
2016-08-22 19:55:22 +00:00
|
|
|
var user = profile.user();
|
2015-12-12 00:59:41 +00:00
|
|
|
user.name.should.equal('User A');
|
|
|
|
user.age.should.equal(21);
|
2016-09-08 20:05:48 +00:00
|
|
|
user.id.should.eql(createdUsers[0].id);
|
2015-12-12 00:59:41 +00:00
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2016-04-01 13:23:42 +00:00
|
|
|
});
|
2016-04-01 11:48:17 +00:00
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2015-12-12 00:59:41 +00:00
|
|
|
it('should fetch Users with include scope on Posts - belongsTo',
|
|
|
|
function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
Post.find({include: {relation: 'author', scope: {fields: ['name']}}},
|
2015-12-12 00:59:41 +00:00
|
|
|
function(err, posts) {
|
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(posts);
|
|
|
|
posts.length.should.equal(5);
|
|
|
|
|
|
|
|
var author = posts[0].author();
|
|
|
|
author.name.should.equal('User A');
|
|
|
|
author.should.have.property('id');
|
|
|
|
author.should.have.property('age', undefined);
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('should fetch Users with include scope on Posts - hasMany', function(done) {
|
2014-10-10 12:24:25 +00:00
|
|
|
User.find({
|
2016-08-19 17:46:59 +00:00
|
|
|
include: {relation: 'posts', scope: {
|
2016-04-01 11:48:17 +00:00
|
|
|
order: 'title DESC',
|
|
|
|
}},
|
|
|
|
}, function(err, users) {
|
2014-10-10 12:24:25 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(users);
|
|
|
|
users.length.should.equal(5);
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-10 12:24:25 +00:00
|
|
|
users[0].name.should.equal('User A');
|
|
|
|
users[1].name.should.equal('User B');
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-10 12:24:25 +00:00
|
|
|
var posts = users[0].posts();
|
|
|
|
posts.should.be.an.array;
|
|
|
|
posts.should.have.length(3);
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-10 12:24:25 +00:00
|
|
|
posts[0].title.should.equal('Post C');
|
|
|
|
posts[1].title.should.equal('Post B');
|
|
|
|
posts[2].title.should.equal('Post A');
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-10 12:24:25 +00:00
|
|
|
var posts = users[1].posts();
|
|
|
|
posts.should.be.an.array;
|
|
|
|
posts.should.have.length(1);
|
|
|
|
posts[0].title.should.equal('Post D');
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-10 12:24:25 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('should fetch Users with include scope on Passports - hasMany', function(done) {
|
2014-10-10 12:24:25 +00:00
|
|
|
User.find({
|
2016-08-19 17:46:59 +00:00
|
|
|
include: {relation: 'passports', scope: {
|
|
|
|
where: {number: '2'},
|
2016-04-01 11:48:17 +00:00
|
|
|
}},
|
|
|
|
}, function(err, users) {
|
2014-10-10 12:24:25 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(users);
|
|
|
|
users.length.should.equal(5);
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-10 12:24:25 +00:00
|
|
|
users[0].name.should.equal('User A');
|
|
|
|
users[0].passports().should.be.empty;
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-10 12:24:25 +00:00
|
|
|
users[1].name.should.equal('User B');
|
|
|
|
var passports = users[1].passports();
|
|
|
|
passports[0].number.should.equal('2');
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-10 12:24:25 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('should fetch User - Posts AND Passports', function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
User.find({include: ['posts', 'passports']}, function(err, users) {
|
2014-01-24 17:09:53 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(users);
|
|
|
|
users.length.should.be.ok;
|
2016-04-01 11:48:17 +00:00
|
|
|
users.forEach(function(user) {
|
2014-01-28 00:04:37 +00:00
|
|
|
// The relation should be promoted as the 'owner' property
|
|
|
|
user.should.have.property('posts');
|
|
|
|
user.should.have.property('passports');
|
2014-02-14 01:43:28 +00:00
|
|
|
|
|
|
|
var userObj = user.toJSON();
|
|
|
|
userObj.should.have.property('posts');
|
|
|
|
userObj.should.have.property('passports');
|
|
|
|
userObj.posts.should.be.an.instanceOf(Array);
|
|
|
|
userObj.passports.should.be.an.instanceOf(Array);
|
|
|
|
|
2014-01-28 00:04:37 +00:00
|
|
|
// The __cachedRelations should be removed from json output
|
2014-02-14 01:43:28 +00:00
|
|
|
userObj.should.not.have.property('__cachedRelations');
|
2014-01-28 00:04:37 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
user.__cachedRelations.should.have.property('posts');
|
|
|
|
user.__cachedRelations.should.have.property('passports');
|
2016-04-01 11:48:17 +00:00
|
|
|
user.__cachedRelations.posts.forEach(function(p) {
|
2015-02-08 19:14:51 +00:00
|
|
|
p.userId.should.eql(user.id);
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2016-04-01 11:48:17 +00:00
|
|
|
user.__cachedRelations.passports.forEach(function(pp) {
|
2015-02-08 19:14:51 +00:00
|
|
|
pp.ownerId.should.eql(user.id);
|
2013-03-27 00:46:59 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
|
|
|
done();
|
2013-03-27 00:46:59 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-03-27 00:46:59 +00:00
|
|
|
|
2014-11-05 04:45:25 +00:00
|
|
|
it('should fetch User - Posts AND Passports in relation syntax',
|
|
|
|
function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
User.find({include: [
|
|
|
|
{relation: 'posts', scope: {
|
|
|
|
where: {title: 'Post A'},
|
2014-11-05 04:45:25 +00:00
|
|
|
}},
|
2016-04-01 11:48:17 +00:00
|
|
|
'passports',
|
2016-08-19 17:46:59 +00:00
|
|
|
]}, function(err, users) {
|
2014-11-05 04:45:25 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(users);
|
|
|
|
users.length.should.be.ok;
|
|
|
|
users.forEach(function(user) {
|
|
|
|
// The relation should be promoted as the 'owner' property
|
|
|
|
user.should.have.property('posts');
|
|
|
|
user.should.have.property('passports');
|
|
|
|
|
|
|
|
var userObj = user.toJSON();
|
|
|
|
userObj.should.have.property('posts');
|
|
|
|
userObj.should.have.property('passports');
|
|
|
|
userObj.posts.should.be.an.instanceOf(Array);
|
|
|
|
userObj.passports.should.be.an.instanceOf(Array);
|
|
|
|
|
|
|
|
// The __cachedRelations should be removed from json output
|
|
|
|
userObj.should.not.have.property('__cachedRelations');
|
|
|
|
|
|
|
|
user.__cachedRelations.should.have.property('posts');
|
|
|
|
user.__cachedRelations.should.have.property('passports');
|
|
|
|
user.__cachedRelations.posts.forEach(function(p) {
|
2015-02-08 19:14:51 +00:00
|
|
|
p.userId.should.eql(user.id);
|
2014-11-05 04:45:25 +00:00
|
|
|
p.title.should.be.equal('Post A');
|
|
|
|
});
|
|
|
|
user.__cachedRelations.passports.forEach(function(pp) {
|
2015-02-08 19:14:51 +00:00
|
|
|
pp.ownerId.should.eql(user.id);
|
2014-11-05 04:45:25 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('should not fetch User - AccessTokens', function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
User.find({include: ['accesstokens']}, function(err, users) {
|
2014-07-21 20:39:29 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(users);
|
|
|
|
users.length.should.be.ok;
|
2016-04-01 11:48:17 +00:00
|
|
|
users.forEach(function(user) {
|
2014-07-21 20:39:29 +00:00
|
|
|
var userObj = user.toJSON();
|
|
|
|
userObj.should.not.have.property('accesstokens');
|
|
|
|
});
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('should support hasAndBelongsToMany', function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
Assembly.create({name: 'car'}, function(err, assembly) {
|
|
|
|
Part.create({partNumber: 'engine'}, function(err, part) {
|
2016-04-01 11:48:17 +00:00
|
|
|
assembly.parts.add(part, function(err, data) {
|
|
|
|
assembly.parts(function(err, parts) {
|
2014-10-14 20:47:59 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exists(parts);
|
|
|
|
parts.length.should.equal(1);
|
|
|
|
parts[0].partNumber.should.equal('engine');
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-10-14 20:47:59 +00:00
|
|
|
// Create a part
|
2016-08-19 17:46:59 +00:00
|
|
|
assembly.parts.create({partNumber: 'door'}, function(err, part4) {
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
Assembly.find({include: 'parts'}, function(err, assemblies) {
|
2014-10-14 20:47:59 +00:00
|
|
|
assemblies.length.should.equal(1);
|
|
|
|
assemblies[0].parts().length.should.equal(2);
|
|
|
|
done();
|
2014-03-13 23:43:38 +00:00
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-03-13 23:43:38 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('should fetch User - Profile (HasOne)', function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
User.find({include: ['profile']}, function(err, users) {
|
2015-05-13 18:49:43 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(users);
|
|
|
|
users.length.should.be.ok;
|
|
|
|
var usersWithProfile = 0;
|
2016-04-01 11:48:17 +00:00
|
|
|
users.forEach(function(user) {
|
2015-05-13 18:49:43 +00:00
|
|
|
// The relation should be promoted as the 'owner' property
|
|
|
|
user.should.have.property('profile');
|
|
|
|
var userObj = user.toJSON();
|
|
|
|
var profile = user.profile();
|
|
|
|
if (profile) {
|
|
|
|
profile.should.be.an.instanceOf(Profile);
|
|
|
|
usersWithProfile++;
|
2016-04-01 13:23:42 +00:00
|
|
|
} else {
|
2015-05-13 18:49:43 +00:00
|
|
|
(profile === null).should.be.true;
|
|
|
|
}
|
|
|
|
// The __cachedRelations should be removed from json output
|
|
|
|
userObj.should.not.have.property('__cachedRelations');
|
|
|
|
user.__cachedRelations.should.have.property('profile');
|
|
|
|
if (user.__cachedRelations.profile) {
|
|
|
|
user.__cachedRelations.profile.userId.should.eql(user.id);
|
|
|
|
usersWithProfile++;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
usersWithProfile.should.equal(2 * 2);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-10-14 20:47:59 +00:00
|
|
|
// Not implemented correctly, see: loopback-datasource-juggler/issues/166
|
2015-05-13 18:49:43 +00:00
|
|
|
// fixed by DB optimization
|
2016-04-01 11:48:17 +00:00
|
|
|
it('should support include scope on hasAndBelongsToMany', function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
Assembly.find({include: {relation: 'parts', scope: {
|
|
|
|
where: {partNumber: 'engine'},
|
2016-04-01 11:48:17 +00:00
|
|
|
}}}, function(err, assemblies) {
|
2015-05-13 18:49:43 +00:00
|
|
|
assemblies.length.should.equal(1);
|
|
|
|
var parts = assemblies[0].parts();
|
|
|
|
parts.should.have.length(1);
|
|
|
|
parts[0].partNumber.should.equal('engine');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-01-08 10:09:29 +00:00
|
|
|
it('should save related items separately', function(done) {
|
|
|
|
User.find({
|
2016-04-01 11:48:17 +00:00
|
|
|
include: 'posts',
|
2016-01-08 10:09:29 +00:00
|
|
|
})
|
|
|
|
.then(function(users) {
|
|
|
|
var posts = users[0].posts();
|
|
|
|
posts.should.have.length(3);
|
|
|
|
return users[0].save();
|
|
|
|
})
|
|
|
|
.then(function(updatedUser) {
|
|
|
|
return User.findById(updatedUser.id, {
|
2016-04-01 11:48:17 +00:00
|
|
|
include: 'posts',
|
2016-01-08 10:09:29 +00:00
|
|
|
});
|
|
|
|
})
|
|
|
|
.then(function(user) {
|
|
|
|
var posts = user.posts();
|
|
|
|
posts.should.have.length(3);
|
|
|
|
})
|
|
|
|
.then(done)
|
|
|
|
.catch(done);
|
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
describe('performance', function() {
|
2015-05-20 16:13:56 +00:00
|
|
|
var all;
|
|
|
|
beforeEach(function() {
|
|
|
|
this.called = 0;
|
|
|
|
var self = this;
|
|
|
|
all = db.connector.all;
|
|
|
|
db.connector.all = function(model, filter, options, cb) {
|
|
|
|
self.called++;
|
|
|
|
return all.apply(db.connector, arguments);
|
|
|
|
};
|
2015-05-13 18:49:43 +00:00
|
|
|
});
|
2015-05-20 16:13:56 +00:00
|
|
|
afterEach(function() {
|
|
|
|
db.connector.all = all;
|
2015-05-13 18:49:43 +00:00
|
|
|
});
|
2016-04-01 11:48:17 +00:00
|
|
|
it('including belongsTo should make only 2 db calls', function(done) {
|
2015-05-13 18:49:43 +00:00
|
|
|
var self = this;
|
2016-08-19 17:46:59 +00:00
|
|
|
Passport.find({include: 'owner'}, function(err, passports) {
|
2015-05-13 18:49:43 +00:00
|
|
|
passports.length.should.be.ok;
|
2016-04-01 11:48:17 +00:00
|
|
|
passports.forEach(function(p) {
|
2015-05-13 18:49:43 +00:00
|
|
|
p.__cachedRelations.should.have.property('owner');
|
|
|
|
// The relation should be promoted as the 'owner' property
|
|
|
|
p.should.have.property('owner');
|
|
|
|
// The __cachedRelations should be removed from json output
|
|
|
|
p.toJSON().should.not.have.property('__cachedRelations');
|
|
|
|
var owner = p.__cachedRelations.owner;
|
|
|
|
if (!p.ownerId) {
|
|
|
|
should.not.exist(owner);
|
|
|
|
} else {
|
|
|
|
should.exist(owner);
|
|
|
|
owner.id.should.eql(p.ownerId);
|
|
|
|
}
|
|
|
|
});
|
2015-05-20 16:13:56 +00:00
|
|
|
self.called.should.eql(2);
|
2015-05-13 18:49:43 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('including hasManyThrough should make only 3 db calls', function(done) {
|
2015-05-13 18:49:43 +00:00
|
|
|
var self = this;
|
2016-08-19 17:46:59 +00:00
|
|
|
Assembly.create([{name: 'sedan'}, {name: 'hatchback'},
|
|
|
|
{name: 'SUV'}],
|
2016-04-01 11:48:17 +00:00
|
|
|
function(err, assemblies) {
|
2016-08-19 17:46:59 +00:00
|
|
|
Part.create([{partNumber: 'engine'}, {partNumber: 'bootspace'},
|
|
|
|
{partNumber: 'silencer'}],
|
2016-04-01 11:48:17 +00:00
|
|
|
function(err, parts) {
|
|
|
|
async.each(parts, function(part, next) {
|
|
|
|
async.each(assemblies, function(assembly, next) {
|
2015-05-13 18:49:43 +00:00
|
|
|
if (assembly.name === 'SUV') {
|
|
|
|
return next();
|
|
|
|
}
|
|
|
|
if (assembly.name === 'hatchback' &&
|
|
|
|
part.partNumber === 'bootspace') {
|
|
|
|
return next();
|
|
|
|
}
|
2016-04-01 11:48:17 +00:00
|
|
|
assembly.parts.add(part, function(err, data) {
|
2015-05-13 18:49:43 +00:00
|
|
|
next();
|
|
|
|
});
|
|
|
|
}, next);
|
2016-04-01 11:48:17 +00:00
|
|
|
}, function(err) {
|
2015-05-20 16:13:56 +00:00
|
|
|
self.called = 0;
|
2015-05-13 18:49:43 +00:00
|
|
|
Assembly.find({
|
|
|
|
where: {
|
|
|
|
name: {
|
2016-04-01 11:48:17 +00:00
|
|
|
inq: ['sedan', 'hatchback', 'SUV'],
|
|
|
|
},
|
2015-05-13 18:49:43 +00:00
|
|
|
},
|
2016-04-01 11:48:17 +00:00
|
|
|
include: 'parts',
|
|
|
|
}, function(err, result) {
|
2015-05-13 18:49:43 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exists(result);
|
|
|
|
result.length.should.equal(3);
|
2015-05-29 15:57:28 +00:00
|
|
|
// Please note the order of assemblies is random
|
|
|
|
var assemblies = {};
|
|
|
|
result.forEach(function(r) {
|
|
|
|
assemblies[r.name] = r;
|
|
|
|
});
|
2015-05-13 18:49:43 +00:00
|
|
|
//sedan
|
2015-05-29 15:57:28 +00:00
|
|
|
assemblies.sedan.parts().should.have.length(3);
|
|
|
|
//hatchback
|
|
|
|
assemblies.hatchback.parts().should.have.length(2);
|
2015-05-13 18:49:43 +00:00
|
|
|
//SUV
|
2015-05-29 15:57:28 +00:00
|
|
|
assemblies.SUV.parts().should.have.length(0);
|
2015-05-20 16:13:56 +00:00
|
|
|
self.called.should.eql(3);
|
2015-05-13 18:49:43 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('including hasMany should make only 2 db calls', function(done) {
|
2015-05-13 18:49:43 +00:00
|
|
|
var self = this;
|
2016-08-19 17:46:59 +00:00
|
|
|
User.find({include: ['posts', 'passports']}, function(err, users) {
|
2015-05-13 18:49:43 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(users);
|
|
|
|
users.length.should.be.ok;
|
2016-04-01 11:48:17 +00:00
|
|
|
users.forEach(function(user) {
|
2015-05-13 18:49:43 +00:00
|
|
|
// The relation should be promoted as the 'owner' property
|
|
|
|
user.should.have.property('posts');
|
|
|
|
user.should.have.property('passports');
|
|
|
|
|
|
|
|
var userObj = user.toJSON();
|
|
|
|
userObj.should.have.property('posts');
|
|
|
|
userObj.should.have.property('passports');
|
|
|
|
userObj.posts.should.be.an.instanceOf(Array);
|
|
|
|
userObj.passports.should.be.an.instanceOf(Array);
|
|
|
|
|
|
|
|
// The __cachedRelations should be removed from json output
|
|
|
|
userObj.should.not.have.property('__cachedRelations');
|
|
|
|
|
|
|
|
user.__cachedRelations.should.have.property('posts');
|
|
|
|
user.__cachedRelations.should.have.property('passports');
|
2016-04-01 11:48:17 +00:00
|
|
|
user.__cachedRelations.posts.forEach(function(p) {
|
2015-05-13 18:49:43 +00:00
|
|
|
p.userId.should.eql(user.id);
|
|
|
|
});
|
2016-04-01 11:48:17 +00:00
|
|
|
user.__cachedRelations.passports.forEach(function(pp) {
|
2015-05-13 18:49:43 +00:00
|
|
|
pp.ownerId.should.eql(user.id);
|
|
|
|
});
|
|
|
|
});
|
2015-05-20 16:13:56 +00:00
|
|
|
self.called.should.eql(3);
|
2015-05-13 18:49:43 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should not make n+1 db calls in relation syntax',
|
2016-04-01 11:48:17 +00:00
|
|
|
function(done) {
|
2015-05-13 18:49:43 +00:00
|
|
|
var self = this;
|
2016-08-19 17:46:59 +00:00
|
|
|
User.find({include: [{relation: 'posts', scope: {
|
|
|
|
where: {title: 'Post A'},
|
|
|
|
}}, 'passports']}, function(err, users) {
|
2015-05-13 18:49:43 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
should.exist(users);
|
|
|
|
users.length.should.be.ok;
|
2016-04-01 11:48:17 +00:00
|
|
|
users.forEach(function(user) {
|
2015-05-13 18:49:43 +00:00
|
|
|
// The relation should be promoted as the 'owner' property
|
|
|
|
user.should.have.property('posts');
|
|
|
|
user.should.have.property('passports');
|
|
|
|
|
|
|
|
var userObj = user.toJSON();
|
|
|
|
userObj.should.have.property('posts');
|
|
|
|
userObj.should.have.property('passports');
|
|
|
|
userObj.posts.should.be.an.instanceOf(Array);
|
|
|
|
userObj.passports.should.be.an.instanceOf(Array);
|
|
|
|
|
|
|
|
// The __cachedRelations should be removed from json output
|
|
|
|
userObj.should.not.have.property('__cachedRelations');
|
|
|
|
|
|
|
|
user.__cachedRelations.should.have.property('posts');
|
|
|
|
user.__cachedRelations.should.have.property('passports');
|
2016-04-01 11:48:17 +00:00
|
|
|
user.__cachedRelations.posts.forEach(function(p) {
|
2015-05-13 18:49:43 +00:00
|
|
|
p.userId.should.eql(user.id);
|
|
|
|
p.title.should.be.equal('Post A');
|
|
|
|
});
|
2016-04-01 11:48:17 +00:00
|
|
|
user.__cachedRelations.passports.forEach(function(pp) {
|
2015-05-13 18:49:43 +00:00
|
|
|
pp.ownerId.should.eql(user.id);
|
|
|
|
});
|
|
|
|
});
|
2015-05-20 16:13:56 +00:00
|
|
|
self.called.should.eql(3);
|
2015-05-13 18:49:43 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2016-06-03 22:00:21 +00:00
|
|
|
|
|
|
|
it('should support disableInclude for hasAndBelongsToMany', function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
var Patient = db.define('Patient', {name: String});
|
|
|
|
var Doctor = db.define('Doctor', {name: String});
|
2016-06-03 22:00:21 +00:00
|
|
|
var DoctorPatient = db.define('DoctorPatient');
|
|
|
|
Doctor.hasAndBelongsToMany('patients', {
|
|
|
|
model: 'Patient',
|
2016-08-19 17:46:59 +00:00
|
|
|
options: {disableInclude: true},
|
2016-06-03 22:00:21 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
var doctor;
|
|
|
|
return db.automigrate(['Patient', 'Doctor', 'DoctorPatient']).then(function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
return Doctor.create({name: 'Who'});
|
2016-06-03 22:00:21 +00:00
|
|
|
}).then(function(inst) {
|
|
|
|
doctor = inst;
|
2016-08-19 17:46:59 +00:00
|
|
|
return doctor.patients.create({name: 'Lazarus'});
|
2016-06-03 22:00:21 +00:00
|
|
|
}).then(function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
return Doctor.find({include: ['patients']});
|
2016-06-03 22:00:21 +00:00
|
|
|
}).then(function(list) {
|
|
|
|
list.should.have.length(1);
|
|
|
|
list[0].toJSON().should.not.have.property('patients');
|
|
|
|
});
|
|
|
|
});
|
2013-03-27 00:46:59 +00:00
|
|
|
});
|
|
|
|
|
2016-09-08 20:05:48 +00:00
|
|
|
var createdUsers = [];
|
|
|
|
var createdPassports = [];
|
|
|
|
var createdProfiles = [];
|
|
|
|
var createdPosts = [];
|
2013-03-27 00:46:59 +00:00
|
|
|
function setup(done) {
|
2014-01-24 17:09:53 +00:00
|
|
|
db = getSchema();
|
|
|
|
City = db.define('City');
|
|
|
|
Street = db.define('Street');
|
|
|
|
Building = db.define('Building');
|
|
|
|
User = db.define('User', {
|
|
|
|
name: String,
|
2016-04-01 11:48:17 +00:00
|
|
|
age: Number,
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2015-05-13 18:49:43 +00:00
|
|
|
Profile = db.define('Profile', {
|
2016-04-01 11:48:17 +00:00
|
|
|
profileName: String,
|
2015-05-13 18:49:43 +00:00
|
|
|
});
|
2014-07-21 20:39:29 +00:00
|
|
|
AccessToken = db.define('AccessToken', {
|
2016-04-01 11:48:17 +00:00
|
|
|
token: String,
|
2014-07-21 20:39:29 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
Passport = db.define('Passport', {
|
2016-04-01 11:48:17 +00:00
|
|
|
number: String,
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
|
|
|
Post = db.define('Post', {
|
2016-04-01 11:48:17 +00:00
|
|
|
title: String,
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
Passport.belongsTo('owner', {model: User});
|
|
|
|
User.hasMany('passports', {foreignKey: 'ownerId'});
|
|
|
|
User.hasMany('posts', {foreignKey: 'userId'});
|
2014-07-21 20:39:29 +00:00
|
|
|
User.hasMany('accesstokens', {
|
|
|
|
foreignKey: 'userId',
|
2016-08-19 17:46:59 +00:00
|
|
|
options: {disableInclude: true},
|
2014-07-21 20:39:29 +00:00
|
|
|
});
|
2016-08-19 17:46:59 +00:00
|
|
|
Profile.belongsTo('user', {model: User});
|
|
|
|
User.hasOne('profile', {foreignKey: 'userId'});
|
|
|
|
Post.belongsTo('author', {model: User, foreignKey: 'userId'});
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2014-03-13 23:43:38 +00:00
|
|
|
Assembly = db.define('Assembly', {
|
2016-04-01 11:48:17 +00:00
|
|
|
name: String,
|
2014-03-13 23:43:38 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
Part = db.define('Part', {
|
2016-04-01 11:48:17 +00:00
|
|
|
partNumber: String,
|
2014-03-13 23:43:38 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
Assembly.hasAndBelongsToMany(Part);
|
|
|
|
Part.hasAndBelongsToMany(Assembly);
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
db.automigrate(function() {
|
2014-01-24 17:09:53 +00:00
|
|
|
createUsers();
|
|
|
|
function createUsers() {
|
|
|
|
clearAndCreate(
|
|
|
|
User,
|
|
|
|
[
|
2016-08-19 17:46:59 +00:00
|
|
|
{name: 'User A', age: 21},
|
|
|
|
{name: 'User B', age: 22},
|
|
|
|
{name: 'User C', age: 23},
|
|
|
|
{name: 'User D', age: 24},
|
|
|
|
{name: 'User E', age: 25},
|
2014-01-24 17:09:53 +00:00
|
|
|
],
|
2016-04-01 11:48:17 +00:00
|
|
|
function(items) {
|
2014-01-24 17:09:53 +00:00
|
|
|
createdUsers = items;
|
|
|
|
createPassports();
|
2014-07-21 20:39:29 +00:00
|
|
|
createAccessTokens();
|
2013-03-27 00:46:59 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
);
|
|
|
|
}
|
2013-03-27 00:46:59 +00:00
|
|
|
|
2014-07-21 20:39:29 +00:00
|
|
|
function createAccessTokens() {
|
|
|
|
clearAndCreate(
|
|
|
|
AccessToken,
|
|
|
|
[
|
2016-08-19 17:46:59 +00:00
|
|
|
{token: '1', userId: createdUsers[0].id},
|
|
|
|
{token: '2', userId: createdUsers[1].id},
|
2014-07-21 20:39:29 +00:00
|
|
|
],
|
2016-04-01 11:48:17 +00:00
|
|
|
function(items) {}
|
2014-07-21 20:39:29 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
function createPassports() {
|
|
|
|
clearAndCreate(
|
|
|
|
Passport,
|
|
|
|
[
|
2016-08-19 17:46:59 +00:00
|
|
|
{number: '1', ownerId: createdUsers[0].id},
|
|
|
|
{number: '2', ownerId: createdUsers[1].id},
|
|
|
|
{number: '3'},
|
|
|
|
{number: '4', ownerId: createdUsers[2].id},
|
2014-01-24 17:09:53 +00:00
|
|
|
],
|
2016-04-01 11:48:17 +00:00
|
|
|
function(items) {
|
2014-01-24 17:09:53 +00:00
|
|
|
createdPassports = items;
|
|
|
|
createPosts();
|
2013-03-27 00:46:59 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
);
|
|
|
|
}
|
2013-03-27 00:46:59 +00:00
|
|
|
|
2015-05-13 18:49:43 +00:00
|
|
|
function createProfiles() {
|
|
|
|
clearAndCreate(
|
|
|
|
Profile,
|
|
|
|
[
|
2016-08-19 17:46:59 +00:00
|
|
|
{profileName: 'Profile A', userId: createdUsers[0].id},
|
|
|
|
{profileName: 'Profile B', userId: createdUsers[1].id},
|
|
|
|
{profileName: 'Profile Z'},
|
2015-05-13 18:49:43 +00:00
|
|
|
],
|
2016-04-01 11:48:17 +00:00
|
|
|
function(items) {
|
|
|
|
createdProfiles = items;
|
2015-05-13 18:49:43 +00:00
|
|
|
done();
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
function createPosts() {
|
|
|
|
clearAndCreate(
|
|
|
|
Post,
|
|
|
|
[
|
2016-08-19 17:46:59 +00:00
|
|
|
{title: 'Post A', userId: createdUsers[0].id},
|
|
|
|
{title: 'Post B', userId: createdUsers[0].id},
|
|
|
|
{title: 'Post C', userId: createdUsers[0].id},
|
|
|
|
{title: 'Post D', userId: createdUsers[1].id},
|
|
|
|
{title: 'Post E'},
|
2014-01-24 17:09:53 +00:00
|
|
|
],
|
2016-04-01 11:48:17 +00:00
|
|
|
function(items) {
|
2014-01-24 17:09:53 +00:00
|
|
|
createdPosts = items;
|
2015-05-13 18:49:43 +00:00
|
|
|
createProfiles();
|
2013-03-27 00:46:59 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
);
|
|
|
|
}
|
2013-03-27 00:46:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-03-27 00:46:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function clearAndCreate(model, data, callback) {
|
2014-01-24 17:09:53 +00:00
|
|
|
var createdItems = [];
|
2016-04-01 11:48:17 +00:00
|
|
|
model.destroyAll(function() {
|
2014-01-24 17:09:53 +00:00
|
|
|
nextItem(null, null);
|
|
|
|
});
|
2013-03-27 00:46:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var itemIndex = 0;
|
|
|
|
|
|
|
|
function nextItem(err, lastItem) {
|
|
|
|
if (lastItem !== null) {
|
|
|
|
createdItems.push(lastItem);
|
|
|
|
}
|
|
|
|
if (itemIndex >= data.length) {
|
|
|
|
callback(createdItems);
|
|
|
|
return;
|
2013-03-27 00:46:59 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
model.create(data[itemIndex], nextItem);
|
|
|
|
itemIndex++;
|
|
|
|
}
|
2013-03-27 00:46:59 +00:00
|
|
|
}
|
2015-05-22 18:39:37 +00:00
|
|
|
|
|
|
|
describe('Model instance with included relation .toJSON()', function() {
|
|
|
|
var db, ChallengerModel, GameParticipationModel, ResultModel;
|
|
|
|
|
|
|
|
before(function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
db = new DataSource({connector: 'memory'});
|
2015-05-22 18:39:37 +00:00
|
|
|
ChallengerModel = db.createModel('Challenger',
|
|
|
|
{
|
2016-04-01 11:48:17 +00:00
|
|
|
name: String,
|
2015-05-22 18:39:37 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
relations: {
|
|
|
|
gameParticipations: {
|
|
|
|
type: 'hasMany',
|
|
|
|
model: 'GameParticipation',
|
2016-04-01 11:48:17 +00:00
|
|
|
foreignKey: '',
|
|
|
|
},
|
|
|
|
},
|
2015-05-22 18:39:37 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
GameParticipationModel = db.createModel('GameParticipation',
|
|
|
|
{
|
2016-04-01 11:48:17 +00:00
|
|
|
date: Date,
|
2015-05-22 18:39:37 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
relations: {
|
|
|
|
challenger: {
|
|
|
|
type: 'belongsTo',
|
|
|
|
model: 'Challenger',
|
2016-04-01 11:48:17 +00:00
|
|
|
foreignKey: '',
|
2015-05-22 18:39:37 +00:00
|
|
|
},
|
|
|
|
results: {
|
|
|
|
type: 'hasMany',
|
|
|
|
model: 'Result',
|
2016-04-01 11:48:17 +00:00
|
|
|
foreignKey: '',
|
|
|
|
},
|
|
|
|
},
|
2015-05-22 18:39:37 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
ResultModel = db.createModel('Result', {
|
|
|
|
points: Number,
|
|
|
|
}, {
|
|
|
|
relations: {
|
|
|
|
gameParticipation: {
|
|
|
|
type: 'belongsTo',
|
|
|
|
model: 'GameParticipation',
|
2016-04-01 11:48:17 +00:00
|
|
|
foreignKey: '',
|
|
|
|
},
|
|
|
|
},
|
2015-05-22 18:39:37 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
async.waterfall([
|
|
|
|
createChallengers,
|
|
|
|
createGameParticipations,
|
|
|
|
createResults],
|
|
|
|
function(err) {
|
|
|
|
done(err);
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
function createChallengers(callback) {
|
2016-08-19 17:46:59 +00:00
|
|
|
ChallengerModel.create([{name: 'challenger1'}, {name: 'challenger2'}], callback);
|
2015-05-22 18:39:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function createGameParticipations(challengers, callback) {
|
|
|
|
GameParticipationModel.create([
|
2016-08-19 17:46:59 +00:00
|
|
|
{challengerId: challengers[0].id, date: Date.now()},
|
|
|
|
{challengerId: challengers[0].id, date: Date.now()},
|
2015-05-22 18:39:37 +00:00
|
|
|
], callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
function createResults(gameParticipations, callback) {
|
|
|
|
ResultModel.create([
|
2016-08-19 17:46:59 +00:00
|
|
|
{gameParticipationId: gameParticipations[0].id, points: 10},
|
|
|
|
{gameParticipationId: gameParticipations[0].id, points: 20},
|
2015-05-22 18:39:37 +00:00
|
|
|
], callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
it('should recursively serialize objects', function(done) {
|
2016-08-19 17:46:59 +00:00
|
|
|
var filter = {include: {gameParticipations: 'results'}};
|
2015-05-22 18:39:37 +00:00
|
|
|
ChallengerModel.find(filter, function(err, challengers) {
|
|
|
|
|
|
|
|
var levelOneInclusion = challengers[0].toJSON().gameParticipations[0];
|
|
|
|
assert(levelOneInclusion.__data === undefined, '.__data of a level 1 inclusion is undefined.');
|
|
|
|
|
|
|
|
var levelTwoInclusion = challengers[0].toJSON().gameParticipations[0].results[0];
|
|
|
|
assert(levelTwoInclusion.__data === undefined, '__data of a level 2 inclusion is undefined.');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|