schema.test.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245
  1. 'use strict';
  2. const upperFirst = require('lodash/upperFirst');
  3. const chai = require('chai');
  4. const expect = chai.expect;
  5. const Support = require('../support');
  6. const { DataTypes, Op } = require('@sequelize/core');
  7. const dialect = Support.sequelize.dialect;
  8. const promiseProps = require('p-props');
  9. const sortById = function (a, b) {
  10. return a.id < b.id ? -1 : 1;
  11. };
  12. describe(Support.getTestDialectTeaser('Includes with schemas'), () => {
  13. if (!dialect.supports.schemas) {
  14. return;
  15. }
  16. describe('findAll', () => {
  17. beforeEach(async function () {
  18. this.fixtureA = async function () {
  19. await this.sequelize.dropSchema('account');
  20. await this.sequelize.createSchema('account');
  21. const AccUser = this.sequelize.define('AccUser', {}, { schema: 'account' });
  22. const Company = this.sequelize.define(
  23. 'Company',
  24. {
  25. name: DataTypes.STRING,
  26. },
  27. { schema: 'account' },
  28. );
  29. const Product = this.sequelize.define(
  30. 'Product',
  31. {
  32. title: DataTypes.STRING,
  33. },
  34. { schema: 'account' },
  35. );
  36. const Tag = this.sequelize.define(
  37. 'Tag',
  38. {
  39. name: DataTypes.STRING,
  40. },
  41. { schema: 'account' },
  42. );
  43. const Price = this.sequelize.define(
  44. 'Price',
  45. {
  46. value: DataTypes.FLOAT,
  47. },
  48. { schema: 'account' },
  49. );
  50. const Customer = this.sequelize.define(
  51. 'Customer',
  52. {
  53. name: DataTypes.STRING,
  54. },
  55. { schema: 'account' },
  56. );
  57. const Group = this.sequelize.define(
  58. 'Group',
  59. {
  60. name: DataTypes.STRING,
  61. },
  62. { schema: 'account' },
  63. );
  64. const GroupMember = this.sequelize.define('GroupMember', {}, { schema: 'account' });
  65. const Rank = this.sequelize.define(
  66. 'Rank',
  67. {
  68. name: DataTypes.STRING,
  69. canInvite: {
  70. type: DataTypes.INTEGER,
  71. defaultValue: 0,
  72. },
  73. canRemove: {
  74. type: DataTypes.INTEGER,
  75. defaultValue: 0,
  76. },
  77. canPost: {
  78. type: DataTypes.INTEGER,
  79. defaultValue: 0,
  80. },
  81. },
  82. { schema: 'account' },
  83. );
  84. this.models = {
  85. AccUser,
  86. Company,
  87. Product,
  88. Tag,
  89. Price,
  90. Customer,
  91. Group,
  92. GroupMember,
  93. Rank,
  94. };
  95. AccUser.hasMany(Product);
  96. Product.belongsTo(AccUser);
  97. Product.belongsToMany(Tag, { through: 'product_tag' });
  98. Tag.belongsToMany(Product, { through: 'product_tag' });
  99. Product.belongsTo(Tag, { as: 'Category' });
  100. Product.belongsTo(Company);
  101. Product.hasMany(Price);
  102. Price.belongsTo(Product);
  103. AccUser.hasMany(GroupMember, { as: 'Memberships' });
  104. GroupMember.belongsTo(AccUser);
  105. GroupMember.belongsTo(Rank);
  106. GroupMember.belongsTo(Group);
  107. Group.hasMany(GroupMember, { as: 'Memberships' });
  108. await this.sequelize.sync({ force: true });
  109. const [groups, companies, ranks, tags] = await Promise.all([
  110. Group.bulkCreate([
  111. { name: 'Developers' },
  112. { name: 'Designers' },
  113. { name: 'Managers' },
  114. ]).then(() => Group.findAll()),
  115. Company.bulkCreate([
  116. { name: 'Sequelize' },
  117. { name: 'Coca Cola' },
  118. { name: 'Bonanza' },
  119. { name: 'NYSE' },
  120. { name: 'Coshopr' },
  121. ]).then(() => Company.findAll()),
  122. Rank.bulkCreate([
  123. { name: 'Admin', canInvite: 1, canRemove: 1, canPost: 1 },
  124. { name: 'Trustee', canInvite: 1, canRemove: 0, canPost: 1 },
  125. { name: 'Member', canInvite: 1, canRemove: 0, canPost: 0 },
  126. ]).then(() => Rank.findAll()),
  127. Tag.bulkCreate([
  128. { name: 'A' },
  129. { name: 'B' },
  130. { name: 'C' },
  131. { name: 'D' },
  132. { name: 'E' },
  133. ]).then(() => Tag.findAll()),
  134. ]);
  135. for (const i of [0, 1, 2, 3, 4]) {
  136. const [user, products] = await Promise.all([
  137. AccUser.create(),
  138. Product.bulkCreate([
  139. { title: 'Chair' },
  140. { title: 'Desk' },
  141. { title: 'Bed' },
  142. { title: 'Pen' },
  143. { title: 'Monitor' },
  144. ]).then(() => Product.findAll()),
  145. ]);
  146. const groupMembers = [
  147. { accUserId: user.id, GroupId: groups[0].id, rankId: ranks[0].id },
  148. { accUserId: user.id, GroupId: groups[1].id, rankId: ranks[2].id },
  149. ];
  150. if (i < 3) {
  151. groupMembers.push({ accUserId: user.id, GroupId: groups[2].id, rankId: ranks[1].id });
  152. }
  153. await Promise.all([
  154. GroupMember.bulkCreate(groupMembers),
  155. user.setProducts([products[i * 5 + 0], products[i * 5 + 1], products[i * 5 + 3]]),
  156. products[i * 5 + 0].setTags([tags[0], tags[2]]),
  157. products[i * 5 + 1].setTags([tags[1]]),
  158. products[i * 5 + 0].setCategory(tags[1]),
  159. products[i * 5 + 2].setTags([tags[0]]),
  160. products[i * 5 + 3].setTags([tags[0]]),
  161. products[i * 5 + 0].setCompany(companies[4]),
  162. products[i * 5 + 1].setCompany(companies[3]),
  163. products[i * 5 + 2].setCompany(companies[2]),
  164. products[i * 5 + 3].setCompany(companies[1]),
  165. products[i * 5 + 4].setCompany(companies[0]),
  166. Price.bulkCreate([
  167. { productId: products[i * 5 + 0].id, value: 5 },
  168. { productId: products[i * 5 + 0].id, value: 10 },
  169. { productId: products[i * 5 + 1].id, value: 5 },
  170. { productId: products[i * 5 + 1].id, value: 10 },
  171. { productId: products[i * 5 + 1].id, value: 15 },
  172. { productId: products[i * 5 + 1].id, value: 20 },
  173. { productId: products[i * 5 + 2].id, value: 20 },
  174. { productId: products[i * 5 + 3].id, value: 20 },
  175. ]),
  176. ]);
  177. }
  178. };
  179. await this.sequelize.createSchema('account');
  180. });
  181. it('should support an include with multiple different association types', async function () {
  182. await this.sequelize.dropSchema('account');
  183. await this.sequelize.createSchema('account');
  184. const AccUser = this.sequelize.define('AccUser', {}, { schema: 'account' });
  185. const Product = this.sequelize.define(
  186. 'Product',
  187. {
  188. title: DataTypes.STRING,
  189. },
  190. { schema: 'account' },
  191. );
  192. const Tag = this.sequelize.define(
  193. 'Tag',
  194. {
  195. name: DataTypes.STRING,
  196. },
  197. { schema: 'account' },
  198. );
  199. const Price = this.sequelize.define(
  200. 'Price',
  201. {
  202. value: DataTypes.FLOAT,
  203. },
  204. { schema: 'account' },
  205. );
  206. const Group = this.sequelize.define(
  207. 'Group',
  208. {
  209. name: DataTypes.STRING,
  210. },
  211. { schema: 'account' },
  212. );
  213. const GroupMember = this.sequelize.define('GroupMember', {}, { schema: 'account' });
  214. const Rank = this.sequelize.define(
  215. 'Rank',
  216. {
  217. name: DataTypes.STRING,
  218. canInvite: {
  219. type: DataTypes.INTEGER,
  220. defaultValue: 0,
  221. },
  222. canRemove: {
  223. type: DataTypes.INTEGER,
  224. defaultValue: 0,
  225. },
  226. },
  227. { schema: 'account' },
  228. );
  229. AccUser.hasMany(Product);
  230. Product.belongsTo(AccUser);
  231. Product.belongsToMany(Tag, { through: 'product_tag' });
  232. Tag.belongsToMany(Product, { through: 'product_tag' });
  233. Product.belongsTo(Tag, { as: 'Category' });
  234. Product.hasMany(Price);
  235. Price.belongsTo(Product);
  236. AccUser.hasMany(GroupMember, { as: 'Memberships' });
  237. GroupMember.belongsTo(AccUser);
  238. GroupMember.belongsTo(Rank);
  239. GroupMember.belongsTo(Group);
  240. Group.hasMany(GroupMember, { as: 'Memberships' });
  241. await this.sequelize.sync({ force: true });
  242. const [groups, ranks, tags] = await Promise.all([
  243. Group.bulkCreate([{ name: 'Developers' }, { name: 'Designers' }]).then(() =>
  244. Group.findAll(),
  245. ),
  246. Rank.bulkCreate([
  247. { name: 'Admin', canInvite: 1, canRemove: 1 },
  248. { name: 'Member', canInvite: 1, canRemove: 0 },
  249. ]).then(() => Rank.findAll()),
  250. Tag.bulkCreate([{ name: 'A' }, { name: 'B' }, { name: 'C' }]).then(() => Tag.findAll()),
  251. ]);
  252. for (const i of [0, 1, 2, 3, 4]) {
  253. const [user, products] = await Promise.all([
  254. AccUser.create(),
  255. Product.bulkCreate([{ title: 'Chair' }, { title: 'Desk' }]).then(() => Product.findAll()),
  256. ]);
  257. await Promise.all([
  258. GroupMember.bulkCreate([
  259. { accUserId: user.id, groupId: groups[0].id, rankId: ranks[0].id },
  260. { accUserId: user.id, groupId: groups[1].id, rankId: ranks[1].id },
  261. ]),
  262. user.setProducts([products[i * 2 + 0], products[i * 2 + 1]]),
  263. products[i * 2 + 0].setTags([tags[0], tags[2]]),
  264. products[i * 2 + 1].setTags([tags[1]]),
  265. products[i * 2 + 0].setCategory(tags[1]),
  266. Price.bulkCreate([
  267. { productId: products[i * 2 + 0].id, value: 5 },
  268. { productId: products[i * 2 + 0].id, value: 10 },
  269. { productId: products[i * 2 + 1].id, value: 5 },
  270. { productId: products[i * 2 + 1].id, value: 10 },
  271. { productId: products[i * 2 + 1].id, value: 15 },
  272. { productId: products[i * 2 + 1].id, value: 20 },
  273. ]),
  274. ]);
  275. const users = await AccUser.findAll({
  276. include: [
  277. {
  278. model: GroupMember,
  279. as: 'Memberships',
  280. include: [Group, Rank],
  281. },
  282. {
  283. model: Product,
  284. include: ['tags', { model: Tag, as: 'Category' }, Price],
  285. },
  286. ],
  287. order: [[AccUser.getAttributes().id, 'ASC']],
  288. });
  289. for (const user of users) {
  290. expect(user.Memberships).to.be.ok;
  291. user.Memberships.sort(sortById);
  292. expect(user.Memberships.length).to.equal(2);
  293. expect(user.Memberships[0].group.name).to.equal('Developers');
  294. expect(user.Memberships[0].rank.canRemove).to.equal(1);
  295. expect(user.Memberships[1].group.name).to.equal('Designers');
  296. expect(user.Memberships[1].rank.canRemove).to.equal(0);
  297. user.products.sort(sortById);
  298. expect(user.products.length).to.equal(2);
  299. expect(user.products[0].tags.length).to.equal(2);
  300. expect(user.products[1].tags.length).to.equal(1);
  301. expect(user.products[0].Category).to.be.ok;
  302. expect(user.products[1].Category).not.to.be.ok;
  303. expect(user.products[0].prices.length).to.equal(2);
  304. expect(user.products[1].prices.length).to.equal(4);
  305. }
  306. }
  307. });
  308. it('should support many levels of belongsTo', async function () {
  309. const A = this.sequelize.define('a', {}, { schema: 'account' });
  310. const B = this.sequelize.define('b', {}, { schema: 'account' });
  311. const C = this.sequelize.define('c', {}, { schema: 'account' });
  312. const D = this.sequelize.define('d', {}, { schema: 'account' });
  313. const E = this.sequelize.define('e', {}, { schema: 'account' });
  314. const F = this.sequelize.define('f', {}, { schema: 'account' });
  315. const G = this.sequelize.define('g', {}, { schema: 'account' });
  316. const H = this.sequelize.define('h', {}, { schema: 'account' });
  317. A.belongsTo(B);
  318. B.belongsTo(C);
  319. C.belongsTo(D);
  320. D.belongsTo(E);
  321. E.belongsTo(F);
  322. F.belongsTo(G);
  323. G.belongsTo(H);
  324. let b;
  325. const singles = [B, C, D, E, F, G, H];
  326. await this.sequelize.sync();
  327. await A.bulkCreate([{}, {}, {}, {}, {}, {}, {}, {}]);
  328. let previousInstance;
  329. for (const model of singles) {
  330. const instance = await model.create({});
  331. if (previousInstance) {
  332. await previousInstance[`set${upperFirst(model.name)}`](instance);
  333. previousInstance = instance;
  334. continue;
  335. }
  336. previousInstance = b = instance;
  337. }
  338. let as = await A.findAll();
  339. await Promise.all(as.map(a => a.setB(b)));
  340. as = await A.findAll({
  341. include: [
  342. {
  343. model: B,
  344. include: [
  345. {
  346. model: C,
  347. include: [
  348. {
  349. model: D,
  350. include: [
  351. {
  352. model: E,
  353. include: [
  354. {
  355. model: F,
  356. include: [
  357. {
  358. model: G,
  359. include: [{ model: H }],
  360. },
  361. ],
  362. },
  363. ],
  364. },
  365. ],
  366. },
  367. ],
  368. },
  369. ],
  370. },
  371. ],
  372. });
  373. expect(as.length).to.be.ok;
  374. for (const a of as) {
  375. expect(a.b.c.d.e.f.g.h).to.be.ok;
  376. }
  377. });
  378. it('should support ordering with only belongsTo includes', async function () {
  379. const User = this.sequelize.define('SpecialUser', {}, { schema: 'account' });
  380. const Item = this.sequelize.define('Item', { test: DataTypes.STRING }, { schema: 'account' });
  381. const Order = this.sequelize.define(
  382. 'Order',
  383. { position: DataTypes.INTEGER },
  384. { schema: 'account' },
  385. );
  386. User.belongsTo(Item, { as: 'itemA', foreignKey: 'itemA_id' });
  387. User.belongsTo(Item, { as: 'itemB', foreignKey: 'itemB_id' });
  388. User.belongsTo(Order);
  389. await this.sequelize.sync();
  390. await Promise.all([
  391. User.bulkCreate([{}, {}, {}]),
  392. Item.bulkCreate([{ test: 'abc' }, { test: 'def' }, { test: 'ghi' }, { test: 'jkl' }]),
  393. Order.bulkCreate([{ position: 2 }, { position: 3 }, { position: 1 }]),
  394. ]);
  395. const [users, items, orders] = await Promise.all([
  396. User.findAll(),
  397. Item.findAll({ order: ['id'] }),
  398. Order.findAll({ order: ['id'] }),
  399. ]);
  400. await Promise.all([
  401. users[0].setItemA(items[0]),
  402. users[0].setItemB(items[1]),
  403. users[0].setOrder(orders[2]),
  404. users[1].setItemA(items[2]),
  405. users[1].setItemB(items[3]),
  406. users[1].setOrder(orders[1]),
  407. users[2].setItemA(items[0]),
  408. users[2].setItemB(items[3]),
  409. users[2].setOrder(orders[0]),
  410. ]);
  411. const as = await User.findAll({
  412. include: [
  413. { model: Item, as: 'itemA', where: { test: 'abc' } },
  414. { model: Item, as: 'itemB' },
  415. Order,
  416. ],
  417. order: [[Order, 'position']],
  418. });
  419. expect(as.length).to.eql(2);
  420. expect(as[0].itemA.test).to.eql('abc');
  421. expect(as[1].itemA.test).to.eql('abc');
  422. expect(as[0].order.position).to.eql(1);
  423. expect(as[1].order.position).to.eql(2);
  424. });
  425. it('should include attributes from through models', async function () {
  426. const Product = this.sequelize.define(
  427. 'Product',
  428. {
  429. title: DataTypes.STRING,
  430. },
  431. { schema: 'account' },
  432. );
  433. const Tag = this.sequelize.define(
  434. 'Tag',
  435. {
  436. name: DataTypes.STRING,
  437. },
  438. { schema: 'account' },
  439. );
  440. const ProductTag = this.sequelize.define(
  441. 'ProductTag',
  442. {
  443. priority: DataTypes.INTEGER,
  444. },
  445. { schema: 'account' },
  446. );
  447. Product.belongsToMany(Tag, { through: ProductTag });
  448. Tag.belongsToMany(Product, { through: ProductTag });
  449. await this.sequelize.sync({ force: true });
  450. await Promise.all([
  451. Product.bulkCreate([{ title: 'Chair' }, { title: 'Desk' }, { title: 'Dress' }]),
  452. Tag.bulkCreate([{ name: 'A' }, { name: 'B' }, { name: 'C' }]),
  453. ]);
  454. const [products0, tags] = await Promise.all([Product.findAll(), Tag.findAll()]);
  455. await Promise.all([
  456. products0[0].addTag(tags[0], { through: { priority: 1 } }),
  457. products0[0].addTag(tags[1], { through: { priority: 2 } }),
  458. products0[1].addTag(tags[1], { through: { priority: 1 } }),
  459. products0[2].addTag(tags[0], { through: { priority: 3 } }),
  460. products0[2].addTag(tags[1], { through: { priority: 1 } }),
  461. products0[2].addTag(tags[2], { through: { priority: 2 } }),
  462. ]);
  463. const products = await Product.findAll({
  464. include: [{ model: Tag }],
  465. order: [
  466. ['id', 'ASC'],
  467. [Tag, 'id', 'ASC'],
  468. ],
  469. });
  470. expect(products[0].tags[0].ProductTag.priority).to.equal(1);
  471. expect(products[0].tags[1].ProductTag.priority).to.equal(2);
  472. expect(products[1].tags[0].ProductTag.priority).to.equal(1);
  473. expect(products[2].tags[0].ProductTag.priority).to.equal(3);
  474. expect(products[2].tags[1].ProductTag.priority).to.equal(1);
  475. expect(products[2].tags[2].ProductTag.priority).to.equal(2);
  476. });
  477. it('should support a required belongsTo include', async function () {
  478. const User = this.sequelize.define('User', {}, { schema: 'account' });
  479. const Group = this.sequelize.define('Group', {}, { schema: 'account' });
  480. User.belongsTo(Group);
  481. await this.sequelize.sync({ force: true });
  482. await Promise.all([Group.bulkCreate([{}, {}]), User.bulkCreate([{}, {}, {}])]);
  483. const [groups, users0] = await Promise.all([Group.findAll(), User.findAll()]);
  484. await users0[2].setGroup(groups[1]);
  485. const users = await User.findAll({
  486. include: [{ model: Group, required: true }],
  487. });
  488. expect(users.length).to.equal(1);
  489. expect(users[0].group).to.be.ok;
  490. });
  491. it('should be possible to extend the on clause with a where option on a belongsTo include', async function () {
  492. const User = this.sequelize.define('User', {}, { schema: 'account' });
  493. const Group = this.sequelize.define(
  494. 'Group',
  495. {
  496. name: DataTypes.STRING,
  497. },
  498. { schema: 'account' },
  499. );
  500. User.belongsTo(Group);
  501. await this.sequelize.sync({ force: true });
  502. await Promise.all([
  503. Group.bulkCreate([{ name: 'A' }, { name: 'B' }]),
  504. User.bulkCreate([{}, {}]),
  505. ]);
  506. const [groups, users0] = await Promise.all([Group.findAll(), User.findAll()]);
  507. await Promise.all([users0[0].setGroup(groups[1]), users0[1].setGroup(groups[0])]);
  508. const users = await User.findAll({
  509. include: [{ model: Group, where: { name: 'A' } }],
  510. });
  511. expect(users.length).to.equal(1);
  512. expect(users[0].group).to.be.ok;
  513. expect(users[0].group.name).to.equal('A');
  514. });
  515. it('should be possible to extend the on clause with a where option on a belongsTo include', async function () {
  516. const User = this.sequelize.define('User', {}, { schema: 'account' });
  517. const Group = this.sequelize.define(
  518. 'Group',
  519. {
  520. name: DataTypes.STRING,
  521. },
  522. { schema: 'account' },
  523. );
  524. User.belongsTo(Group);
  525. await this.sequelize.sync({ force: true });
  526. await Promise.all([
  527. Group.bulkCreate([{ name: 'A' }, { name: 'B' }]),
  528. User.bulkCreate([{}, {}]),
  529. ]);
  530. const [groups, users0] = await Promise.all([Group.findAll(), User.findAll()]);
  531. await Promise.all([users0[0].setGroup(groups[1]), users0[1].setGroup(groups[0])]);
  532. const users = await User.findAll({
  533. include: [{ model: Group, required: true }],
  534. });
  535. for (const user of users) {
  536. expect(user.group).to.be.ok;
  537. }
  538. });
  539. it('should be possible to define a belongsTo include as required with child hasMany with limit', async function () {
  540. const User = this.sequelize.define('User', {}, { schema: 'account' });
  541. const Group = this.sequelize.define(
  542. 'Group',
  543. {
  544. name: DataTypes.STRING,
  545. },
  546. { schema: 'account' },
  547. );
  548. const Category = this.sequelize.define(
  549. 'Category',
  550. {
  551. category: DataTypes.STRING,
  552. },
  553. { schema: 'account' },
  554. );
  555. User.belongsTo(Group);
  556. Group.hasMany(Category);
  557. await this.sequelize.sync({ force: true });
  558. await Promise.all([
  559. Group.bulkCreate([{ name: 'A' }, { name: 'B' }]),
  560. User.bulkCreate([{}, {}]),
  561. Category.bulkCreate([{}, {}]),
  562. ]);
  563. const [groups, users0, categories] = await Promise.all([
  564. Group.findAll(),
  565. User.findAll(),
  566. Category.findAll(),
  567. ]);
  568. const promises = [users0[0].setGroup(groups[1]), users0[1].setGroup(groups[0])];
  569. for (const group of groups) {
  570. promises.push(group.setCategories(categories));
  571. }
  572. await Promise.all(promises);
  573. const users = await User.findAll({
  574. include: [
  575. {
  576. model: Group,
  577. required: true,
  578. include: [{ model: Category }],
  579. },
  580. ],
  581. limit: 1,
  582. });
  583. expect(users.length).to.equal(1);
  584. for (const user of users) {
  585. expect(user.group).to.be.ok;
  586. expect(user.group.categories).to.be.ok;
  587. }
  588. });
  589. it('should be possible to define a belongsTo include as required with child hasMany with limit and aliases', async function () {
  590. const User = this.sequelize.define('User', {}, { schema: 'account' });
  591. const Group = this.sequelize.define(
  592. 'Group',
  593. {
  594. name: DataTypes.STRING,
  595. },
  596. { schema: 'account' },
  597. );
  598. const Category = this.sequelize.define(
  599. 'Category',
  600. {
  601. category: DataTypes.STRING,
  602. },
  603. { schema: 'account' },
  604. );
  605. User.belongsTo(Group, { as: 'Team' });
  606. Group.hasMany(Category, { as: 'Tags' });
  607. await this.sequelize.sync({ force: true });
  608. await Promise.all([
  609. Group.bulkCreate([{ name: 'A' }, { name: 'B' }]),
  610. User.bulkCreate([{}, {}]),
  611. Category.bulkCreate([{}, {}]),
  612. ]);
  613. const [groups, users0, categories] = await Promise.all([
  614. Group.findAll(),
  615. User.findAll(),
  616. Category.findAll(),
  617. ]);
  618. const promises = [users0[0].setTeam(groups[1]), users0[1].setTeam(groups[0])];
  619. for (const group of groups) {
  620. promises.push(group.setTags(categories));
  621. }
  622. await Promise.all(promises);
  623. const users = await User.findAll({
  624. include: [
  625. {
  626. model: Group,
  627. required: true,
  628. as: 'Team',
  629. include: [{ model: Category, as: 'Tags' }],
  630. },
  631. ],
  632. limit: 1,
  633. });
  634. expect(users.length).to.equal(1);
  635. for (const user of users) {
  636. expect(user.Team).to.be.ok;
  637. expect(user.Team.Tags).to.be.ok;
  638. }
  639. });
  640. it('should be possible to define a belongsTo include as required with child hasMany which is not required with limit', async function () {
  641. const User = this.sequelize.define('User', {}, { schema: 'account' });
  642. const Group = this.sequelize.define(
  643. 'Group',
  644. {
  645. name: DataTypes.STRING,
  646. },
  647. { schema: 'account' },
  648. );
  649. const Category = this.sequelize.define(
  650. 'Category',
  651. {
  652. category: DataTypes.STRING,
  653. },
  654. { schema: 'account' },
  655. );
  656. User.belongsTo(Group);
  657. Group.hasMany(Category);
  658. await this.sequelize.sync({ force: true });
  659. await Promise.all([
  660. Group.bulkCreate([{ name: 'A' }, { name: 'B' }]),
  661. User.bulkCreate([{}, {}]),
  662. Category.bulkCreate([{}, {}]),
  663. ]);
  664. const [groups, users0, categories] = await Promise.all([
  665. Group.findAll(),
  666. User.findAll(),
  667. Category.findAll(),
  668. ]);
  669. const promises = [users0[0].setGroup(groups[1]), users0[1].setGroup(groups[0])];
  670. for (const group of groups) {
  671. promises.push(group.setCategories(categories));
  672. }
  673. await Promise.all(promises);
  674. const users = await User.findAll({
  675. include: [
  676. {
  677. model: Group,
  678. required: true,
  679. include: [{ model: Category, required: false }],
  680. },
  681. ],
  682. limit: 1,
  683. });
  684. expect(users.length).to.equal(1);
  685. for (const user of users) {
  686. expect(user.group).to.be.ok;
  687. expect(user.group.categories).to.be.ok;
  688. }
  689. });
  690. it('should be possible to extend the on clause with a where option on a hasOne include', async function () {
  691. const User = this.sequelize.define('User', {}, { schema: 'account' });
  692. const Project = this.sequelize.define(
  693. 'Project',
  694. {
  695. title: DataTypes.STRING,
  696. },
  697. { schema: 'account' },
  698. );
  699. User.hasOne(Project, { as: 'LeaderOf' });
  700. await this.sequelize.sync({ force: true });
  701. await Promise.all([
  702. Project.bulkCreate([{ title: 'Alpha' }, { title: 'Beta' }]),
  703. User.bulkCreate([{}, {}]),
  704. ]);
  705. const [projects, users0] = await Promise.all([Project.findAll(), User.findAll()]);
  706. await Promise.all([users0[1].setLeaderOf(projects[1]), users0[0].setLeaderOf(projects[0])]);
  707. const users = await User.findAll({
  708. include: [{ model: Project, as: 'LeaderOf', where: { title: 'Beta' } }],
  709. });
  710. expect(users.length).to.equal(1);
  711. expect(users[0].LeaderOf).to.be.ok;
  712. expect(users[0].LeaderOf.title).to.equal('Beta');
  713. });
  714. it('should be possible to extend the on clause with a where option on a hasMany include with a through model', async function () {
  715. const Product = this.sequelize.define(
  716. 'Product',
  717. {
  718. title: DataTypes.STRING,
  719. },
  720. { schema: 'account' },
  721. );
  722. const Tag = this.sequelize.define(
  723. 'Tag',
  724. {
  725. name: DataTypes.STRING,
  726. },
  727. { schema: 'account' },
  728. );
  729. const ProductTag = this.sequelize.define(
  730. 'ProductTag',
  731. {
  732. priority: DataTypes.INTEGER,
  733. },
  734. { schema: 'account' },
  735. );
  736. Product.belongsToMany(Tag, { through: ProductTag });
  737. Tag.belongsToMany(Product, { through: ProductTag });
  738. await this.sequelize.sync({ force: true });
  739. await Promise.all([
  740. Product.bulkCreate([{ title: 'Chair' }, { title: 'Desk' }, { title: 'Dress' }]),
  741. Tag.bulkCreate([{ name: 'A' }, { name: 'B' }, { name: 'C' }]),
  742. ]);
  743. const [products0, tags] = await Promise.all([Product.findAll(), Tag.findAll()]);
  744. await Promise.all([
  745. products0[0].addTag(tags[0], { priority: 1 }),
  746. products0[0].addTag(tags[1], { priority: 2 }),
  747. products0[1].addTag(tags[1], { priority: 1 }),
  748. products0[2].addTag(tags[0], { priority: 3 }),
  749. products0[2].addTag(tags[1], { priority: 1 }),
  750. products0[2].addTag(tags[2], { priority: 2 }),
  751. ]);
  752. const products = await Product.findAll({
  753. include: [{ model: Tag, where: { name: 'C' } }],
  754. });
  755. expect(products.length).to.equal(1);
  756. expect(products[0].tags.length).to.equal(1);
  757. });
  758. it('should be possible to extend the on clause with a where option on nested includes', async function () {
  759. const User = this.sequelize.define(
  760. 'User',
  761. {
  762. name: DataTypes.STRING,
  763. },
  764. { schema: 'account' },
  765. );
  766. const Product = this.sequelize.define(
  767. 'Product',
  768. {
  769. title: DataTypes.STRING,
  770. },
  771. { schema: 'account' },
  772. );
  773. const Tag = this.sequelize.define(
  774. 'Tag',
  775. {
  776. name: DataTypes.STRING,
  777. },
  778. { schema: 'account' },
  779. );
  780. const Price = this.sequelize.define(
  781. 'Price',
  782. {
  783. value: DataTypes.FLOAT,
  784. },
  785. { schema: 'account' },
  786. );
  787. const Group = this.sequelize.define(
  788. 'Group',
  789. {
  790. name: DataTypes.STRING,
  791. },
  792. { schema: 'account' },
  793. );
  794. const GroupMember = this.sequelize.define('GroupMember', {}, { schema: 'account' });
  795. const Rank = this.sequelize.define(
  796. 'Rank',
  797. {
  798. name: DataTypes.STRING,
  799. canInvite: {
  800. type: DataTypes.INTEGER,
  801. defaultValue: 0,
  802. },
  803. canRemove: {
  804. type: DataTypes.INTEGER,
  805. defaultValue: 0,
  806. },
  807. },
  808. { schema: 'account' },
  809. );
  810. User.hasMany(Product);
  811. Product.belongsTo(User);
  812. Product.belongsToMany(Tag, { through: 'product_tag' });
  813. Tag.belongsToMany(Product, { through: 'product_tag' });
  814. Product.belongsTo(Tag, { as: 'Category' });
  815. Product.hasMany(Price);
  816. Price.belongsTo(Product);
  817. User.hasMany(GroupMember, { as: 'Memberships' });
  818. GroupMember.belongsTo(User);
  819. GroupMember.belongsTo(Rank);
  820. GroupMember.belongsTo(Group);
  821. Group.hasMany(GroupMember, { as: 'Memberships' });
  822. await this.sequelize.sync({ force: true });
  823. const [groups, ranks, tags] = await Promise.all([
  824. Group.bulkCreate([{ name: 'Developers' }, { name: 'Designers' }]).then(() =>
  825. Group.findAll(),
  826. ),
  827. Rank.bulkCreate([
  828. { name: 'Admin', canInvite: 1, canRemove: 1 },
  829. { name: 'Member', canInvite: 1, canRemove: 0 },
  830. ]).then(() => Rank.findAll()),
  831. Tag.bulkCreate([{ name: 'A' }, { name: 'B' }, { name: 'C' }]).then(() => Tag.findAll()),
  832. ]);
  833. for (const i of [0, 1, 2, 3, 4]) {
  834. const [user, products] = await Promise.all([
  835. User.create({ name: 'FooBarzz' }),
  836. Product.bulkCreate([{ title: 'Chair' }, { title: 'Desk' }]).then(() => Product.findAll()),
  837. ]);
  838. await Promise.all([
  839. GroupMember.bulkCreate([
  840. { userId: user.id, groupId: groups[0].id, rankId: ranks[0].id },
  841. { userId: user.id, groupId: groups[1].id, rankId: ranks[1].id },
  842. ]),
  843. user.setProducts([products[i * 2 + 0], products[i * 2 + 1]]),
  844. products[i * 2 + 0].setTags([tags[0], tags[2]]),
  845. products[i * 2 + 1].setTags([tags[1]]),
  846. products[i * 2 + 0].setCategory(tags[1]),
  847. Price.bulkCreate([
  848. { productId: products[i * 2 + 0].id, value: 5 },
  849. { productId: products[i * 2 + 0].id, value: 10 },
  850. { productId: products[i * 2 + 1].id, value: 5 },
  851. { productId: products[i * 2 + 1].id, value: 10 },
  852. { productId: products[i * 2 + 1].id, value: 15 },
  853. { productId: products[i * 2 + 1].id, value: 20 },
  854. ]),
  855. ]);
  856. const users = await User.findAll({
  857. include: [
  858. {
  859. model: GroupMember,
  860. as: 'Memberships',
  861. include: [Group, { model: Rank, where: { name: 'Admin' } }],
  862. },
  863. {
  864. model: Product,
  865. include: [
  866. 'tags',
  867. { model: Tag, as: 'Category' },
  868. {
  869. model: Price,
  870. where: {
  871. value: {
  872. [Op.gt]: 15,
  873. },
  874. },
  875. },
  876. ],
  877. },
  878. ],
  879. order: [['id', 'ASC']],
  880. });
  881. for (const user of users) {
  882. expect(user.Memberships.length).to.equal(1);
  883. expect(user.Memberships[0].rank.name).to.equal('Admin');
  884. expect(user.products.length).to.equal(1);
  885. expect(user.products[0].prices.length).to.equal(1);
  886. }
  887. }
  888. });
  889. it('should be possible to use limit and a where with a belongsTo include', async function () {
  890. const User = this.sequelize.define('User', {}, { schema: 'account' });
  891. const Group = this.sequelize.define(
  892. 'Group',
  893. {
  894. name: DataTypes.STRING,
  895. },
  896. { schema: 'account' },
  897. );
  898. User.belongsTo(Group);
  899. await this.sequelize.sync({ force: true });
  900. const results = await promiseProps({
  901. groups: Group.bulkCreate([{ name: 'A' }, { name: 'B' }]).then(() => {
  902. return Group.findAll();
  903. }),
  904. users: User.bulkCreate([{}, {}, {}, {}]).then(() => {
  905. return User.findAll();
  906. }),
  907. });
  908. await Promise.all([
  909. results.users[1].setGroup(results.groups[0]),
  910. results.users[2].setGroup(results.groups[0]),
  911. results.users[3].setGroup(results.groups[1]),
  912. results.users[0].setGroup(results.groups[0]),
  913. ]);
  914. const users = await User.findAll({
  915. include: [{ model: Group, where: { name: 'A' } }],
  916. limit: 2,
  917. });
  918. expect(users.length).to.equal(2);
  919. for (const user of users) {
  920. expect(user.group.name).to.equal('A');
  921. }
  922. });
  923. it('should be possible use limit, attributes and a where on a belongsTo with additional hasMany includes', async function () {
  924. await this.fixtureA();
  925. const products = await this.models.Product.findAll({
  926. attributes: ['title'],
  927. include: [
  928. { model: this.models.Company, where: { name: 'NYSE' } },
  929. { model: this.models.Tag, as: 'tags' },
  930. { model: this.models.Price },
  931. ],
  932. limit: 3,
  933. order: [['id', 'ASC']],
  934. });
  935. expect(products.length).to.equal(3);
  936. for (const product of products) {
  937. expect(product.company.name).to.equal('NYSE');
  938. expect(product.tags.length).to.be.ok;
  939. expect(product.prices.length).to.be.ok;
  940. }
  941. });
  942. it('should be possible to use limit and a where on a hasMany with additional includes', async function () {
  943. await this.fixtureA();
  944. const products = await this.models.Product.findAll({
  945. include: [
  946. { model: this.models.Company },
  947. { model: this.models.Tag, as: 'tags' },
  948. {
  949. model: this.models.Price,
  950. where: {
  951. value: { [Op.gt]: 5 },
  952. },
  953. },
  954. ],
  955. limit: 6,
  956. order: [['id', 'ASC']],
  957. });
  958. expect(products.length).to.equal(6);
  959. for (const product of products) {
  960. expect(product.tags.length).to.be.ok;
  961. expect(product.prices.length).to.be.ok;
  962. for (const price of product.prices) {
  963. expect(price.value).to.be.above(5);
  964. }
  965. }
  966. });
  967. it('should be possible to use limit and a where on a hasMany with a through model with additional includes', async function () {
  968. await this.fixtureA();
  969. const products = await this.models.Product.findAll({
  970. include: [
  971. { model: this.models.Company },
  972. { model: this.models.Tag, as: 'tags', where: { name: ['A', 'B', 'C'] } },
  973. { model: this.models.Price },
  974. ],
  975. limit: 10,
  976. order: [['id', 'ASC']],
  977. });
  978. expect(products.length).to.equal(10);
  979. for (const product of products) {
  980. expect(product.tags.length).to.be.ok;
  981. expect(product.prices.length).to.be.ok;
  982. for (const tag of product.tags) {
  983. expect(['A', 'B', 'C']).to.include(tag.name);
  984. }
  985. }
  986. });
  987. it('should support including date fields, with the correct timezone', async function () {
  988. const User = this.sequelize.define(
  989. 'user',
  990. {
  991. dateField: DataTypes.DATE,
  992. },
  993. { timestamps: false, schema: 'account' },
  994. );
  995. const Group = this.sequelize.define(
  996. 'group',
  997. {
  998. dateField: DataTypes.DATE,
  999. },
  1000. { timestamps: false, schema: 'account' },
  1001. );
  1002. User.belongsToMany(Group, { through: 'group_user' });
  1003. Group.belongsToMany(User, { through: 'group_user' });
  1004. await this.sequelize.sync();
  1005. const user = await User.create({ dateField: Date.UTC(2014, 1, 20) });
  1006. const group = await Group.create({ dateField: Date.UTC(2014, 1, 20) });
  1007. await user.addGroup(group);
  1008. const users = await User.findAll({
  1009. where: {
  1010. id: user.id,
  1011. },
  1012. include: [Group],
  1013. });
  1014. expect(users[0].dateField.getTime()).to.equal(Date.UTC(2014, 1, 20));
  1015. expect(users[0].groups[0].dateField.getTime()).to.equal(Date.UTC(2014, 1, 20));
  1016. });
  1017. });
  1018. describe('findOne', () => {
  1019. it('should work with schemas', async function () {
  1020. const UserModel = this.sequelize.define(
  1021. 'User',
  1022. {
  1023. Id: {
  1024. type: DataTypes.INTEGER,
  1025. primaryKey: true,
  1026. },
  1027. Name: DataTypes.STRING,
  1028. UserType: DataTypes.INTEGER,
  1029. Email: DataTypes.STRING,
  1030. PasswordHash: DataTypes.STRING,
  1031. Enabled: {
  1032. type: DataTypes.BOOLEAN,
  1033. },
  1034. CreatedDatetime: DataTypes.DATE,
  1035. UpdatedDatetime: DataTypes.DATE,
  1036. },
  1037. {
  1038. schema: 'hero',
  1039. tableName: 'User',
  1040. timestamps: false,
  1041. },
  1042. );
  1043. const UserIdColumn = { type: DataTypes.INTEGER, references: { model: UserModel, key: 'Id' } };
  1044. const ResumeModel = this.sequelize.define(
  1045. 'Resume',
  1046. {
  1047. Id: {
  1048. type: DataTypes.INTEGER,
  1049. primaryKey: true,
  1050. },
  1051. UserId: UserIdColumn,
  1052. Name: DataTypes.STRING,
  1053. Contact: DataTypes.STRING,
  1054. School: DataTypes.STRING,
  1055. WorkingAge: DataTypes.STRING,
  1056. Description: DataTypes.STRING,
  1057. PostType: DataTypes.INTEGER,
  1058. RefreshDatetime: DataTypes.DATE,
  1059. CreatedDatetime: DataTypes.DATE,
  1060. },
  1061. {
  1062. schema: 'hero',
  1063. tableName: 'resume',
  1064. timestamps: false,
  1065. },
  1066. );
  1067. UserModel.hasOne(ResumeModel, {
  1068. foreignKey: 'UserId',
  1069. as: 'Resume',
  1070. });
  1071. ResumeModel.belongsTo(UserModel, {
  1072. foreignKey: 'UserId',
  1073. });
  1074. await this.sequelize.createSchema('hero');
  1075. await this.sequelize.sync({ force: true });
  1076. await UserModel.findOne({
  1077. where: {
  1078. Id: 1,
  1079. },
  1080. include: [
  1081. {
  1082. model: ResumeModel,
  1083. as: 'Resume',
  1084. },
  1085. ],
  1086. });
  1087. });
  1088. });
  1089. });