findAll.test.js 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125
  1. 'use strict';
  2. const partition = require('lodash/partition');
  3. const upperFirst = require('lodash/upperFirst');
  4. const chai = require('chai');
  5. const expect = chai.expect;
  6. const Support = require('../support');
  7. const { DataTypes, Op } = require('@sequelize/core');
  8. const promiseProps = require('p-props');
  9. function sortById(a, b) {
  10. return a.id < b.id ? -1 : 1;
  11. }
  12. describe(Support.getTestDialectTeaser('Include'), () => {
  13. describe('findAll', () => {
  14. beforeEach(function () {
  15. this.fixtureA = async function () {
  16. const User = this.sequelize.define('User', {});
  17. const Company = this.sequelize.define('Company', {
  18. name: DataTypes.STRING,
  19. });
  20. const Product = this.sequelize.define('Product', {
  21. title: DataTypes.STRING,
  22. });
  23. const Tag = this.sequelize.define('Tag', {
  24. name: DataTypes.STRING,
  25. });
  26. const Price = this.sequelize.define('Price', {
  27. value: DataTypes.FLOAT,
  28. });
  29. const Customer = this.sequelize.define('Customer', {
  30. name: DataTypes.STRING,
  31. });
  32. const Group = this.sequelize.define('Group', {
  33. name: DataTypes.STRING,
  34. });
  35. const GroupMember = this.sequelize.define('GroupMember', {});
  36. const Rank = this.sequelize.define('Rank', {
  37. name: DataTypes.STRING,
  38. canInvite: {
  39. type: DataTypes.INTEGER,
  40. defaultValue: 0,
  41. },
  42. canRemove: {
  43. type: DataTypes.INTEGER,
  44. defaultValue: 0,
  45. },
  46. canPost: {
  47. type: DataTypes.INTEGER,
  48. defaultValue: 0,
  49. },
  50. });
  51. this.models = {
  52. User,
  53. Company,
  54. Product,
  55. Tag,
  56. Price,
  57. Customer,
  58. Group,
  59. GroupMember,
  60. Rank,
  61. };
  62. User.hasMany(Product);
  63. Product.belongsTo(User);
  64. Product.belongsToMany(Tag, { through: 'product_tag' });
  65. Tag.belongsToMany(Product, { through: 'product_tag' });
  66. Product.belongsTo(Tag, { as: 'Category' });
  67. Product.belongsTo(Company);
  68. Product.hasMany(Price);
  69. Price.belongsTo(Product);
  70. User.hasMany(GroupMember, { as: 'Memberships' });
  71. GroupMember.belongsTo(User);
  72. GroupMember.belongsTo(Rank);
  73. GroupMember.belongsTo(Group);
  74. Group.hasMany(GroupMember, { as: 'Memberships' });
  75. await this.sequelize.sync({ force: true });
  76. await Group.bulkCreate([
  77. { name: 'Developers' },
  78. { name: 'Designers' },
  79. { name: 'Managers' },
  80. ]);
  81. const groups = await Group.findAll();
  82. await Company.bulkCreate([
  83. { name: 'Sequelize' },
  84. { name: 'Coca Cola' },
  85. { name: 'Bonanza' },
  86. { name: 'NYSE' },
  87. { name: 'Coshopr' },
  88. ]);
  89. const companies = await Company.findAll();
  90. await Rank.bulkCreate([
  91. { name: 'Admin', canInvite: 1, canRemove: 1, canPost: 1 },
  92. { name: 'Trustee', canInvite: 1, canRemove: 0, canPost: 1 },
  93. { name: 'Member', canInvite: 1, canRemove: 0, canPost: 0 },
  94. ]);
  95. const ranks = await Rank.findAll();
  96. await Tag.bulkCreate([
  97. { name: 'A' },
  98. { name: 'B' },
  99. { name: 'C' },
  100. { name: 'D' },
  101. { name: 'E' },
  102. ]);
  103. const tags = await Tag.findAll();
  104. for (const i of [0, 1, 2, 3, 4]) {
  105. const user = await User.create();
  106. await Product.bulkCreate([
  107. { title: 'Chair' },
  108. { title: 'Desk' },
  109. { title: 'Bed' },
  110. { title: 'Pen' },
  111. { title: 'Monitor' },
  112. ]);
  113. const products = await Product.findAll();
  114. const groupMembers = [
  115. { groupId: groups[0].id, rankId: ranks[0].id },
  116. { groupId: groups[1].id, rankId: ranks[2].id },
  117. ];
  118. if (i < 3) {
  119. groupMembers.push({ groupId: groups[2].id, rankId: ranks[1].id });
  120. }
  121. await Promise.all([
  122. GroupMember.bulkCreate(groupMembers),
  123. user.setProducts([products[i * 5 + 0], products[i * 5 + 1], products[i * 5 + 3]]),
  124. products[i * 5 + 0].setTags([tags[0], tags[2]]),
  125. products[i * 5 + 1].setTags([tags[1]]),
  126. products[i * 5 + 0].setCategory(tags[1]),
  127. products[i * 5 + 2].setTags([tags[0]]),
  128. products[i * 5 + 3].setTags([tags[0]]),
  129. products[i * 5 + 0].setCompany(companies[4]),
  130. products[i * 5 + 1].setCompany(companies[3]),
  131. products[i * 5 + 2].setCompany(companies[2]),
  132. products[i * 5 + 3].setCompany(companies[1]),
  133. products[i * 5 + 4].setCompany(companies[0]),
  134. Price.bulkCreate([
  135. { productId: products[i * 5 + 0].id, value: 5 },
  136. { productId: products[i * 5 + 0].id, value: 10 },
  137. { productId: products[i * 5 + 1].id, value: 5 },
  138. { productId: products[i * 5 + 1].id, value: 10 },
  139. { productId: products[i * 5 + 1].id, value: 15 },
  140. { productId: products[i * 5 + 1].id, value: 20 },
  141. { productId: products[i * 5 + 2].id, value: 20 },
  142. { productId: products[i * 5 + 3].id, value: 20 },
  143. ]),
  144. ]);
  145. }
  146. };
  147. });
  148. it('should work on a nested set of relations with a where condition in between relations', async function () {
  149. const User = this.sequelize.define('User', {});
  150. const SubscriptionForm = this.sequelize.define('SubscriptionForm', {});
  151. const Collection = this.sequelize.define('Collection', {});
  152. const Category = this.sequelize.define('Category', {});
  153. const SubCategory = this.sequelize.define('SubCategory', {});
  154. const Capital = this.sequelize.define('Capital', {});
  155. User.hasOne(SubscriptionForm, { foreignKey: 'boundUser' });
  156. SubscriptionForm.belongsTo(User, { foreignKey: 'boundUser' });
  157. SubscriptionForm.hasOne(Collection, { foreignKey: 'boundDesigner' });
  158. Collection.belongsTo(SubscriptionForm, { foreignKey: 'boundDesigner' });
  159. SubscriptionForm.belongsTo(Category, { foreignKey: 'boundCategory' });
  160. Category.hasMany(SubscriptionForm, { foreignKey: 'boundCategory' });
  161. Capital.hasMany(Category, { foreignKey: 'boundCapital' });
  162. Category.belongsTo(Capital, { foreignKey: 'boundCapital' });
  163. Category.hasMany(SubCategory, { foreignKey: 'boundCategory' });
  164. SubCategory.belongsTo(Category, { foreignKey: 'boundCategory' });
  165. await this.sequelize.sync({ force: true });
  166. await User.findOne({
  167. include: [
  168. {
  169. model: SubscriptionForm,
  170. include: [
  171. {
  172. model: Collection,
  173. where: {
  174. id: 13,
  175. },
  176. },
  177. {
  178. model: Category,
  179. include: [
  180. {
  181. model: SubCategory,
  182. },
  183. {
  184. model: Capital,
  185. include: [
  186. {
  187. model: Category,
  188. },
  189. ],
  190. },
  191. ],
  192. },
  193. ],
  194. },
  195. ],
  196. });
  197. });
  198. it('should accept nested `where` and `limit` at the same time', async function () {
  199. const Product = this.sequelize.define('Product', {
  200. title: DataTypes.STRING,
  201. });
  202. const Tag = this.sequelize.define('Tag', {
  203. name: DataTypes.STRING,
  204. });
  205. const ProductTag = this.sequelize.define('ProductTag', {
  206. priority: DataTypes.INTEGER,
  207. });
  208. const Set = this.sequelize.define('Set', {
  209. title: DataTypes.STRING,
  210. });
  211. Set.hasMany(Product, { inverse: 'Set', as: 'Products' });
  212. Product.belongsToMany(Tag, { through: ProductTag });
  213. Tag.belongsToMany(Product, { through: ProductTag });
  214. await this.sequelize.sync({ force: true });
  215. await Promise.all([
  216. Set.bulkCreate([{ title: 'office' }]),
  217. Product.bulkCreate([{ title: 'Chair' }, { title: 'Desk' }, { title: 'Dress' }]),
  218. Tag.bulkCreate([{ name: 'A' }, { name: 'B' }, { name: 'C' }]),
  219. ]);
  220. const [sets, products, tags] = await Promise.all([
  221. Set.findAll(),
  222. Product.findAll(),
  223. Tag.findAll(),
  224. ]);
  225. await Promise.all([
  226. sets[0].addProducts([products[0], products[1]]),
  227. products[0]
  228. .addTag(tags[0], { priority: 1 })
  229. .then(() => {
  230. return products[0].addTag(tags[1], { priority: 2 });
  231. })
  232. .then(() => {
  233. return products[0].addTag(tags[2], { priority: 1 });
  234. }),
  235. products[1]
  236. .addTag(tags[1], { priority: 2 })
  237. .then(() => {
  238. return products[2].addTag(tags[1], { priority: 3 });
  239. })
  240. .then(() => {
  241. return products[2].addTag(tags[2], { priority: 0 });
  242. }),
  243. ]);
  244. await Set.findAll({
  245. include: [
  246. {
  247. model: Product,
  248. include: [
  249. {
  250. model: Tag,
  251. where: {
  252. name: 'A',
  253. },
  254. },
  255. ],
  256. },
  257. ],
  258. limit: 1,
  259. });
  260. });
  261. it('should support an include with multiple different association types', async function () {
  262. const User = this.sequelize.define('User', {});
  263. const Product = this.sequelize.define('Product', {
  264. title: DataTypes.STRING,
  265. });
  266. const Tag = this.sequelize.define('Tag', {
  267. name: DataTypes.STRING,
  268. });
  269. const Price = this.sequelize.define('Price', {
  270. value: DataTypes.FLOAT,
  271. });
  272. const Group = this.sequelize.define('Group', {
  273. name: DataTypes.STRING,
  274. });
  275. const GroupMember = this.sequelize.define('GroupMember', {});
  276. const Rank = this.sequelize.define('Rank', {
  277. name: DataTypes.STRING,
  278. canInvite: {
  279. type: DataTypes.INTEGER,
  280. defaultValue: 0,
  281. },
  282. canRemove: {
  283. type: DataTypes.INTEGER,
  284. defaultValue: 0,
  285. },
  286. });
  287. User.hasMany(Product);
  288. Product.belongsTo(User);
  289. Product.belongsToMany(Tag, { through: 'product_tag' });
  290. Tag.belongsToMany(Product, { through: 'product_tag' });
  291. Product.belongsTo(Tag, { as: 'Category' });
  292. Product.hasMany(Price);
  293. Price.belongsTo(Product);
  294. User.hasMany(GroupMember, { as: 'Memberships' });
  295. GroupMember.belongsTo(User);
  296. GroupMember.belongsTo(Rank);
  297. GroupMember.belongsTo(Group);
  298. Group.hasMany(GroupMember, { as: 'Memberships' });
  299. await this.sequelize.sync({ force: true });
  300. const [groups, ranks, tags] = await Promise.all([
  301. Group.bulkCreate([{ name: 'Developers' }, { name: 'Designers' }]).then(() =>
  302. Group.findAll(),
  303. ),
  304. Rank.bulkCreate([
  305. { name: 'Admin', canInvite: 1, canRemove: 1 },
  306. { name: 'Member', canInvite: 1, canRemove: 0 },
  307. ]).then(() => Rank.findAll()),
  308. Tag.bulkCreate([{ name: 'A' }, { name: 'B' }, { name: 'C' }]).then(() => Tag.findAll()),
  309. ]);
  310. for (const i of [0, 1, 2, 3, 4]) {
  311. const [user, products] = await Promise.all([
  312. User.create(),
  313. Product.bulkCreate([{ title: 'Chair' }, { title: 'Desk' }]).then(() => Product.findAll()),
  314. ]);
  315. await Promise.all([
  316. GroupMember.bulkCreate([
  317. { userId: user.id, groupId: groups[0].id, rankId: ranks[0].id },
  318. { userId: user.id, groupId: groups[1].id, rankId: ranks[1].id },
  319. ]),
  320. user.setProducts([products[i * 2 + 0], products[i * 2 + 1]]),
  321. products[i * 2 + 0].setTags([tags[0], tags[2]]),
  322. products[i * 2 + 1].setTags([tags[1]]),
  323. products[i * 2 + 0].setCategory(tags[1]),
  324. Price.bulkCreate([
  325. { productId: products[i * 2 + 0].id, value: 5 },
  326. { productId: products[i * 2 + 0].id, value: 10 },
  327. { productId: products[i * 2 + 1].id, value: 5 },
  328. { productId: products[i * 2 + 1].id, value: 10 },
  329. { productId: products[i * 2 + 1].id, value: 15 },
  330. { productId: products[i * 2 + 1].id, value: 20 },
  331. ]),
  332. ]);
  333. const users = await User.findAll({
  334. include: [
  335. {
  336. model: GroupMember,
  337. as: 'Memberships',
  338. include: [Group, Rank],
  339. },
  340. {
  341. model: Product,
  342. include: ['tags', { model: Tag, as: 'Category' }, Price],
  343. },
  344. ],
  345. order: [['id', 'ASC']],
  346. });
  347. for (const user of users) {
  348. user.Memberships.sort(sortById);
  349. expect(user.Memberships.length).to.equal(2);
  350. expect(user.Memberships[0].group.name).to.equal('Developers');
  351. expect(user.Memberships[0].rank.canRemove).to.equal(1);
  352. expect(user.Memberships[1].group.name).to.equal('Designers');
  353. expect(user.Memberships[1].rank.canRemove).to.equal(0);
  354. user.products.sort(sortById);
  355. expect(user.products.length).to.equal(2);
  356. expect(user.products[0].tags.length).to.equal(2);
  357. expect(user.products[1].tags.length).to.equal(1);
  358. expect(user.products[0].Category).to.be.ok;
  359. expect(user.products[1].Category).not.to.be.ok;
  360. expect(user.products[0].prices.length).to.equal(2);
  361. expect(user.products[1].prices.length).to.equal(4);
  362. }
  363. }
  364. });
  365. it('should support many levels of belongsTo', async function () {
  366. const A = this.sequelize.define('a', {});
  367. const B = this.sequelize.define('b', {});
  368. const C = this.sequelize.define('c', {});
  369. const D = this.sequelize.define('d', {});
  370. const E = this.sequelize.define('e', {});
  371. const F = this.sequelize.define('f', {});
  372. const G = this.sequelize.define('g', {});
  373. const H = this.sequelize.define('h', {});
  374. A.belongsTo(B);
  375. B.belongsTo(C);
  376. C.belongsTo(D);
  377. D.belongsTo(E);
  378. E.belongsTo(F);
  379. F.belongsTo(G);
  380. G.belongsTo(H);
  381. await this.sequelize.sync({ force: true });
  382. const [as0, b] = await Promise.all([
  383. A.bulkCreate([{}, {}, {}, {}, {}, {}, {}, {}]).then(() => {
  384. return A.findAll();
  385. }),
  386. (function (singles) {
  387. let promise = Promise.resolve();
  388. let previousInstance;
  389. let b;
  390. for (const model of singles) {
  391. promise = (async () => {
  392. await promise;
  393. const instance = await model.create({});
  394. if (previousInstance) {
  395. await previousInstance[`set${upperFirst(model.name)}`](instance);
  396. previousInstance = instance;
  397. return;
  398. }
  399. previousInstance = b = instance;
  400. })();
  401. }
  402. promise = promise.then(() => {
  403. return b;
  404. });
  405. return promise;
  406. })([B, C, D, E, F, G, H]),
  407. ]);
  408. await Promise.all(
  409. as0.map(a => {
  410. return a.setB(b);
  411. }),
  412. );
  413. const as = await A.findAll({
  414. include: [
  415. {
  416. model: B,
  417. include: [
  418. {
  419. model: C,
  420. include: [
  421. {
  422. model: D,
  423. include: [
  424. {
  425. model: E,
  426. include: [
  427. {
  428. model: F,
  429. include: [
  430. {
  431. model: G,
  432. include: [{ model: H }],
  433. },
  434. ],
  435. },
  436. ],
  437. },
  438. ],
  439. },
  440. ],
  441. },
  442. ],
  443. },
  444. ],
  445. });
  446. expect(as.length).to.be.ok;
  447. for (const a of as) {
  448. expect(a.b.c.d.e.f.g.h).to.be.ok;
  449. }
  450. });
  451. it('should support many levels of belongsTo (with a lower level having a where)', async function () {
  452. const A = this.sequelize.define('a', {});
  453. const B = this.sequelize.define('b', {});
  454. const C = this.sequelize.define('c', {});
  455. const D = this.sequelize.define('d', {});
  456. const E = this.sequelize.define('e', {});
  457. const F = this.sequelize.define('f', {});
  458. const G = this.sequelize.define('g', {
  459. name: DataTypes.STRING,
  460. });
  461. const H = this.sequelize.define('h', {
  462. name: DataTypes.STRING,
  463. });
  464. A.belongsTo(B);
  465. B.belongsTo(C);
  466. C.belongsTo(D);
  467. D.belongsTo(E);
  468. E.belongsTo(F);
  469. F.belongsTo(G);
  470. G.belongsTo(H);
  471. await this.sequelize.sync({ force: true });
  472. const [as0, b] = await Promise.all([
  473. A.bulkCreate([{}, {}, {}, {}, {}, {}, {}, {}]).then(() => {
  474. return A.findAll();
  475. }),
  476. (function (singles) {
  477. let promise = Promise.resolve();
  478. let previousInstance;
  479. let b;
  480. for (const model of singles) {
  481. const values = {};
  482. if (model.name === 'g') {
  483. values.name = 'yolo';
  484. }
  485. promise = (async () => {
  486. await promise;
  487. const instance = await model.create(values);
  488. if (previousInstance) {
  489. await previousInstance[`set${upperFirst(model.name)}`](instance);
  490. previousInstance = instance;
  491. return;
  492. }
  493. previousInstance = b = instance;
  494. })();
  495. }
  496. promise = promise.then(() => {
  497. return b;
  498. });
  499. return promise;
  500. })([B, C, D, E, F, G, H]),
  501. ]);
  502. await Promise.all(
  503. as0.map(a => {
  504. return a.setB(b);
  505. }),
  506. );
  507. const as = await A.findAll({
  508. include: [
  509. {
  510. model: B,
  511. include: [
  512. {
  513. model: C,
  514. include: [
  515. {
  516. model: D,
  517. include: [
  518. {
  519. model: E,
  520. include: [
  521. {
  522. model: F,
  523. include: [
  524. {
  525. model: G,
  526. where: {
  527. name: 'yolo',
  528. },
  529. include: [{ model: H }],
  530. },
  531. ],
  532. },
  533. ],
  534. },
  535. ],
  536. },
  537. ],
  538. },
  539. ],
  540. },
  541. ],
  542. });
  543. expect(as.length).to.be.ok;
  544. for (const a of as) {
  545. expect(a.b.c.d.e.f.g.h).to.be.ok;
  546. }
  547. });
  548. it('should support ordering with only belongsTo includes', async function () {
  549. const User = this.sequelize.define('User', {});
  550. const Item = this.sequelize.define('Item', { test: DataTypes.STRING });
  551. const Order = this.sequelize.define('Order', { position: DataTypes.INTEGER });
  552. User.belongsTo(Item, { as: 'itemA', foreignKey: 'itemA_id' });
  553. User.belongsTo(Item, { as: 'itemB', foreignKey: 'itemB_id' });
  554. User.belongsTo(Order);
  555. await this.sequelize.sync();
  556. const results = await promiseProps({
  557. users: User.bulkCreate([{}, {}, {}]).then(() => {
  558. return User.findAll();
  559. }),
  560. items: Item.bulkCreate([
  561. { test: 'abc' },
  562. { test: 'def' },
  563. { test: 'ghi' },
  564. { test: 'jkl' },
  565. ]).then(() => {
  566. return Item.findAll({ order: ['id'] });
  567. }),
  568. orders: Order.bulkCreate([{ position: 2 }, { position: 3 }, { position: 1 }]).then(() => {
  569. return Order.findAll({ order: ['id'] });
  570. }),
  571. });
  572. const user1 = results.users[0];
  573. const user2 = results.users[1];
  574. const user3 = results.users[2];
  575. const item1 = results.items[0];
  576. const item2 = results.items[1];
  577. const item3 = results.items[2];
  578. const item4 = results.items[3];
  579. const order1 = results.orders[0];
  580. const order2 = results.orders[1];
  581. const order3 = results.orders[2];
  582. await Promise.all([
  583. user1.setItemA(item1),
  584. user1.setItemB(item2),
  585. user1.setOrder(order3),
  586. user2.setItemA(item3),
  587. user2.setItemB(item4),
  588. user2.setOrder(order2),
  589. user3.setItemA(item1),
  590. user3.setItemB(item4),
  591. user3.setOrder(order1),
  592. ]);
  593. const as = await User.findAll({
  594. include: [
  595. { model: Item, as: 'itemA', where: { test: 'abc' } },
  596. { model: Item, as: 'itemB' },
  597. Order,
  598. ],
  599. order: [[Order, 'position']],
  600. });
  601. expect(as.length).to.eql(2);
  602. expect(as[0].itemA.test).to.eql('abc');
  603. expect(as[1].itemA.test).to.eql('abc');
  604. expect(as[0].order.position).to.eql(1);
  605. expect(as[1].order.position).to.eql(2);
  606. });
  607. it('should include attributes from through models', async function () {
  608. const Product = this.sequelize.define('Product', {
  609. title: DataTypes.STRING,
  610. });
  611. const Tag = this.sequelize.define('Tag', {
  612. name: DataTypes.STRING,
  613. });
  614. const ProductTag = this.sequelize.define('ProductTag', {
  615. priority: DataTypes.INTEGER,
  616. });
  617. Product.belongsToMany(Tag, { through: ProductTag });
  618. Tag.belongsToMany(Product, { through: ProductTag });
  619. await this.sequelize.sync({ force: true });
  620. const results = await promiseProps({
  621. products: Product.bulkCreate([
  622. { title: 'Chair' },
  623. { title: 'Desk' },
  624. { title: 'Dress' },
  625. ]).then(() => {
  626. return Product.findAll();
  627. }),
  628. tags: Tag.bulkCreate([{ name: 'A' }, { name: 'B' }, { name: 'C' }]).then(() => {
  629. return Tag.findAll();
  630. }),
  631. });
  632. await Promise.all([
  633. results.products[0].addTag(results.tags[0], { through: { priority: 1 } }),
  634. results.products[0].addTag(results.tags[1], { through: { priority: 2 } }),
  635. results.products[1].addTag(results.tags[1], { through: { priority: 1 } }),
  636. results.products[2].addTag(results.tags[0], { through: { priority: 3 } }),
  637. results.products[2].addTag(results.tags[1], { through: { priority: 1 } }),
  638. results.products[2].addTag(results.tags[2], { through: { priority: 2 } }),
  639. ]);
  640. const products = await Product.findAll({
  641. include: [{ model: Tag }],
  642. order: [
  643. ['id', 'ASC'],
  644. [Tag, 'id', 'ASC'],
  645. ],
  646. });
  647. expect(products[0].tags[0].ProductTag.priority).to.equal(1);
  648. expect(products[0].tags[1].ProductTag.priority).to.equal(2);
  649. expect(products[1].tags[0].ProductTag.priority).to.equal(1);
  650. expect(products[2].tags[0].ProductTag.priority).to.equal(3);
  651. expect(products[2].tags[1].ProductTag.priority).to.equal(1);
  652. expect(products[2].tags[2].ProductTag.priority).to.equal(2);
  653. });
  654. it('should support a required belongsTo include', async function () {
  655. const User = this.sequelize.define('User', {});
  656. const Group = this.sequelize.define('Group', {});
  657. User.belongsTo(Group);
  658. await this.sequelize.sync({ force: true });
  659. const results = await promiseProps({
  660. groups: Group.bulkCreate([{}, {}]).then(() => {
  661. return Group.findAll();
  662. }),
  663. users: User.bulkCreate([{}, {}, {}]).then(() => {
  664. return User.findAll();
  665. }),
  666. });
  667. await results.users[2].setGroup(results.groups[1]);
  668. const users = await User.findAll({
  669. include: [{ model: Group, required: true }],
  670. });
  671. expect(users.length).to.equal(1);
  672. expect(users[0].group).to.be.ok;
  673. });
  674. it('should be possible to extend the on clause with a where option on a belongsTo include', async function () {
  675. const User = this.sequelize.define('User', {});
  676. const Group = this.sequelize.define('Group', {
  677. name: DataTypes.STRING,
  678. });
  679. User.belongsTo(Group);
  680. await this.sequelize.sync({ force: true });
  681. const results = await promiseProps({
  682. groups: Group.bulkCreate([{ name: 'A' }, { name: 'B' }]).then(() => {
  683. return Group.findAll();
  684. }),
  685. users: User.bulkCreate([{}, {}]).then(() => {
  686. return User.findAll();
  687. }),
  688. });
  689. await Promise.all([
  690. results.users[0].setGroup(results.groups[1]),
  691. results.users[1].setGroup(results.groups[0]),
  692. ]);
  693. const users = await User.findAll({
  694. include: [{ model: Group, where: { name: 'A' } }],
  695. });
  696. expect(users.length).to.equal(1);
  697. expect(users[0].group).to.be.ok;
  698. expect(users[0].group.name).to.equal('A');
  699. });
  700. it('should be possible to extend the on clause with a where option on a belongsTo include', async function () {
  701. const User = this.sequelize.define('User', {});
  702. const Group = this.sequelize.define('Group', {
  703. name: DataTypes.STRING,
  704. });
  705. User.belongsTo(Group);
  706. await this.sequelize.sync({ force: true });
  707. const results = await promiseProps({
  708. groups: Group.bulkCreate([{ name: 'A' }, { name: 'B' }]).then(() => {
  709. return Group.findAll();
  710. }),
  711. users: User.bulkCreate([{}, {}]).then(() => {
  712. return User.findAll();
  713. }),
  714. });
  715. await Promise.all([
  716. results.users[0].setGroup(results.groups[1]),
  717. results.users[1].setGroup(results.groups[0]),
  718. ]);
  719. const users = await User.findAll({
  720. include: [{ model: Group, required: true }],
  721. });
  722. for (const user of users) {
  723. expect(user.group).to.be.ok;
  724. }
  725. });
  726. it('should be possible to define a belongsTo include as required with child hasMany not required', async function () {
  727. const Address = this.sequelize.define('Address', { active: DataTypes.BOOLEAN });
  728. const Street = this.sequelize.define('Street', { active: DataTypes.BOOLEAN });
  729. const User = this.sequelize.define('User', { username: DataTypes.STRING });
  730. // Associate
  731. User.belongsTo(Address, { foreignKey: 'addressId' });
  732. Address.hasMany(User, { foreignKey: 'addressId' });
  733. Address.belongsTo(Street, { foreignKey: 'streetId' });
  734. Street.hasMany(Address, { foreignKey: 'streetId' });
  735. // Sync
  736. await this.sequelize.sync({ force: true });
  737. const street = await Street.create({ active: true });
  738. const address = await Address.create({ active: true, streetId: street.id });
  739. await User.create({ username: 'John', addressId: address.id });
  740. const john = await User.findOne({
  741. where: { username: 'John' },
  742. include: [
  743. {
  744. model: Address,
  745. required: true,
  746. where: {
  747. active: true,
  748. },
  749. include: [
  750. {
  751. model: Street,
  752. },
  753. ],
  754. },
  755. ],
  756. });
  757. expect(john.address).to.be.ok;
  758. expect(john.address.street).to.be.ok;
  759. });
  760. it('should be possible to define a belongsTo include as required with child hasMany with limit', async function () {
  761. const User = this.sequelize.define('User', {});
  762. const Group = this.sequelize.define('Group', {
  763. name: DataTypes.STRING,
  764. });
  765. const Category = this.sequelize.define('Category', {
  766. category: DataTypes.STRING,
  767. });
  768. User.belongsTo(Group);
  769. Group.hasMany(Category);
  770. await this.sequelize.sync({ force: true });
  771. const results = await promiseProps({
  772. groups: Group.bulkCreate([{ name: 'A' }, { name: 'B' }]).then(() => {
  773. return Group.findAll();
  774. }),
  775. users: User.bulkCreate([{}, {}]).then(() => {
  776. return User.findAll();
  777. }),
  778. categories: Category.bulkCreate([{}, {}]).then(() => {
  779. return Category.findAll();
  780. }),
  781. });
  782. await Promise.all([
  783. results.users[0].setGroup(results.groups[1]),
  784. results.users[1].setGroup(results.groups[0]),
  785. Promise.all(
  786. results.groups.map(group => {
  787. return group.setCategories(results.categories);
  788. }),
  789. ),
  790. ]);
  791. const users = await User.findAll({
  792. include: [
  793. {
  794. model: Group,
  795. required: true,
  796. include: [{ model: Category }],
  797. },
  798. ],
  799. limit: 1,
  800. });
  801. expect(users.length).to.equal(1);
  802. for (const user of users) {
  803. expect(user.group).to.be.ok;
  804. expect(user.group.categories).to.be.ok;
  805. }
  806. });
  807. it('should be possible to define a belongsTo include as required with child hasMany with limit and aliases', async function () {
  808. const User = this.sequelize.define('User', {});
  809. const Group = this.sequelize.define('Group', {
  810. name: DataTypes.STRING,
  811. });
  812. const Category = this.sequelize.define('Category', {
  813. category: DataTypes.STRING,
  814. });
  815. User.belongsTo(Group, { as: 'Team' });
  816. Group.hasMany(Category, { as: 'Tags' });
  817. await this.sequelize.sync({ force: true });
  818. const results = await promiseProps({
  819. groups: Group.bulkCreate([{ name: 'A' }, { name: 'B' }]).then(() => {
  820. return Group.findAll();
  821. }),
  822. users: User.bulkCreate([{}, {}]).then(() => {
  823. return User.findAll();
  824. }),
  825. categories: Category.bulkCreate([{}, {}]).then(() => {
  826. return Category.findAll();
  827. }),
  828. });
  829. await Promise.all([
  830. results.users[0].setTeam(results.groups[1]),
  831. results.users[1].setTeam(results.groups[0]),
  832. Promise.all(
  833. results.groups.map(group => {
  834. return group.setTags(results.categories);
  835. }),
  836. ),
  837. ]);
  838. const users = await User.findAll({
  839. include: [
  840. {
  841. model: Group,
  842. required: true,
  843. as: 'Team',
  844. include: [{ model: Category, as: 'Tags' }],
  845. },
  846. ],
  847. limit: 1,
  848. });
  849. expect(users.length).to.equal(1);
  850. for (const user of users) {
  851. expect(user.Team).to.be.ok;
  852. expect(user.Team.Tags).to.be.ok;
  853. }
  854. });
  855. it('should be possible to define a belongsTo include as required with child hasMany which is not required with limit', async function () {
  856. const User = this.sequelize.define('User', {});
  857. const Group = this.sequelize.define('Group', {
  858. name: DataTypes.STRING,
  859. });
  860. const Category = this.sequelize.define('Category', {
  861. category: DataTypes.STRING,
  862. });
  863. User.belongsTo(Group);
  864. Group.hasMany(Category);
  865. await this.sequelize.sync({ force: true });
  866. const results = await promiseProps({
  867. groups: Group.bulkCreate([{ name: 'A' }, { name: 'B' }]).then(() => {
  868. return Group.findAll();
  869. }),
  870. users: User.bulkCreate([{}, {}]).then(() => {
  871. return User.findAll();
  872. }),
  873. categories: Category.bulkCreate([{}, {}]).then(() => {
  874. return Category.findAll();
  875. }),
  876. });
  877. await Promise.all([
  878. results.users[0].setGroup(results.groups[1]),
  879. results.users[1].setGroup(results.groups[0]),
  880. Promise.all(
  881. results.groups.map(group => {
  882. return group.setCategories(results.categories);
  883. }),
  884. ),
  885. ]);
  886. const users = await User.findAll({
  887. include: [
  888. {
  889. model: Group,
  890. required: true,
  891. include: [{ model: Category, required: false }],
  892. },
  893. ],
  894. limit: 1,
  895. });
  896. expect(users.length).to.equal(1);
  897. for (const user of users) {
  898. expect(user.group).to.be.ok;
  899. expect(user.group.categories).to.be.ok;
  900. }
  901. });
  902. it('should be possible to extend the on clause with a where option on a hasOne include', async function () {
  903. const User = this.sequelize.define('User', {});
  904. const Project = this.sequelize.define('Project', {
  905. title: DataTypes.STRING,
  906. });
  907. User.hasOne(Project, { as: 'LeaderOf' });
  908. await this.sequelize.sync({ force: true });
  909. const results = await promiseProps({
  910. projects: Project.bulkCreate([{ title: 'Alpha' }, { title: 'Beta' }]).then(() => {
  911. return Project.findAll();
  912. }),
  913. users: User.bulkCreate([{}, {}]).then(() => {
  914. return User.findAll();
  915. }),
  916. });
  917. await Promise.all([
  918. results.users[1].setLeaderOf(results.projects[1]),
  919. results.users[0].setLeaderOf(results.projects[0]),
  920. ]);
  921. const users = await User.findAll({
  922. include: [{ model: Project, as: 'LeaderOf', where: { title: 'Beta' } }],
  923. });
  924. expect(users.length).to.equal(1);
  925. expect(users[0].LeaderOf).to.be.ok;
  926. expect(users[0].LeaderOf.title).to.equal('Beta');
  927. });
  928. it('should be possible to extend the on clause with a where option on a hasMany include with a through model', async function () {
  929. const Product = this.sequelize.define('Product', {
  930. title: DataTypes.STRING,
  931. });
  932. const Tag = this.sequelize.define('Tag', {
  933. name: DataTypes.STRING,
  934. });
  935. const ProductTag = this.sequelize.define('ProductTag', {
  936. priority: DataTypes.INTEGER,
  937. });
  938. Product.belongsToMany(Tag, { through: ProductTag });
  939. Tag.belongsToMany(Product, { through: ProductTag });
  940. await this.sequelize.sync({ force: true });
  941. const results = await promiseProps({
  942. products: Product.bulkCreate([
  943. { title: 'Chair' },
  944. { title: 'Desk' },
  945. { title: 'Dress' },
  946. ]).then(() => {
  947. return Product.findAll();
  948. }),
  949. tags: Tag.bulkCreate([{ name: 'A' }, { name: 'B' }, { name: 'C' }]).then(() => {
  950. return Tag.findAll();
  951. }),
  952. });
  953. await Promise.all([
  954. results.products[0].addTag(results.tags[0], { priority: 1 }),
  955. results.products[0].addTag(results.tags[1], { priority: 2 }),
  956. results.products[1].addTag(results.tags[1], { priority: 1 }),
  957. results.products[2].addTag(results.tags[0], { priority: 3 }),
  958. results.products[2].addTag(results.tags[1], { priority: 1 }),
  959. results.products[2].addTag(results.tags[2], { priority: 2 }),
  960. ]);
  961. const products = await Product.findAll({
  962. include: [{ model: Tag, where: { name: 'C' } }],
  963. });
  964. expect(products.length).to.equal(1);
  965. expect(products[0].tags.length).to.equal(1);
  966. });
  967. it('should be possible to extend the on clause with a where option on nested includes', async function () {
  968. const User = this.sequelize.define('User', {
  969. name: DataTypes.STRING,
  970. });
  971. const Product = this.sequelize.define('Product', {
  972. title: DataTypes.STRING,
  973. });
  974. const Tag = this.sequelize.define('Tag', {
  975. name: DataTypes.STRING,
  976. });
  977. const Price = this.sequelize.define('Price', {
  978. value: DataTypes.FLOAT,
  979. });
  980. const Group = this.sequelize.define('Group', {
  981. name: DataTypes.STRING,
  982. });
  983. const GroupMember = this.sequelize.define('GroupMember', {});
  984. const Rank = this.sequelize.define('Rank', {
  985. name: DataTypes.STRING,
  986. canInvite: {
  987. type: DataTypes.INTEGER,
  988. defaultValue: 0,
  989. },
  990. canRemove: {
  991. type: DataTypes.INTEGER,
  992. defaultValue: 0,
  993. },
  994. });
  995. User.hasMany(Product);
  996. Product.belongsTo(User);
  997. Product.belongsToMany(Tag, { through: 'product_tag' });
  998. Tag.belongsToMany(Product, { through: 'product_tag' });
  999. Product.belongsTo(Tag, { as: 'Category' });
  1000. Product.hasMany(Price);
  1001. Price.belongsTo(Product);
  1002. User.hasMany(GroupMember, { as: 'Memberships' });
  1003. GroupMember.belongsTo(User);
  1004. GroupMember.belongsTo(Rank);
  1005. GroupMember.belongsTo(Group);
  1006. Group.hasMany(GroupMember, { as: 'Memberships' });
  1007. await this.sequelize.sync({ force: true });
  1008. const [groups, ranks, tags] = await Promise.all([
  1009. Group.bulkCreate([{ name: 'Developers' }, { name: 'Designers' }]).then(() =>
  1010. Group.findAll(),
  1011. ),
  1012. Rank.bulkCreate([
  1013. { name: 'Admin', canInvite: 1, canRemove: 1 },
  1014. { name: 'Member', canInvite: 1, canRemove: 0 },
  1015. ]).then(() => Rank.findAll()),
  1016. Tag.bulkCreate([{ name: 'A' }, { name: 'B' }, { name: 'C' }]).then(() => Tag.findAll()),
  1017. ]);
  1018. for (const i of [0, 1, 2, 3, 4]) {
  1019. const user = await User.create({ name: 'FooBarzz' });
  1020. await Product.bulkCreate([{ title: 'Chair' }, { title: 'Desk' }]);
  1021. const products = await Product.findAll();
  1022. await Promise.all([
  1023. GroupMember.bulkCreate([
  1024. { userId: user.id, groupId: groups[0].id, rankId: ranks[0].id },
  1025. { userId: user.id, groupId: groups[1].id, rankId: ranks[1].id },
  1026. ]),
  1027. user.setProducts([products[i * 2 + 0], products[i * 2 + 1]]),
  1028. products[i * 2 + 0].setTags([tags[0], tags[2]]),
  1029. products[i * 2 + 1].setTags([tags[1]]),
  1030. products[i * 2 + 0].setCategory(tags[1]),
  1031. Price.bulkCreate([
  1032. { productId: products[i * 2 + 0].id, value: 5 },
  1033. { productId: products[i * 2 + 0].id, value: 10 },
  1034. { productId: products[i * 2 + 1].id, value: 5 },
  1035. { productId: products[i * 2 + 1].id, value: 10 },
  1036. { productId: products[i * 2 + 1].id, value: 15 },
  1037. { productId: products[i * 2 + 1].id, value: 20 },
  1038. ]),
  1039. ]);
  1040. }
  1041. const users = await User.findAll({
  1042. include: [
  1043. {
  1044. model: GroupMember,
  1045. as: 'Memberships',
  1046. include: [Group, { model: Rank, where: { name: 'Admin' } }],
  1047. },
  1048. {
  1049. model: Product,
  1050. include: [
  1051. 'tags',
  1052. { model: Tag, as: 'Category' },
  1053. {
  1054. model: Price,
  1055. where: {
  1056. value: {
  1057. [Op.gt]: 15,
  1058. },
  1059. },
  1060. },
  1061. ],
  1062. },
  1063. ],
  1064. order: [['id', 'ASC']],
  1065. });
  1066. for (const user of users) {
  1067. expect(user.Memberships.length).to.equal(1);
  1068. expect(user.Memberships[0].rank.name).to.equal('Admin');
  1069. expect(user.products.length).to.equal(1);
  1070. expect(user.products[0].prices.length).to.equal(1);
  1071. }
  1072. });
  1073. it('should be possible to use limit and a where with a belongsTo include', async function () {
  1074. const User = this.sequelize.define('User', {});
  1075. const Group = this.sequelize.define('Group', {
  1076. name: DataTypes.STRING,
  1077. });
  1078. User.belongsTo(Group);
  1079. await this.sequelize.sync({ force: true });
  1080. const results = await promiseProps({
  1081. groups: Group.bulkCreate([{ name: 'A' }, { name: 'B' }]).then(() => {
  1082. return Group.findAll();
  1083. }),
  1084. users: User.bulkCreate([{}, {}, {}, {}]).then(() => {
  1085. return User.findAll();
  1086. }),
  1087. });
  1088. await Promise.all([
  1089. results.users[0].setGroup(results.groups[0]),
  1090. results.users[1].setGroup(results.groups[0]),
  1091. results.users[2].setGroup(results.groups[0]),
  1092. results.users[3].setGroup(results.groups[1]),
  1093. ]);
  1094. const users = await User.findAll({
  1095. include: [{ model: Group, where: { name: 'A' } }],
  1096. limit: 2,
  1097. });
  1098. expect(users.length).to.equal(2);
  1099. for (const user of users) {
  1100. expect(user.group.name).to.equal('A');
  1101. }
  1102. });
  1103. it('should be possible use limit, attributes and a where on a belongsTo with additional hasMany includes', async function () {
  1104. await this.fixtureA();
  1105. const products = await this.models.Product.findAll({
  1106. attributes: ['id', 'title'],
  1107. include: [
  1108. { model: this.models.Company, where: { name: 'NYSE' } },
  1109. { model: this.models.Tag, as: 'tags' },
  1110. { model: this.models.Price },
  1111. ],
  1112. limit: 3,
  1113. order: [[this.sequelize.col(`${this.models.Product.name}.id`), 'ASC']],
  1114. });
  1115. expect(products.length).to.equal(3);
  1116. for (const product of products) {
  1117. expect(product.company.name).to.equal('NYSE');
  1118. expect(product.tags.length).to.be.ok;
  1119. expect(product.prices.length).to.be.ok;
  1120. }
  1121. });
  1122. it('should be possible to have the primary key in attributes', async function () {
  1123. const Parent = this.sequelize.define('Parent', {});
  1124. const Child1 = this.sequelize.define('Child1', {});
  1125. Parent.hasMany(Child1);
  1126. Child1.belongsTo(Parent);
  1127. await this.sequelize.sync({ force: true });
  1128. const [parent0, child] = await Promise.all([Parent.create(), Child1.create()]);
  1129. await parent0.addChild1(child);
  1130. const parent = parent0;
  1131. await Child1.findOne({
  1132. include: [
  1133. {
  1134. model: Parent,
  1135. attributes: ['id'], // This causes a duplicated entry in the query
  1136. where: {
  1137. id: parent.id,
  1138. },
  1139. },
  1140. ],
  1141. });
  1142. });
  1143. it('should be possible to turn off the attributes for the through table', async function () {
  1144. await this.fixtureA();
  1145. const products = await this.models.Product.findAll({
  1146. attributes: ['title'],
  1147. include: [
  1148. { model: this.models.Tag, as: 'tags', through: { attributes: [] }, required: true },
  1149. ],
  1150. });
  1151. for (const product of products) {
  1152. expect(product.tags.length).to.be.ok;
  1153. for (const tag of product.tags) {
  1154. expect(tag.get().productTags).not.to.be.ok;
  1155. }
  1156. }
  1157. });
  1158. it('should be possible to select on columns inside a through table', async function () {
  1159. await this.fixtureA();
  1160. const products = await this.models.Product.findAll({
  1161. attributes: ['title'],
  1162. include: [
  1163. {
  1164. model: this.models.Tag,
  1165. as: 'tags',
  1166. through: {
  1167. where: {
  1168. productId: 3,
  1169. },
  1170. },
  1171. required: true,
  1172. },
  1173. ],
  1174. });
  1175. expect(products).have.length(1);
  1176. });
  1177. it('should be possible to select on columns inside a through table and a limit', async function () {
  1178. await this.fixtureA();
  1179. const products = await this.models.Product.findAll({
  1180. attributes: ['title'],
  1181. include: [
  1182. {
  1183. model: this.models.Tag,
  1184. as: 'tags',
  1185. through: {
  1186. where: {
  1187. productId: 3,
  1188. },
  1189. },
  1190. required: true,
  1191. },
  1192. ],
  1193. limit: 5,
  1194. });
  1195. expect(products).have.length(1);
  1196. });
  1197. // Test case by @eshell
  1198. it('should be possible not to include the main id in the attributes', async function () {
  1199. const Member = this.sequelize.define('Member', {
  1200. id: {
  1201. type: DataTypes.INTEGER,
  1202. primaryKey: true,
  1203. autoIncrement: true,
  1204. },
  1205. email: {
  1206. type: DataTypes.STRING,
  1207. unique: true,
  1208. allowNull: false,
  1209. validate: {
  1210. isEmail: true,
  1211. notNull: true,
  1212. notEmpty: true,
  1213. },
  1214. },
  1215. password: DataTypes.STRING,
  1216. });
  1217. const Album = this.sequelize.define('Album', {
  1218. id: {
  1219. type: DataTypes.INTEGER,
  1220. primaryKey: true,
  1221. autoIncrement: true,
  1222. },
  1223. title: {
  1224. type: DataTypes.STRING(25),
  1225. allowNull: false,
  1226. },
  1227. });
  1228. Album.belongsTo(Member);
  1229. Member.hasMany(Album);
  1230. await this.sequelize.sync({ force: true });
  1231. const members = [];
  1232. const albums = [];
  1233. const memberCount = 20;
  1234. for (let i = 1; i <= memberCount; i++) {
  1235. members.push({
  1236. id: i,
  1237. email: `email${i}@lmu.com`,
  1238. password: `testing${i}`,
  1239. });
  1240. albums.push({
  1241. title: `Album${i}`,
  1242. memberId: i,
  1243. });
  1244. }
  1245. await Member.bulkCreate(members);
  1246. await Album.bulkCreate(albums);
  1247. const members0 = await Member.findAll({
  1248. attributes: ['email'],
  1249. include: [
  1250. {
  1251. model: Album,
  1252. },
  1253. ],
  1254. });
  1255. expect(members0.length).to.equal(20);
  1256. for (const member of members0) {
  1257. expect(member.get('id')).not.to.be.ok;
  1258. expect(member.albums.length).to.equal(1);
  1259. }
  1260. });
  1261. it('should be possible to use limit and a where on a hasMany with additional includes', async function () {
  1262. await this.fixtureA();
  1263. const products = await this.models.Product.findAll({
  1264. include: [
  1265. { model: this.models.Company },
  1266. { model: this.models.Tag, as: 'tags' },
  1267. {
  1268. model: this.models.Price,
  1269. where: {
  1270. value: { [Op.gt]: 5 },
  1271. },
  1272. },
  1273. ],
  1274. limit: 6,
  1275. order: [['id', 'ASC']],
  1276. });
  1277. expect(products.length).to.equal(6);
  1278. for (const product of products) {
  1279. expect(product.tags.length).to.be.ok;
  1280. expect(product.prices.length).to.be.ok;
  1281. for (const price of product.prices) {
  1282. expect(price.value).to.be.above(5);
  1283. }
  1284. }
  1285. });
  1286. it('should be possible to use limit and a where on a hasMany with a through model with additional includes', async function () {
  1287. await this.fixtureA();
  1288. const products = await this.models.Product.findAll({
  1289. include: [
  1290. { model: this.models.Company },
  1291. { model: this.models.Tag, as: 'tags', where: { name: ['A', 'B', 'C'] } },
  1292. { model: this.models.Price },
  1293. ],
  1294. limit: 10,
  1295. order: [['id', 'ASC']],
  1296. });
  1297. expect(products.length).to.equal(10);
  1298. for (const product of products) {
  1299. expect(product.tags.length).to.be.ok;
  1300. expect(product.prices.length).to.be.ok;
  1301. for (const tag of product.tags) {
  1302. expect(['A', 'B', 'C']).to.include(tag.name);
  1303. }
  1304. }
  1305. });
  1306. it('should support including date fields, with the correct timeszone', async function () {
  1307. const User = this.sequelize.define(
  1308. 'user',
  1309. {
  1310. dateField: DataTypes.DATE,
  1311. },
  1312. { timestamps: false },
  1313. );
  1314. const Group = this.sequelize.define(
  1315. 'group',
  1316. {
  1317. dateField: DataTypes.DATE,
  1318. },
  1319. { timestamps: false },
  1320. );
  1321. User.belongsToMany(Group, { through: 'group_user' });
  1322. Group.belongsToMany(User, { through: 'group_user' });
  1323. await this.sequelize.sync();
  1324. const user = await User.create({ dateField: Date.UTC(2014, 1, 20) });
  1325. const group = await Group.create({ dateField: Date.UTC(2014, 1, 20) });
  1326. await user.addGroup(group);
  1327. const users = await User.findAll({
  1328. where: {
  1329. id: user.id,
  1330. },
  1331. include: [Group],
  1332. });
  1333. expect(users[0].dateField.getTime()).to.equal(Date.UTC(2014, 1, 20));
  1334. expect(users[0].groups[0].dateField.getTime()).to.equal(Date.UTC(2014, 1, 20));
  1335. });
  1336. it('should still pull the main record(s) when an included model is not required and has where restrictions without matches', async function () {
  1337. const A = this.sequelize.define('a', { name: DataTypes.STRING(40) });
  1338. const B = this.sequelize.define('b', { name: DataTypes.STRING(40) });
  1339. A.belongsToMany(B, { through: 'a_b' });
  1340. B.belongsToMany(A, { through: 'a_b' });
  1341. await this.sequelize.sync({ force: true });
  1342. await A.create({
  1343. name: 'Foobar',
  1344. });
  1345. const as = await A.findAll({
  1346. where: { name: 'Foobar' },
  1347. include: [{ model: B, where: { name: 'idontexist' }, required: false }],
  1348. });
  1349. expect(as.length).to.equal(1);
  1350. expect(as[0].get('bs')).deep.equal([]);
  1351. });
  1352. it('should work with paranoid, a main record where, an include where, and a limit', async function () {
  1353. const Post = this.sequelize.define(
  1354. 'post',
  1355. {
  1356. date: DataTypes.DATE,
  1357. public: DataTypes.BOOLEAN,
  1358. },
  1359. {
  1360. paranoid: true,
  1361. },
  1362. );
  1363. const Category = this.sequelize.define('category', {
  1364. slug: DataTypes.STRING,
  1365. });
  1366. Post.hasMany(Category);
  1367. Category.belongsTo(Post);
  1368. await this.sequelize.sync({ force: true });
  1369. const posts0 = await Promise.all([
  1370. Post.create({ public: true }),
  1371. Post.create({ public: true }),
  1372. Post.create({ public: true }),
  1373. Post.create({ public: true }),
  1374. ]);
  1375. await Promise.all(
  1376. posts0.slice(1, 3).map(post => {
  1377. return post.createCategory({ slug: 'food' });
  1378. }),
  1379. );
  1380. const posts = await Post.findAll({
  1381. limit: 2,
  1382. where: {
  1383. public: true,
  1384. },
  1385. include: [
  1386. {
  1387. model: Category,
  1388. where: {
  1389. slug: 'food',
  1390. },
  1391. },
  1392. ],
  1393. });
  1394. expect(posts.length).to.equal(2);
  1395. });
  1396. it('should work on a nested set of required 1:1 relations', async function () {
  1397. const Person = this.sequelize.define('Person', {
  1398. name: {
  1399. type: DataTypes.STRING,
  1400. allowNull: false,
  1401. },
  1402. });
  1403. const UserPerson = this.sequelize.define('UserPerson', {
  1404. personId: {
  1405. type: DataTypes.INTEGER,
  1406. primaryKey: true,
  1407. },
  1408. rank: {
  1409. type: DataTypes.STRING,
  1410. },
  1411. });
  1412. const User = this.sequelize.define('User', {
  1413. userPersonId: {
  1414. type: DataTypes.INTEGER,
  1415. primaryKey: true,
  1416. },
  1417. login: {
  1418. type: DataTypes.STRING,
  1419. unique: true,
  1420. allowNull: false,
  1421. },
  1422. });
  1423. UserPerson.belongsTo(Person, {
  1424. foreignKey: {
  1425. allowNull: false,
  1426. onDelete: 'CASCADE',
  1427. },
  1428. });
  1429. Person.hasOne(UserPerson, {
  1430. foreignKey: {
  1431. allowNull: false,
  1432. onDelete: 'CASCADE',
  1433. },
  1434. });
  1435. User.belongsTo(UserPerson, {
  1436. foreignKey: {
  1437. name: 'userPersonId',
  1438. allowNull: false,
  1439. onDelete: 'CASCADE',
  1440. },
  1441. });
  1442. UserPerson.hasOne(User, {
  1443. foreignKey: {
  1444. name: 'userPersonId',
  1445. allowNull: false,
  1446. onDelete: 'CASCADE',
  1447. },
  1448. });
  1449. await this.sequelize.sync({ force: true });
  1450. await Person.findAll({
  1451. offset: 0,
  1452. limit: 20,
  1453. attributes: ['id', 'name'],
  1454. include: [
  1455. {
  1456. model: UserPerson,
  1457. required: true,
  1458. attributes: ['rank'],
  1459. include: [
  1460. {
  1461. model: User,
  1462. required: true,
  1463. attributes: ['login'],
  1464. },
  1465. ],
  1466. },
  1467. ],
  1468. });
  1469. });
  1470. it('should work with an empty include.where', async function () {
  1471. const User = this.sequelize.define('User', {});
  1472. const Company = this.sequelize.define('Company', {});
  1473. const Group = this.sequelize.define('Group', {});
  1474. User.belongsTo(Company);
  1475. User.belongsToMany(Group, { through: 'UsersGroups' });
  1476. Group.belongsToMany(User, { through: 'UsersGroups' });
  1477. await this.sequelize.sync({ force: true });
  1478. await User.findAll({
  1479. include: [
  1480. { model: Group, where: {} },
  1481. { model: Company, where: {} },
  1482. ],
  1483. });
  1484. });
  1485. it('should be able to order on the main table and a required belongsTo relation with custom tablenames and limit ', async function () {
  1486. const User = this.sequelize.define(
  1487. 'User',
  1488. {
  1489. lastName: DataTypes.STRING,
  1490. },
  1491. { tableName: 'dem_users' },
  1492. );
  1493. const Company = this.sequelize.define(
  1494. 'Company',
  1495. {
  1496. rank: DataTypes.INTEGER,
  1497. },
  1498. { tableName: 'dem_companies' },
  1499. );
  1500. User.belongsTo(Company);
  1501. Company.hasMany(User);
  1502. await this.sequelize.sync({ force: true });
  1503. const [albertsen, zenith, hansen, company1, company2] = await Promise.all([
  1504. User.create({ lastName: 'Albertsen' }),
  1505. User.create({ lastName: 'Zenith' }),
  1506. User.create({ lastName: 'Hansen' }),
  1507. Company.create({ rank: 1 }),
  1508. Company.create({ rank: 2 }),
  1509. ]);
  1510. await Promise.all([
  1511. albertsen.setCompany(company1),
  1512. zenith.setCompany(company2),
  1513. hansen.setCompany(company2),
  1514. ]);
  1515. const users = await User.findAll({
  1516. include: [{ model: Company, required: true }],
  1517. order: [
  1518. [Company, 'rank', 'ASC'],
  1519. ['lastName', 'DESC'],
  1520. ],
  1521. limit: 5,
  1522. });
  1523. expect(users[0].lastName).to.equal('Albertsen');
  1524. expect(users[0].company.rank).to.equal(1);
  1525. expect(users[1].lastName).to.equal('Zenith');
  1526. expect(users[1].company.rank).to.equal(2);
  1527. expect(users[2].lastName).to.equal('Hansen');
  1528. expect(users[2].company.rank).to.equal(2);
  1529. });
  1530. it('should ignore include with attributes: [] (used for aggregates)', async function () {
  1531. const Post = this.sequelize.define('Post', {
  1532. title: DataTypes.STRING,
  1533. });
  1534. const Comment = this.sequelize.define('Comment', {
  1535. content: DataTypes.TEXT,
  1536. });
  1537. Post.Comments = Post.hasMany(Comment, { as: 'comments' });
  1538. await this.sequelize.sync({ force: true });
  1539. await Post.create(
  1540. {
  1541. title: Math.random().toString(),
  1542. comments: [
  1543. { content: Math.random().toString() },
  1544. { content: Math.random().toString() },
  1545. { content: Math.random().toString() },
  1546. ],
  1547. },
  1548. {
  1549. include: [Post.Comments],
  1550. },
  1551. );
  1552. const posts = await Post.findAll({
  1553. attributes: [
  1554. [this.sequelize.fn('COUNT', this.sequelize.col('comments.id')), 'commentCount'],
  1555. ],
  1556. include: [{ association: Post.Comments, attributes: [] }],
  1557. group: ['Post.id'],
  1558. });
  1559. expect(posts.length).to.equal(1);
  1560. const post = posts[0];
  1561. expect(post.get('comments')).not.to.be.ok;
  1562. expect(Number.parseInt(post.get('commentCount'), 10)).to.equal(3);
  1563. });
  1564. it('should ignore include with attributes: [] and through: { attributes: [] } (used for aggregates)', async function () {
  1565. const User = this.sequelize.define('User', {
  1566. name: DataTypes.STRING,
  1567. });
  1568. const Project = this.sequelize.define('Project', {
  1569. title: DataTypes.STRING,
  1570. });
  1571. User.belongsToMany(Project, { as: 'projects', through: 'UserProject' });
  1572. Project.belongsToMany(User, { as: 'users', through: 'UserProject' });
  1573. await this.sequelize.sync({ force: true });
  1574. await User.create(
  1575. {
  1576. name: Math.random().toString(),
  1577. projects: [
  1578. { title: Math.random().toString() },
  1579. { title: Math.random().toString() },
  1580. { title: Math.random().toString() },
  1581. ],
  1582. },
  1583. {
  1584. include: [User.associations.projects],
  1585. },
  1586. );
  1587. const users = await User.findAll({
  1588. attributes: [
  1589. [this.sequelize.fn('COUNT', this.sequelize.col('projects.id')), 'projectsCount'],
  1590. ],
  1591. include: {
  1592. association: User.associations.projects,
  1593. attributes: [],
  1594. through: { attributes: [] },
  1595. },
  1596. group: ['User.id'],
  1597. });
  1598. expect(users.length).to.equal(1);
  1599. const user = users[0];
  1600. expect(user.projects).not.to.be.ok;
  1601. expect(Number.parseInt(user.get('projectsCount'), 10)).to.equal(3);
  1602. });
  1603. it('should not add primary key when including and aggregating with raw: true', async function () {
  1604. const Post = this.sequelize.define('Post', {
  1605. title: DataTypes.STRING,
  1606. });
  1607. const Comment = this.sequelize.define('Comment', {
  1608. content: DataTypes.TEXT,
  1609. });
  1610. Post.Comments = Post.hasMany(Comment, { as: 'comments' });
  1611. await this.sequelize.sync({ force: true });
  1612. await Post.create(
  1613. {
  1614. title: Math.random().toString(),
  1615. comments: [
  1616. { content: Math.random().toString() },
  1617. { content: Math.random().toString() },
  1618. { content: Math.random().toString() },
  1619. ],
  1620. },
  1621. {
  1622. include: [Post.Comments],
  1623. },
  1624. );
  1625. const posts = await Post.findAll({
  1626. attributes: [],
  1627. include: [
  1628. {
  1629. association: Post.Comments,
  1630. attributes: [
  1631. [this.sequelize.fn('COUNT', this.sequelize.col('comments.id')), 'commentCount'],
  1632. ],
  1633. },
  1634. ],
  1635. raw: true,
  1636. });
  1637. expect(posts.length).to.equal(1);
  1638. const post = posts[0];
  1639. expect(post.id).not.to.be.ok;
  1640. expect(Number.parseInt(post['comments.commentCount'], 10)).to.equal(3);
  1641. });
  1642. it('should return posts with nested include with inner join with a m:n association', async function () {
  1643. const User = this.sequelize.define('User', {
  1644. username: {
  1645. type: DataTypes.STRING,
  1646. primaryKey: true,
  1647. },
  1648. });
  1649. const Entity = this.sequelize.define('Entity', {
  1650. entity_id: {
  1651. type: DataTypes.INTEGER,
  1652. autoIncrement: true,
  1653. primaryKey: true,
  1654. },
  1655. creator: {
  1656. type: DataTypes.STRING,
  1657. allowNull: false,
  1658. },
  1659. votes: {
  1660. type: DataTypes.INTEGER,
  1661. allowNull: false,
  1662. defaultValue: 0,
  1663. },
  1664. });
  1665. const Post = this.sequelize.define('Post', {
  1666. post_id: {
  1667. type: DataTypes.INTEGER,
  1668. allowNull: false,
  1669. primaryKey: true,
  1670. },
  1671. });
  1672. const TaggableSentient = this.sequelize.define('TaggableSentient', {
  1673. nametag: {
  1674. type: DataTypes.STRING,
  1675. primaryKey: true,
  1676. },
  1677. });
  1678. Entity.belongsTo(User, { foreignKey: 'creator', targetKey: 'username' });
  1679. Post.belongsTo(Entity, { foreignKey: 'post_id', targetKey: 'entity_id' });
  1680. Entity.belongsToMany(TaggableSentient, {
  1681. as: 'tags',
  1682. through: { model: 'EntityTag', unique: false },
  1683. foreignKey: 'entity_id',
  1684. otherKey: 'tag_name',
  1685. });
  1686. await this.sequelize.sync({ force: true });
  1687. await User.create({ username: 'bob' });
  1688. await TaggableSentient.create({ nametag: 'bob' });
  1689. const entity = await Entity.create({ creator: 'bob' });
  1690. await Promise.all([Post.create({ post_id: entity.entity_id }), entity.addTags('bob')]);
  1691. const posts = await Post.findAll({
  1692. include: [
  1693. {
  1694. model: Entity,
  1695. required: true,
  1696. include: [
  1697. {
  1698. model: User,
  1699. required: true,
  1700. },
  1701. {
  1702. model: TaggableSentient,
  1703. as: 'tags',
  1704. required: true,
  1705. through: {
  1706. where: {
  1707. tag_name: ['bob'],
  1708. },
  1709. },
  1710. },
  1711. ],
  1712. },
  1713. ],
  1714. limit: 5,
  1715. offset: 0,
  1716. });
  1717. expect(posts.length).to.equal(1);
  1718. expect(posts[0].entity.creator).to.equal('bob');
  1719. expect(posts[0].entity.tags.length).to.equal(1);
  1720. expect(posts[0].entity.tags[0].EntityTag.tag_name).to.equal('bob');
  1721. expect(posts[0].entity.tags[0].EntityTag.entity_id).to.equal(posts[0].post_id);
  1722. });
  1723. it('should be able to generate a correct request with inner and outer join', async function () {
  1724. const Customer = this.sequelize.define('customer', {
  1725. name: DataTypes.STRING,
  1726. });
  1727. const ShippingAddress = this.sequelize.define('shippingAddress', {
  1728. address: DataTypes.STRING,
  1729. verified: DataTypes.BOOLEAN,
  1730. });
  1731. const Order = this.sequelize.define('purchaseOrder', {
  1732. description: DataTypes.TEXT,
  1733. });
  1734. const Shipment = this.sequelize.define('shipment', {
  1735. trackingNumber: DataTypes.STRING,
  1736. });
  1737. Customer.hasMany(ShippingAddress);
  1738. ShippingAddress.belongsTo(Customer);
  1739. Customer.hasMany(Order);
  1740. Order.belongsTo(Customer);
  1741. Shipment.belongsTo(Order);
  1742. Order.hasOne(Shipment);
  1743. await this.sequelize.sync({ force: true });
  1744. await Shipment.findOne({
  1745. include: [
  1746. {
  1747. model: Order,
  1748. required: true,
  1749. include: [
  1750. {
  1751. model: Customer,
  1752. include: [
  1753. {
  1754. model: ShippingAddress,
  1755. where: { verified: true },
  1756. },
  1757. ],
  1758. },
  1759. ],
  1760. },
  1761. ],
  1762. });
  1763. });
  1764. it('should be able to generate a correct request for entity with 1:n and m:1 associations and limit', async function () {
  1765. await this.fixtureA();
  1766. const products = await this.models.Product.findAll({
  1767. attributes: ['title'],
  1768. include: [{ model: this.models.User }, { model: this.models.Price }],
  1769. limit: 10,
  1770. });
  1771. expect(products).to.be.an('array');
  1772. expect(products).to.be.lengthOf(10);
  1773. for (const product of products) {
  1774. expect(product.title).to.be.a('string');
  1775. // checking that internally added fields used to handle 'BelongsTo' associations are not leaked to result
  1776. expect(product.userId).to.equal(undefined);
  1777. // checking that included models are on their places
  1778. expect(product.user).to.satisfy(User => User === null || User instanceof this.models.User);
  1779. expect(product.prices).to.be.an('array');
  1780. }
  1781. });
  1782. it('should allow through model to be paranoid', async function () {
  1783. const User = this.sequelize.define('User', { name: DataTypes.STRING }, { timestamps: false });
  1784. const Customer = this.sequelize.define(
  1785. 'Customer',
  1786. { name: DataTypes.STRING },
  1787. { timestamps: false },
  1788. );
  1789. const UserCustomer = this.sequelize.define(
  1790. 'UserCustomer',
  1791. {},
  1792. { paranoid: true, createdAt: false, updatedAt: false },
  1793. );
  1794. User.belongsToMany(Customer, {
  1795. through: UserCustomer,
  1796. as: 'customers',
  1797. inverse: { as: 'users' },
  1798. });
  1799. await this.sequelize.sync({ force: true });
  1800. const [user, customer1, customer2] = await Promise.all([
  1801. User.create({ name: 'User 1' }),
  1802. Customer.create({ name: 'Customer 1' }),
  1803. Customer.create({ name: 'Customer 2' }),
  1804. ]);
  1805. await user.setCustomers([customer1]);
  1806. await user.setCustomers([customer2]);
  1807. const users = await User.findAll({ include: Customer });
  1808. expect(users).to.be.an('array');
  1809. expect(users).to.be.lengthOf(1);
  1810. const customers = users[0].customers;
  1811. expect(customers).to.be.an('array');
  1812. expect(customers).to.be.lengthOf(1);
  1813. expect(customers[0].UserCustomer.deletedAt).not.to.exist;
  1814. const userCustomers = await UserCustomer.findAll({
  1815. paranoid: false,
  1816. });
  1817. expect(userCustomers).to.be.an('array');
  1818. expect(userCustomers).to.be.lengthOf(2);
  1819. const [nonDeletedUserCustomers, deletedUserCustomers] = partition(
  1820. userCustomers,
  1821. userCustomer => !userCustomer.deletedAt,
  1822. );
  1823. expect(nonDeletedUserCustomers).to.be.lengthOf(1);
  1824. expect(deletedUserCustomers).to.be.lengthOf(1);
  1825. });
  1826. });
  1827. });