findAll.test.js 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809
  1. 'use strict';
  2. const forEach = require('lodash/forEach');
  3. const chai = require('chai');
  4. const sinon = require('sinon');
  5. const expect = chai.expect;
  6. const Support = require('../support');
  7. const { DataTypes, Op, Sequelize } = require('@sequelize/core');
  8. const dayjs = require('dayjs');
  9. const promiseProps = require('p-props');
  10. const current = Support.sequelize;
  11. const dialect = current.dialect;
  12. const dialectName = Support.getTestDialect();
  13. describe(Support.getTestDialectTeaser('Model'), () => {
  14. beforeEach(async function () {
  15. this.User = this.sequelize.define('User', {
  16. username: DataTypes.STRING,
  17. secretValue: DataTypes.STRING,
  18. data: DataTypes.STRING,
  19. intVal: DataTypes.INTEGER,
  20. theDate: DataTypes.DATE,
  21. aBool: DataTypes.BOOLEAN,
  22. binary: DataTypes.BLOB,
  23. });
  24. await this.User.sync({ force: true });
  25. });
  26. describe('findAll', () => {
  27. if (current.dialect.supports.transactions) {
  28. it('supports transactions', async function () {
  29. const sequelize = await Support.createSingleTransactionalTestSequelizeInstance(
  30. this.sequelize,
  31. );
  32. const User = sequelize.define('User', { username: DataTypes.STRING });
  33. await User.sync({ force: true });
  34. const t = await sequelize.startUnmanagedTransaction();
  35. await User.create({ username: 'foo' }, { transaction: t });
  36. const users1 = await User.findAll({ where: { username: 'foo' } });
  37. const users2 = await User.findAll({ transaction: t });
  38. const users3 = await User.findAll({ where: { username: 'foo' }, transaction: t });
  39. expect(users1.length).to.equal(0);
  40. expect(users2.length).to.equal(1);
  41. expect(users3.length).to.equal(1);
  42. await t.rollback();
  43. });
  44. }
  45. it('should not crash on an empty where array', async function () {
  46. await this.User.findAll({
  47. where: [],
  48. });
  49. });
  50. it('should throw on an attempt to fetch no attributes', async function () {
  51. await expect(this.User.findAll({ attributes: [] })).to.be.rejectedWith(
  52. Sequelize.QueryError,
  53. /^Attempted a SELECT query.+without selecting any columns$/,
  54. );
  55. });
  56. it('should not throw if overall attributes are nonempty', async function () {
  57. const Post = this.sequelize.define('Post', { foo: DataTypes.STRING });
  58. const Comment = this.sequelize.define('Comment', { bar: DataTypes.STRING });
  59. Post.hasMany(Comment, { as: 'comments' });
  60. await Post.sync({ force: true });
  61. await Comment.sync({ force: true });
  62. // Should not throw in this case, even
  63. // though `attributes: []` is set for the main model
  64. await Post.findAll({
  65. raw: true,
  66. attributes: [],
  67. include: [
  68. {
  69. model: Comment,
  70. as: 'comments',
  71. attributes: [[Sequelize.fn('COUNT', Sequelize.col('comments.id')), 'commentCount']],
  72. },
  73. ],
  74. });
  75. });
  76. describe('special where conditions/smartWhere object', () => {
  77. beforeEach(async function () {
  78. this.buf = Buffer.alloc(16);
  79. this.buf.fill('\u0001');
  80. await this.User.bulkCreate([
  81. { username: 'boo', intVal: 5, theDate: '2013-01-01 12:00' },
  82. { username: 'boo2', intVal: 10, theDate: '2013-01-10 12:00', binary: this.buf },
  83. ]);
  84. });
  85. it('should be able to find rows where attribute is in a list of values', async function () {
  86. const users = await this.User.findAll({
  87. where: {
  88. username: ['boo', 'boo2'],
  89. },
  90. });
  91. expect(users).to.have.length(2);
  92. });
  93. it('should not break when trying to find rows using an array of primary keys', async function () {
  94. await this.User.findAll({
  95. where: {
  96. id: [1, 2, 3],
  97. },
  98. });
  99. });
  100. it('should not break when using smart syntax on binary fields', async function () {
  101. const users = await this.User.findAll({
  102. where: {
  103. binary: [this.buf, this.buf],
  104. },
  105. });
  106. expect(users).to.have.length(1);
  107. expect(users[0].binary.toString()).to.equal(this.buf.toString());
  108. expect(users[0].username).to.equal('boo2');
  109. });
  110. it('should be able to find a row using like', async function () {
  111. const users = await this.User.findAll({
  112. where: {
  113. username: {
  114. [Op.like]: '%2',
  115. },
  116. },
  117. });
  118. expect(users).to.be.an.instanceof(Array);
  119. expect(users).to.have.length(1);
  120. expect(users[0].username).to.equal('boo2');
  121. expect(users[0].intVal).to.equal(10);
  122. });
  123. it('should be able to find a row using not like', async function () {
  124. const users = await this.User.findAll({
  125. where: {
  126. username: {
  127. [Op.notLike]: '%2',
  128. },
  129. },
  130. });
  131. expect(users).to.be.an.instanceof(Array);
  132. expect(users).to.have.length(1);
  133. expect(users[0].username).to.equal('boo');
  134. expect(users[0].intVal).to.equal(5);
  135. });
  136. if (dialectName === 'postgres') {
  137. it('should be able to find a row using ilike', async function () {
  138. const users = await this.User.findAll({
  139. where: {
  140. username: {
  141. [Op.iLike]: '%2',
  142. },
  143. },
  144. });
  145. expect(users).to.be.an.instanceof(Array);
  146. expect(users).to.have.length(1);
  147. expect(users[0].username).to.equal('boo2');
  148. expect(users[0].intVal).to.equal(10);
  149. });
  150. it('should be able to find a row using not ilike', async function () {
  151. const users = await this.User.findAll({
  152. where: {
  153. username: {
  154. [Op.notILike]: '%2',
  155. },
  156. },
  157. });
  158. expect(users).to.be.an.instanceof(Array);
  159. expect(users).to.have.length(1);
  160. expect(users[0].username).to.equal('boo');
  161. expect(users[0].intVal).to.equal(5);
  162. });
  163. }
  164. it('should be able to find a row between a certain date using the between shortcut', async function () {
  165. const users = await this.User.findAll({
  166. where: {
  167. theDate: {
  168. [Op.between]: ['2013-01-02', '2013-01-11'],
  169. },
  170. },
  171. });
  172. expect(users[0].username).to.equal('boo2');
  173. expect(users[0].intVal).to.equal(10);
  174. });
  175. it('should be able to find a row not between a certain integer using the not between shortcut', async function () {
  176. const users = await this.User.findAll({
  177. where: {
  178. intVal: {
  179. [Op.notBetween]: [8, 10],
  180. },
  181. },
  182. });
  183. expect(users[0].username).to.equal('boo');
  184. expect(users[0].intVal).to.equal(5);
  185. });
  186. it('should be able to handle false/true values just fine...', async function () {
  187. const User = this.User;
  188. await User.bulkCreate([
  189. { username: 'boo5', aBool: false },
  190. { username: 'boo6', aBool: true },
  191. ]);
  192. const users = await User.findAll({ where: { aBool: false } });
  193. expect(users).to.have.length(1);
  194. expect(users[0].username).to.equal('boo5');
  195. const _users = await User.findAll({ where: { aBool: true } });
  196. expect(_users).to.have.length(1);
  197. expect(_users[0].username).to.equal('boo6');
  198. });
  199. it('should be able to handle false/true values through associations as well...', async function () {
  200. const User = this.User;
  201. const Passports = this.sequelize.define('Passports', {
  202. isActive: DataTypes.BOOLEAN,
  203. });
  204. User.hasMany(Passports);
  205. Passports.belongsTo(User);
  206. await User.sync({ force: true });
  207. await Passports.sync({ force: true });
  208. await User.bulkCreate([
  209. { username: 'boo5', aBool: false },
  210. { username: 'boo6', aBool: true },
  211. ]);
  212. await Passports.bulkCreate([{ isActive: true }, { isActive: false }]);
  213. const user = await User.findByPk(1);
  214. const passport = await Passports.findByPk(1);
  215. await user.setPassports([passport]);
  216. const _user = await User.findByPk(2);
  217. const _passport = await Passports.findByPk(2);
  218. await _user.setPassports([_passport]);
  219. const theFalsePassport = await _user.getPassports({ where: { isActive: false } });
  220. const theTruePassport = await user.getPassports({ where: { isActive: true } });
  221. expect(theFalsePassport).to.have.length(1);
  222. expect(theFalsePassport[0].isActive).to.be.false;
  223. expect(theTruePassport).to.have.length(1);
  224. expect(theTruePassport[0].isActive).to.be.true;
  225. });
  226. // On IBM i, can't have a primaryKey that is a CLOB or BLOB data type
  227. if (dialectName !== 'ibmi') {
  228. // TODO: re-enable once we have DataTypes.BINARY: https://github.com/sequelize/sequelize/issues/14259
  229. it.skip('should be able to handle binary values through associations as well', async function () {
  230. const User = this.User;
  231. const Binary = this.sequelize.define('Binary', {
  232. id: {
  233. type: DataTypes.STRING(16, true),
  234. primaryKey: true,
  235. },
  236. });
  237. const buf1 = this.buf;
  238. const buf2 = Buffer.alloc(16);
  239. buf2.fill('\u0002');
  240. User.belongsTo(Binary, { foreignKey: 'binary' });
  241. await this.sequelize.sync({ force: true });
  242. await User.bulkCreate([
  243. { username: 'boo5', aBool: false },
  244. { username: 'boo6', aBool: true },
  245. ]);
  246. await Binary.bulkCreate([{ id: buf1 }, { id: buf2 }]);
  247. const user = await User.findByPk(1);
  248. const binary = await Binary.findByPk(buf1);
  249. await user.setBinary(binary);
  250. const _user = await User.findByPk(2);
  251. const _binary = await Binary.findByPk(buf2);
  252. await _user.setBinary(_binary);
  253. const _binaryRetrieved = await _user.getBinary();
  254. const binaryRetrieved = await user.getBinary();
  255. expect(binaryRetrieved.id).to.have.length(16);
  256. expect(_binaryRetrieved.id).to.have.length(16);
  257. expect(binaryRetrieved.id.toString()).to.equal(buf1.toString());
  258. expect(_binaryRetrieved.id.toString()).to.equal(buf2.toString());
  259. });
  260. }
  261. it('should be able to find a row between a certain date', async function () {
  262. const users = await this.User.findAll({
  263. where: {
  264. theDate: {
  265. [Op.between]: ['2013-01-02', '2013-01-11'],
  266. },
  267. },
  268. });
  269. expect(users[0].username).to.equal('boo2');
  270. expect(users[0].intVal).to.equal(10);
  271. });
  272. it('should be able to find a row between a certain date and an additional where clause', async function () {
  273. const users = await this.User.findAll({
  274. where: {
  275. theDate: {
  276. [Op.between]: ['2013-01-02', '2013-01-11'],
  277. },
  278. intVal: 10,
  279. },
  280. });
  281. expect(users[0].username).to.equal('boo2');
  282. expect(users[0].intVal).to.equal(10);
  283. });
  284. it('should be able to find a row not between a certain integer', async function () {
  285. const users = await this.User.findAll({
  286. where: {
  287. intVal: {
  288. [Op.notBetween]: [8, 10],
  289. },
  290. },
  291. });
  292. expect(users[0].username).to.equal('boo');
  293. expect(users[0].intVal).to.equal(5);
  294. });
  295. it('should be able to find a row using not between and between logic', async function () {
  296. const users = await this.User.findAll({
  297. where: {
  298. theDate: {
  299. [Op.between]: ['2012-12-10', '2013-01-02'],
  300. [Op.notBetween]: ['2013-01-04', '2013-01-20'],
  301. },
  302. },
  303. });
  304. expect(users[0].username).to.equal('boo');
  305. expect(users[0].intVal).to.equal(5);
  306. });
  307. it('should be able to find a row using not between and between logic with dates', async function () {
  308. const users = await this.User.findAll({
  309. where: {
  310. theDate: {
  311. [Op.between]: [new Date('2012-12-10'), new Date('2013-01-02')],
  312. [Op.notBetween]: [new Date('2013-01-04'), new Date('2013-01-20')],
  313. },
  314. },
  315. });
  316. expect(users[0].username).to.equal('boo');
  317. expect(users[0].intVal).to.equal(5);
  318. });
  319. it('should be able to find a row using greater than or equal to logic with dates', async function () {
  320. const users = await this.User.findAll({
  321. where: {
  322. theDate: {
  323. [Op.gte]: new Date('2013-01-09'),
  324. },
  325. },
  326. });
  327. expect(users[0].username).to.equal('boo2');
  328. expect(users[0].intVal).to.equal(10);
  329. });
  330. it('should be able to find a row using greater than or equal to logic with dayjs dates', async function () {
  331. const users = await this.User.findAll({
  332. where: {
  333. theDate: {
  334. [Op.gte]: dayjs('2013-01-09'),
  335. },
  336. },
  337. });
  338. expect(users[0].username).to.equal('boo2');
  339. expect(users[0].intVal).to.equal(10);
  340. });
  341. it('should be able to find a row using greater than or equal to', async function () {
  342. const user = await this.User.findOne({
  343. where: {
  344. intVal: {
  345. [Op.gte]: 6,
  346. },
  347. },
  348. });
  349. expect(user.username).to.equal('boo2');
  350. expect(user.intVal).to.equal(10);
  351. });
  352. it('should be able to find a row using greater than', async function () {
  353. const user = await this.User.findOne({
  354. where: {
  355. intVal: {
  356. [Op.gt]: 5,
  357. },
  358. },
  359. });
  360. expect(user.username).to.equal('boo2');
  361. expect(user.intVal).to.equal(10);
  362. });
  363. it('should be able to find a row using lesser than or equal to', async function () {
  364. const user = await this.User.findOne({
  365. where: {
  366. intVal: {
  367. [Op.lte]: 5,
  368. },
  369. },
  370. });
  371. expect(user.username).to.equal('boo');
  372. expect(user.intVal).to.equal(5);
  373. });
  374. it('should be able to find a row using lesser than', async function () {
  375. const user = await this.User.findOne({
  376. where: {
  377. intVal: {
  378. [Op.lt]: 6,
  379. },
  380. },
  381. });
  382. expect(user.username).to.equal('boo');
  383. expect(user.intVal).to.equal(5);
  384. });
  385. it('should have no problem finding a row using lesser and greater than', async function () {
  386. const users = await this.User.findAll({
  387. where: {
  388. intVal: {
  389. [Op.lt]: 6,
  390. [Op.gt]: 4,
  391. },
  392. },
  393. });
  394. expect(users[0].username).to.equal('boo');
  395. expect(users[0].intVal).to.equal(5);
  396. });
  397. it('should be able to find a row using not equal to logic', async function () {
  398. const user = await this.User.findOne({
  399. where: {
  400. intVal: {
  401. [Op.ne]: 10,
  402. },
  403. },
  404. });
  405. expect(user.username).to.equal('boo');
  406. expect(user.intVal).to.equal(5);
  407. });
  408. it('should be able to find multiple users with any of the special where logic properties', async function () {
  409. const users = await this.User.findAll({
  410. where: {
  411. intVal: {
  412. [Op.lte]: 10,
  413. },
  414. },
  415. });
  416. expect(users[0].username).to.equal('boo');
  417. expect(users[0].intVal).to.equal(5);
  418. expect(users[1].username).to.equal('boo2');
  419. expect(users[1].intVal).to.equal(10);
  420. });
  421. if (dialect.supports.dataTypes.CITEXT) {
  422. it('should be able to find multiple users with case-insensitive on CITEXT type', async function () {
  423. const User = this.sequelize.define('UsersWithCaseInsensitiveName', {
  424. username: DataTypes.CITEXT,
  425. });
  426. await User.sync({ force: true });
  427. await User.bulkCreate([
  428. { username: 'lowercase' },
  429. { username: 'UPPERCASE' },
  430. { username: 'MIXEDcase' },
  431. ]);
  432. const users = await User.findAll({
  433. where: { username: ['LOWERCASE', 'uppercase', 'mixedCase'] },
  434. order: [['id', 'ASC']],
  435. });
  436. expect(users[0].username).to.equal('lowercase');
  437. expect(users[1].username).to.equal('UPPERCASE');
  438. expect(users[2].username).to.equal('MIXEDcase');
  439. });
  440. }
  441. });
  442. describe('eager loading', () => {
  443. it('should not ignore where condition with empty includes, #8771', async function () {
  444. await this.User.bulkCreate([
  445. { username: 'D.E.N.N.I.S', intVal: 6 },
  446. { username: 'F.R.A.N.K', intVal: 5 },
  447. { username: 'W.I.L.D C.A.R.D', intVal: 8 },
  448. ]);
  449. const users = await this.User.findAll({
  450. where: {
  451. intVal: 8,
  452. },
  453. include: [],
  454. });
  455. expect(users).to.have.length(1);
  456. expect(users[0].get('username')).to.equal('W.I.L.D C.A.R.D');
  457. });
  458. describe('belongsTo', () => {
  459. beforeEach(async function () {
  460. this.Task = this.sequelize.define('TaskBelongsTo', { title: DataTypes.STRING });
  461. this.Worker = this.sequelize.define('Worker', { name: DataTypes.STRING });
  462. this.Task.belongsTo(this.Worker);
  463. await this.Worker.sync({ force: true });
  464. await this.Task.sync({ force: true });
  465. const worker = await this.Worker.create({ name: 'worker' });
  466. const task = await this.Task.create({ title: 'homework' });
  467. this.worker = worker;
  468. this.task = task;
  469. await this.task.setWorker(this.worker);
  470. });
  471. it('throws an error about unexpected input if include contains a non-object', async function () {
  472. await expect(this.Worker.findAll({ include: [1] })).to.be
  473. .rejectedWith(`Invalid Include received. Include has to be either a Model, an Association, the name of an association, or a plain object compatible with IncludeOptions.
  474. Got { association: 1 } instead`);
  475. });
  476. it('throws an error if included DaoFactory is not associated', async function () {
  477. const OtherModel = this.sequelize.define('OtherModel');
  478. await expect(this.Worker.findAll({ include: [OtherModel] })).to.be.rejectedWith(
  479. 'Invalid Include received: no associations exist between "Worker" and "OtherModel"',
  480. );
  481. });
  482. it('returns the associated worker via task.worker', async function () {
  483. const tasks = await this.Task.findAll({
  484. where: { title: 'homework' },
  485. include: [this.Worker],
  486. });
  487. expect(tasks).to.exist;
  488. expect(tasks[0].worker).to.exist;
  489. expect(tasks[0].worker.name).to.equal('worker');
  490. });
  491. it('returns the associated worker via task.worker, using limit and sort', async function () {
  492. const tasks = await this.Task.findAll({
  493. where: { title: 'homework' },
  494. include: [this.Worker],
  495. limit: 1,
  496. order: [['title', 'DESC']],
  497. });
  498. expect(tasks).to.exist;
  499. expect(tasks[0].worker).to.exist;
  500. expect(tasks[0].worker.name).to.equal('worker');
  501. });
  502. });
  503. describe('hasOne', () => {
  504. beforeEach(async function () {
  505. this.Task = this.sequelize.define('TaskHasOne', { title: DataTypes.STRING });
  506. this.Worker = this.sequelize.define('Worker', { name: DataTypes.STRING });
  507. this.Worker.hasOne(this.Task);
  508. await this.Worker.sync({ force: true });
  509. await this.Task.sync({ force: true });
  510. const worker = await this.Worker.create({ name: 'worker' });
  511. const task = await this.Task.create({ title: 'homework' });
  512. this.worker = worker;
  513. this.task = task;
  514. await this.worker.setTaskHasOne(this.task);
  515. });
  516. it('throws an error if included DaoFactory is not associated', async function () {
  517. const OtherModel = this.sequelize.define('OtherModel');
  518. await expect(this.Task.findAll({ include: [OtherModel] })).to.be.rejectedWith(
  519. 'Invalid Include received: no associations exist between "TaskHasOne" and "OtherModel"',
  520. );
  521. });
  522. it('returns the associated task via worker.task', async function () {
  523. const workers = await this.Worker.findAll({
  524. where: { name: 'worker' },
  525. include: [this.Task],
  526. });
  527. expect(workers).to.exist;
  528. expect(workers[0].taskHasOne).to.exist;
  529. expect(workers[0].taskHasOne.title).to.equal('homework');
  530. });
  531. });
  532. describe('hasOne with alias', () => {
  533. beforeEach(async function () {
  534. this.Task = this.sequelize.define('Task', { title: DataTypes.STRING });
  535. this.Worker = this.sequelize.define('Worker', { name: DataTypes.STRING });
  536. this.Worker.hasOne(this.Task, { as: 'ToDo' });
  537. await this.Worker.sync({ force: true });
  538. await this.Task.sync({ force: true });
  539. const worker = await this.Worker.create({ name: 'worker' });
  540. const task = await this.Task.create({ title: 'homework' });
  541. this.worker = worker;
  542. this.task = task;
  543. await this.worker.setToDo(this.task);
  544. });
  545. it('throws an error if alias is not associated', async function () {
  546. await expect(this.Worker.findAll({ include: [{ model: this.Task, as: 'Work' }] })).to.be
  547. .rejectedWith(`Association with alias "Work" does not exist on Worker.
  548. The following associations are defined on "Worker": "ToDo"`);
  549. });
  550. it('returns the associated task via worker.task', async function () {
  551. const workers = await this.Worker.findAll({
  552. where: { name: 'worker' },
  553. include: [{ model: this.Task, as: 'ToDo' }],
  554. });
  555. expect(workers).to.exist;
  556. expect(workers[0].ToDo).to.exist;
  557. expect(workers[0].ToDo.title).to.equal('homework');
  558. });
  559. it('returns the associated task via worker.task when daoFactory is aliased with model', async function () {
  560. const workers = await this.Worker.findAll({
  561. where: { name: 'worker' },
  562. include: [{ model: this.Task, as: 'ToDo' }],
  563. });
  564. expect(workers[0].ToDo.title).to.equal('homework');
  565. });
  566. });
  567. describe('hasMany', () => {
  568. beforeEach(async function () {
  569. this.Task = this.sequelize.define('task', { title: DataTypes.STRING });
  570. this.Worker = this.sequelize.define('worker', { name: DataTypes.STRING });
  571. this.Worker.hasMany(this.Task);
  572. await this.Worker.sync({ force: true });
  573. await this.Task.sync({ force: true });
  574. const worker = await this.Worker.create({ name: 'worker' });
  575. const task = await this.Task.create({ title: 'homework' });
  576. this.worker = worker;
  577. this.task = task;
  578. await this.worker.setTasks([this.task]);
  579. });
  580. it('throws an error if included DaoFactory is not associated', async function () {
  581. const OtherModel = this.sequelize.define('OtherModel');
  582. await expect(this.Task.findAll({ include: [OtherModel] })).to.be.rejectedWith(
  583. 'Invalid Include received: no associations exist between "task" and "OtherModel"',
  584. );
  585. });
  586. it('returns the associated tasks via worker.tasks', async function () {
  587. const workers = await this.Worker.findAll({
  588. where: { name: 'worker' },
  589. include: [this.Task],
  590. });
  591. expect(workers).to.exist;
  592. expect(workers[0].tasks).to.exist;
  593. expect(workers[0].tasks[0].title).to.equal('homework');
  594. });
  595. // https://github.com/sequelize/sequelize/issues/8739
  596. it('supports sorting on renamed sub-query attribute', async function () {
  597. const User = this.sequelize.define('user', {
  598. name: {
  599. type: DataTypes.STRING,
  600. field: 'some_other_name',
  601. },
  602. });
  603. const Project = this.sequelize.define('project', { title: DataTypes.STRING });
  604. User.hasMany(Project);
  605. await User.sync({ force: true });
  606. await Project.sync({ force: true });
  607. await User.bulkCreate([{ name: 'a' }, { name: 'b' }, { name: 'c' }]);
  608. const users = await User.findAll({
  609. order: ['name'],
  610. limit: 2, // to force use of a sub-query
  611. include: [Project],
  612. });
  613. expect(users).to.have.lengthOf(2);
  614. expect(users[0].name).to.equal('a');
  615. expect(users[1].name).to.equal('b');
  616. });
  617. it('supports sorting DESC on renamed sub-query attribute', async function () {
  618. const User = this.sequelize.define('user', {
  619. name: {
  620. type: DataTypes.STRING,
  621. field: 'some_other_name',
  622. },
  623. });
  624. const Project = this.sequelize.define('project', { title: DataTypes.STRING });
  625. User.hasMany(Project);
  626. await User.sync({ force: true });
  627. await Project.sync({ force: true });
  628. await User.bulkCreate([{ name: 'a' }, { name: 'b' }, { name: 'c' }]);
  629. const users = await User.findAll({
  630. order: [['name', 'DESC']],
  631. limit: 2,
  632. include: [Project],
  633. });
  634. expect(users).to.have.lengthOf(2);
  635. expect(users[0].name).to.equal('c');
  636. expect(users[1].name).to.equal('b');
  637. });
  638. it('supports sorting on multiple renamed sub-query attributes', async function () {
  639. const User = this.sequelize.define('user', {
  640. name: {
  641. type: DataTypes.STRING,
  642. field: 'some_other_name',
  643. },
  644. age: {
  645. type: DataTypes.INTEGER,
  646. field: 'a_g_e',
  647. },
  648. });
  649. const Project = this.sequelize.define('project', { title: DataTypes.STRING });
  650. User.hasMany(Project);
  651. await User.sync({ force: true });
  652. await Project.sync({ force: true });
  653. await User.bulkCreate([
  654. { name: 'a', age: 1 },
  655. { name: 'a', age: 2 },
  656. { name: 'b', age: 3 },
  657. ]);
  658. const users0 = await User.findAll({
  659. order: [
  660. ['name', 'ASC'],
  661. ['age', 'DESC'],
  662. ],
  663. limit: 2,
  664. include: [Project],
  665. });
  666. expect(users0).to.have.lengthOf(2);
  667. expect(users0[0].name).to.equal('a');
  668. expect(users0[0].age).to.equal(2);
  669. expect(users0[1].name).to.equal('a');
  670. expect(users0[1].age).to.equal(1);
  671. const users = await User.findAll({
  672. order: [['name', 'DESC'], 'age'],
  673. limit: 2,
  674. include: [Project],
  675. });
  676. expect(users).to.have.lengthOf(2);
  677. expect(users[0].name).to.equal('b');
  678. expect(users[1].name).to.equal('a');
  679. expect(users[1].age).to.equal(1);
  680. });
  681. });
  682. describe('hasMany with alias', () => {
  683. beforeEach(async function () {
  684. this.Task = this.sequelize.define('Task', { title: DataTypes.STRING });
  685. this.Worker = this.sequelize.define('Worker', { name: DataTypes.STRING });
  686. this.Worker.hasMany(this.Task, { as: 'ToDos' });
  687. await this.Worker.sync({ force: true });
  688. await this.Task.sync({ force: true });
  689. const worker = await this.Worker.create({ name: 'worker' });
  690. const task = await this.Task.create({ title: 'homework' });
  691. this.worker = worker;
  692. this.task = task;
  693. await this.worker.setToDos([this.task]);
  694. });
  695. it('throws an error if alias is not associated', async function () {
  696. await expect(this.Worker.findAll({ include: [{ model: this.Task, as: 'Work' }] })).to.be
  697. .rejectedWith(`Association with alias "Work" does not exist on Worker.
  698. The following associations are defined on "Worker": "ToDos"`);
  699. });
  700. it('returns the associated task via worker.task', async function () {
  701. const workers = await this.Worker.findAll({
  702. where: { name: 'worker' },
  703. include: [{ model: this.Task, as: 'ToDos' }],
  704. });
  705. expect(workers).to.exist;
  706. expect(workers[0].ToDos).to.exist;
  707. expect(workers[0].ToDos[0].title).to.equal('homework');
  708. });
  709. it('returns the associated task via worker.task when daoFactory is aliased with model', async function () {
  710. const workers = await this.Worker.findAll({
  711. where: { name: 'worker' },
  712. include: [{ model: this.Task, as: 'ToDos' }],
  713. });
  714. expect(workers[0].ToDos[0].title).to.equal('homework');
  715. });
  716. });
  717. describe('queryOptions', () => {
  718. beforeEach(async function () {
  719. const user = await this.User.create({ username: 'barfooz' });
  720. this.user = user;
  721. });
  722. it('should return a DAO when queryOptions are not set', async function () {
  723. const users = await this.User.findAll({ where: { username: 'barfooz' } });
  724. for (const user of users) {
  725. expect(user).to.be.instanceOf(this.User);
  726. }
  727. });
  728. it('should return a DAO when raw is false', async function () {
  729. const users = await this.User.findAll({ where: { username: 'barfooz' }, raw: false });
  730. for (const user of users) {
  731. expect(user).to.be.instanceOf(this.User);
  732. }
  733. });
  734. it('should return raw data when raw is true', async function () {
  735. const users = await this.User.findAll({ where: { username: 'barfooz' }, raw: true });
  736. for (const user of users) {
  737. expect(user).to.not.be.instanceOf(this.User);
  738. expect(users[0]).to.be.instanceOf(Object);
  739. }
  740. });
  741. });
  742. describe('include all', () => {
  743. beforeEach(async function () {
  744. this.Continent = this.sequelize.define('continent', { name: DataTypes.STRING });
  745. this.Country = this.sequelize.define('country', { name: DataTypes.STRING });
  746. this.Industry = this.sequelize.define('industry', { name: DataTypes.STRING });
  747. this.Person = this.sequelize.define('person', {
  748. name: DataTypes.STRING,
  749. lastName: DataTypes.STRING,
  750. });
  751. this.Continent.hasMany(this.Country);
  752. this.Country.belongsTo(this.Continent);
  753. this.Country.belongsToMany(this.Industry, { through: 'country_industry' });
  754. this.Industry.belongsToMany(this.Country, { through: 'country_industry' });
  755. this.Country.hasMany(this.Person);
  756. this.Person.belongsTo(this.Country);
  757. this.Country.hasMany(this.Person, {
  758. as: 'residents',
  759. foreignKey: 'CountryResidentId',
  760. inverse: { as: 'CountryResident' },
  761. });
  762. await this.sequelize.sync({ force: true });
  763. const r = await promiseProps({
  764. europe: this.Continent.create({ name: 'Europe' }),
  765. england: this.Country.create({ name: 'England' }),
  766. coal: this.Industry.create({ name: 'Coal' }),
  767. bob: this.Person.create({ name: 'Bob', lastName: 'Becket' }),
  768. });
  769. forEach(r, (item, itemName) => {
  770. this[itemName] = item;
  771. });
  772. await Promise.all([
  773. this.england.setContinent(this.europe),
  774. this.england.addIndustry(this.coal),
  775. this.bob.setCountry(this.england),
  776. this.bob.setCountryResident(this.england),
  777. ]);
  778. });
  779. it('includes all associations', async function () {
  780. const countries = await this.Country.findAll({ include: [{ all: true }] });
  781. expect(countries).to.exist;
  782. expect(countries[0]).to.exist;
  783. expect(countries[0].continent).to.exist;
  784. expect(countries[0].industries).to.exist;
  785. expect(countries[0].people).to.exist;
  786. expect(countries[0].residents).to.exist;
  787. });
  788. it('includes specific type of association', async function () {
  789. const countries = await this.Country.findAll({ include: [{ all: 'BelongsTo' }] });
  790. expect(countries).to.exist;
  791. expect(countries[0]).to.exist;
  792. expect(countries[0].continent).to.exist;
  793. expect(countries[0].industries).not.to.exist;
  794. expect(countries[0].people).not.to.exist;
  795. expect(countries[0].residents).not.to.exist;
  796. });
  797. it('forbids using options', async function () {
  798. await expect(
  799. this.Country.findAll({ include: [{ all: 'HasMany', attributes: ['name'] }] }),
  800. ).to.be.rejectedWith(
  801. '"include: { all: true }" does not allow extra options (except for "nested") because they are unsafe. Select includes one by one if you want to specify more options.',
  802. );
  803. });
  804. it('is over-ruled by specified include', async function () {
  805. const countries = await this.Country.findAll({
  806. include: [{ all: true }, { model: this.Continent, attributes: ['id'] }],
  807. });
  808. expect(countries).to.exist;
  809. expect(countries[0]).to.exist;
  810. expect(countries[0].continent).to.exist;
  811. expect(countries[0].continent.name).to.be.undefined;
  812. });
  813. it('includes all nested associations', async function () {
  814. const continents = await this.Continent.findAll({
  815. include: [{ all: true, nested: true }],
  816. });
  817. expect(continents).to.exist;
  818. expect(continents[0]).to.exist;
  819. expect(continents[0].countries).to.exist;
  820. expect(continents[0].countries[0]).to.exist;
  821. expect(continents[0].countries[0].industries).to.exist;
  822. expect(continents[0].countries[0].people).to.exist;
  823. expect(continents[0].countries[0].residents).to.exist;
  824. expect(continents[0].countries[0].continent).not.to.exist;
  825. });
  826. });
  827. describe('properly handles attributes:[] cases', () => {
  828. beforeEach(async function () {
  829. this.Animal = this.sequelize.define('Animal', {
  830. name: DataTypes.STRING,
  831. age: DataTypes.INTEGER,
  832. });
  833. this.Kingdom = this.sequelize.define('Kingdom', {
  834. name: DataTypes.STRING,
  835. });
  836. this.AnimalKingdom = this.sequelize.define('AnimalKingdom', {
  837. relation: DataTypes.STRING,
  838. mutation: DataTypes.BOOLEAN,
  839. });
  840. this.Kingdom.belongsToMany(this.Animal, { through: this.AnimalKingdom });
  841. await this.sequelize.sync({ force: true });
  842. const [a1, a2, a3, a4] = await Promise.all([
  843. this.Animal.create({ name: 'Dog', age: 20 }),
  844. this.Animal.create({ name: 'Cat', age: 30 }),
  845. this.Animal.create({ name: 'Peacock', age: 25 }),
  846. this.Animal.create({ name: 'Fish', age: 100 }),
  847. ]);
  848. const [k1, k2, k3] = await Promise.all([
  849. this.Kingdom.create({ name: 'Earth' }),
  850. this.Kingdom.create({ name: 'Water' }),
  851. this.Kingdom.create({ name: 'Wind' }),
  852. ]);
  853. await Promise.all([k1.addAnimals([a1, a2]), k2.addAnimals([a4]), k3.addAnimals([a3])]);
  854. });
  855. it('N:M with ignoring include.attributes only', async function () {
  856. const kingdoms = await this.Kingdom.findAll({
  857. include: [
  858. {
  859. model: this.Animal,
  860. where: { age: { [Op.gte]: 29 } },
  861. attributes: [],
  862. },
  863. ],
  864. });
  865. expect(kingdoms.length).to.be.eql(2);
  866. for (const kingdom of kingdoms) {
  867. // include.attributes:[] , model doesn't exists
  868. expect(kingdom.Animals).to.not.exist;
  869. }
  870. });
  871. it('N:M with ignoring through.attributes only', async function () {
  872. const kingdoms = await this.Kingdom.findAll({
  873. include: [
  874. {
  875. model: this.Animal,
  876. where: { age: { [Op.gte]: 29 } },
  877. through: {
  878. attributes: [],
  879. },
  880. },
  881. ],
  882. });
  883. expect(kingdoms.length).to.be.eql(2);
  884. for (const kingdom of kingdoms) {
  885. expect(kingdom.animals).to.exist; // include model exists
  886. expect(kingdom.animals[0].AnimalKingdom).to.not.exist; // through doesn't exists
  887. }
  888. });
  889. it('N:M with ignoring include.attributes but having through.attributes', async function () {
  890. const kingdoms = await this.Kingdom.findAll({
  891. include: [
  892. {
  893. model: this.Animal,
  894. where: { age: { [Op.gte]: 29 } },
  895. attributes: [],
  896. through: {
  897. attributes: ['mutation'],
  898. },
  899. },
  900. ],
  901. });
  902. expect(kingdoms.length).to.be.eql(2);
  903. for (const kingdom of kingdoms) {
  904. // include.attributes: [], model doesn't exists
  905. expect(kingdom.Animals).to.not.exist;
  906. }
  907. });
  908. });
  909. });
  910. describe('order by eager loaded tables', () => {
  911. describe('HasMany', () => {
  912. beforeEach(async function () {
  913. this.Continent = this.sequelize.define('continent', { name: DataTypes.STRING });
  914. this.Country = this.sequelize.define('country', { name: DataTypes.STRING });
  915. this.Person = this.sequelize.define('person', {
  916. name: DataTypes.STRING,
  917. lastName: DataTypes.STRING,
  918. });
  919. this.Continent.hasMany(this.Country);
  920. this.Country.hasMany(this.Person);
  921. this.Country.hasMany(this.Person, {
  922. as: 'residents',
  923. foreignKey: 'CountryResidentId',
  924. inverse: { as: 'CountryResident' },
  925. });
  926. await this.sequelize.sync({ force: true });
  927. const r = await promiseProps({
  928. europe: this.Continent.create({ name: 'Europe' }),
  929. asia: this.Continent.create({ name: 'Asia' }),
  930. england: this.Country.create({ name: 'England' }),
  931. france: this.Country.create({ name: 'France' }),
  932. korea: this.Country.create({ name: 'Korea' }),
  933. bob: this.Person.create({ name: 'Bob', lastName: 'Becket' }),
  934. fred: this.Person.create({ name: 'Fred', lastName: 'Able' }),
  935. pierre: this.Person.create({ name: 'Pierre', lastName: 'Paris' }),
  936. kim: this.Person.create({ name: 'Kim', lastName: 'Z' }),
  937. });
  938. forEach(r, (item, itemName) => {
  939. this[itemName] = item;
  940. });
  941. await Promise.all([
  942. this.england.setContinent(this.europe),
  943. this.france.setContinent(this.europe),
  944. this.korea.setContinent(this.asia),
  945. this.bob.setCountry(this.england),
  946. this.fred.setCountry(this.england),
  947. this.pierre.setCountry(this.france),
  948. this.kim.setCountry(this.korea),
  949. this.bob.setCountryResident(this.england),
  950. this.fred.setCountryResident(this.france),
  951. this.pierre.setCountryResident(this.korea),
  952. this.kim.setCountryResident(this.england),
  953. ]);
  954. });
  955. it('sorts simply', async function () {
  956. await Promise.all(
  957. [
  958. ['ASC', 'Asia'],
  959. ['DESC', 'Europe'],
  960. ].map(async params => {
  961. const continents = await this.Continent.findAll({
  962. order: [['name', params[0]]],
  963. });
  964. expect(continents).to.exist;
  965. expect(continents[0]).to.exist;
  966. expect(continents[0].name).to.equal(params[1]);
  967. }),
  968. );
  969. });
  970. it('sorts by 1st degree association', async function () {
  971. await Promise.all(
  972. [
  973. ['ASC', 'Europe', 'England'],
  974. ['DESC', 'Asia', 'Korea'],
  975. ].map(async params => {
  976. const continents = await this.Continent.findAll({
  977. include: [this.Country],
  978. order: [[this.Country, 'name', params[0]]],
  979. });
  980. expect(continents).to.exist;
  981. expect(continents[0]).to.exist;
  982. expect(continents[0].name).to.equal(params[1]);
  983. expect(continents[0].countries).to.exist;
  984. expect(continents[0].countries[0]).to.exist;
  985. expect(continents[0].countries[0].name).to.equal(params[2]);
  986. }),
  987. );
  988. });
  989. it('sorts simply and by 1st degree association with limit where 1st degree associated instances returned for second one and not the first', async function () {
  990. await Promise.all(
  991. [['ASC', 'Asia', 'Europe', 'England']].map(async params => {
  992. const continents = await this.Continent.findAll({
  993. include: [
  994. {
  995. model: this.Country,
  996. required: false,
  997. where: {
  998. name: params[3],
  999. },
  1000. },
  1001. ],
  1002. limit: 2,
  1003. order: [
  1004. ['name', params[0]],
  1005. [this.Country, 'name', params[0]],
  1006. ],
  1007. });
  1008. expect(continents).to.exist;
  1009. expect(continents[0]).to.exist;
  1010. expect(continents[0].name).to.equal(params[1]);
  1011. expect(continents[0].countries).to.exist;
  1012. expect(continents[0].countries.length).to.equal(0);
  1013. expect(continents[1]).to.exist;
  1014. expect(continents[1].name).to.equal(params[2]);
  1015. expect(continents[1].countries).to.exist;
  1016. expect(continents[1].countries.length).to.equal(1);
  1017. expect(continents[1].countries[0]).to.exist;
  1018. expect(continents[1].countries[0].name).to.equal(params[3]);
  1019. }),
  1020. );
  1021. });
  1022. it('sorts by 2nd degree association', async function () {
  1023. await Promise.all(
  1024. [
  1025. ['ASC', 'Europe', 'England', 'Fred'],
  1026. ['DESC', 'Asia', 'Korea', 'Kim'],
  1027. ].map(async params => {
  1028. const continents = await this.Continent.findAll({
  1029. include: [{ model: this.Country, include: ['people'] }],
  1030. order: [[this.Country, 'people', 'lastName', params[0]]],
  1031. });
  1032. expect(continents).to.exist;
  1033. expect(continents[0]).to.exist;
  1034. expect(continents[0].name).to.equal(params[1]);
  1035. expect(continents[0].countries).to.exist;
  1036. expect(continents[0].countries[0]).to.exist;
  1037. expect(continents[0].countries[0].name).to.equal(params[2]);
  1038. expect(continents[0].countries[0].people).to.exist;
  1039. expect(continents[0].countries[0].people[0]).to.exist;
  1040. expect(continents[0].countries[0].people[0].name).to.equal(params[3]);
  1041. }),
  1042. );
  1043. });
  1044. it('sorts by 2nd degree association with alias', async function () {
  1045. await Promise.all(
  1046. [
  1047. ['ASC', 'Europe', 'France', 'Fred'],
  1048. ['DESC', 'Europe', 'England', 'Kim'],
  1049. ].map(async params => {
  1050. const continents = await this.Continent.findAll({
  1051. include: [
  1052. {
  1053. model: this.Country,
  1054. include: ['people', { model: this.Person, as: 'residents' }],
  1055. },
  1056. ],
  1057. order: [
  1058. [this.Country, { model: this.Person, as: 'residents' }, 'lastName', params[0]],
  1059. ],
  1060. });
  1061. expect(continents).to.exist;
  1062. expect(continents[0]).to.exist;
  1063. expect(continents[0].name).to.equal(params[1]);
  1064. expect(continents[0].countries).to.exist;
  1065. expect(continents[0].countries[0]).to.exist;
  1066. expect(continents[0].countries[0].name).to.equal(params[2]);
  1067. expect(continents[0].countries[0].residents).to.exist;
  1068. expect(continents[0].countries[0].residents[0]).to.exist;
  1069. expect(continents[0].countries[0].residents[0].name).to.equal(params[3]);
  1070. }),
  1071. );
  1072. });
  1073. it('sorts by 2nd degree association with alias while using limit', async function () {
  1074. await Promise.all(
  1075. [
  1076. ['ASC', 'Europe', 'France', 'Fred'],
  1077. ['DESC', 'Europe', 'England', 'Kim'],
  1078. ].map(async params => {
  1079. const continents = await this.Continent.findAll({
  1080. include: [
  1081. {
  1082. model: this.Country,
  1083. include: ['people', { model: this.Person, as: 'residents' }],
  1084. },
  1085. ],
  1086. order: [
  1087. [
  1088. { model: this.Country },
  1089. { model: this.Person, as: 'residents' },
  1090. 'lastName',
  1091. params[0],
  1092. ],
  1093. ],
  1094. limit: 3,
  1095. });
  1096. expect(continents).to.exist;
  1097. expect(continents[0]).to.exist;
  1098. expect(continents[0].name).to.equal(params[1]);
  1099. expect(continents[0].countries).to.exist;
  1100. expect(continents[0].countries[0]).to.exist;
  1101. expect(continents[0].countries[0].name).to.equal(params[2]);
  1102. expect(continents[0].countries[0].residents).to.exist;
  1103. expect(continents[0].countries[0].residents[0]).to.exist;
  1104. expect(continents[0].countries[0].residents[0].name).to.equal(params[3]);
  1105. }),
  1106. );
  1107. });
  1108. });
  1109. describe('ManyToMany', () => {
  1110. beforeEach(async function () {
  1111. this.Country = this.sequelize.define('country', { name: DataTypes.STRING });
  1112. this.Industry = this.sequelize.define('industry', { name: DataTypes.STRING });
  1113. this.IndustryCountry = this.sequelize.define('IndustryCountry', {
  1114. numYears: DataTypes.INTEGER,
  1115. });
  1116. this.Country.belongsToMany(this.Industry, { through: this.IndustryCountry });
  1117. this.Industry.belongsToMany(this.Country, { through: this.IndustryCountry });
  1118. await this.sequelize.sync({ force: true });
  1119. const r = await promiseProps({
  1120. england: this.Country.create({ name: 'England' }),
  1121. france: this.Country.create({ name: 'France' }),
  1122. korea: this.Country.create({ name: 'Korea' }),
  1123. energy: this.Industry.create({ name: 'Energy' }),
  1124. media: this.Industry.create({ name: 'Media' }),
  1125. tech: this.Industry.create({ name: 'Tech' }),
  1126. });
  1127. forEach(r, (item, itemName) => {
  1128. this[itemName] = item;
  1129. });
  1130. await Promise.all([
  1131. this.england.addIndustry(this.energy, { through: { numYears: 20 } }),
  1132. this.england.addIndustry(this.media, { through: { numYears: 40 } }),
  1133. this.france.addIndustry(this.media, { through: { numYears: 80 } }),
  1134. this.korea.addIndustry(this.tech, { through: { numYears: 30 } }),
  1135. ]);
  1136. });
  1137. it('sorts by 1st degree association', async function () {
  1138. await Promise.all(
  1139. [
  1140. ['ASC', 'England', 'Energy'],
  1141. ['DESC', 'Korea', 'Tech'],
  1142. ].map(async params => {
  1143. const countries = await this.Country.findAll({
  1144. include: [this.Industry],
  1145. order: [[this.Industry, 'name', params[0]]],
  1146. });
  1147. expect(countries).to.exist;
  1148. expect(countries[0]).to.exist;
  1149. expect(countries[0].name).to.equal(params[1]);
  1150. expect(countries[0].industries).to.exist;
  1151. expect(countries[0].industries[0]).to.exist;
  1152. expect(countries[0].industries[0].name).to.equal(params[2]);
  1153. }),
  1154. );
  1155. });
  1156. it('sorts by 1st degree association while using limit', async function () {
  1157. await Promise.all(
  1158. [
  1159. ['ASC', 'England', 'Energy'],
  1160. ['DESC', 'Korea', 'Tech'],
  1161. ].map(async params => {
  1162. const countries = await this.Country.findAll({
  1163. include: [this.Industry],
  1164. order: [[this.Industry, 'name', params[0]]],
  1165. limit: 3,
  1166. });
  1167. expect(countries).to.exist;
  1168. expect(countries[0]).to.exist;
  1169. expect(countries[0].name).to.equal(params[1]);
  1170. expect(countries[0].industries).to.exist;
  1171. expect(countries[0].industries[0]).to.exist;
  1172. expect(countries[0].industries[0].name).to.equal(params[2]);
  1173. }),
  1174. );
  1175. });
  1176. it('sorts by through table attribute', async function () {
  1177. await Promise.all(
  1178. [
  1179. ['ASC', 'England', 'Energy'],
  1180. ['DESC', 'France', 'Media'],
  1181. ].map(async params => {
  1182. const countries = await this.Country.findAll({
  1183. include: [this.Industry],
  1184. order: [[this.Industry, this.IndustryCountry, 'numYears', params[0]]],
  1185. });
  1186. expect(countries).to.exist;
  1187. expect(countries[0]).to.exist;
  1188. expect(countries[0].name).to.equal(params[1]);
  1189. expect(countries[0].industries).to.exist;
  1190. expect(countries[0].industries[0]).to.exist;
  1191. expect(countries[0].industries[0].name).to.equal(params[2]);
  1192. }),
  1193. );
  1194. });
  1195. });
  1196. });
  1197. describe('normal findAll', () => {
  1198. beforeEach(async function () {
  1199. const user = await this.User.create({
  1200. username: 'user',
  1201. data: 'foobar',
  1202. theDate: dayjs().toDate(),
  1203. });
  1204. const user2 = await this.User.create({
  1205. username: 'user2',
  1206. data: 'bar',
  1207. theDate: dayjs().toDate(),
  1208. });
  1209. this.users = [user].concat(user2);
  1210. });
  1211. it('finds all entries', async function () {
  1212. const users = await this.User.findAll();
  1213. expect(users.length).to.equal(2);
  1214. });
  1215. it('can also handle object notation', async function () {
  1216. const users = await this.User.findAll({ where: { id: this.users[1].id } });
  1217. expect(users.length).to.equal(1);
  1218. expect(users[0].id).to.equal(this.users[1].id);
  1219. });
  1220. it('sorts the results via id in ascending order', async function () {
  1221. const users = await this.User.findAll();
  1222. expect(users.length).to.equal(2);
  1223. expect(users[0].id).to.be.below(users[1].id);
  1224. });
  1225. it('sorts the results via id in descending order', async function () {
  1226. const users = await this.User.findAll({ order: [['id', 'DESC']] });
  1227. expect(users[0].id).to.be.above(users[1].id);
  1228. });
  1229. it('sorts the results via a date column', async function () {
  1230. await this.User.create({
  1231. username: 'user3',
  1232. data: 'bar',
  1233. theDate: dayjs().add(2, 'hours').toDate(),
  1234. });
  1235. const users = await this.User.findAll({ order: [['theDate', 'DESC']] });
  1236. expect(users[0].id).to.be.above(users[2].id);
  1237. });
  1238. it('handles offset and limit', async function () {
  1239. await this.User.bulkCreate([{ username: 'bobby' }, { username: 'tables' }]);
  1240. const users = await this.User.findAll({ limit: 2, offset: 2 });
  1241. expect(users.length).to.equal(2);
  1242. expect(users[0].id).to.equal(3);
  1243. });
  1244. it('should allow us to find IDs using capital letters', async function () {
  1245. const User = this.sequelize.define(`User${Support.rand()}`, {
  1246. ID: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
  1247. Login: { type: DataTypes.STRING },
  1248. });
  1249. await User.sync({ force: true });
  1250. await User.create({ Login: 'foo' });
  1251. const user = await User.findAll({ where: { ID: 1 } });
  1252. expect(user).to.be.instanceof(Array);
  1253. expect(user).to.have.length(1);
  1254. });
  1255. it('should be possible to order by sequelize.col()', async function () {
  1256. const Company = this.sequelize.define('Company', {
  1257. name: DataTypes.STRING,
  1258. });
  1259. await Company.sync();
  1260. await Company.findAll({
  1261. order: [this.sequelize.col('name')],
  1262. });
  1263. });
  1264. it('should pull in dependent fields for a VIRTUAL', async function () {
  1265. const User = this.sequelize.define(
  1266. 'User',
  1267. {
  1268. active: {
  1269. type: DataTypes.VIRTUAL(DataTypes.BOOLEAN, ['createdAt']),
  1270. get() {
  1271. return this.get('createdAt') > Date.now() - 7 * 24 * 60 * 60 * 1000;
  1272. },
  1273. },
  1274. },
  1275. {
  1276. timestamps: true,
  1277. },
  1278. );
  1279. await User.create();
  1280. const users = await User.findAll({
  1281. attributes: ['active'],
  1282. });
  1283. for (const user of users) {
  1284. expect(user.get('createdAt')).to.be.ok;
  1285. expect(user.get('active')).to.equal(true);
  1286. }
  1287. });
  1288. it('should pull in dependent fields for a VIRTUAL in include', async function () {
  1289. const User = this.sequelize.define('User', {
  1290. name: DataTypes.STRING,
  1291. });
  1292. const Image = this.sequelize.define('Image', {
  1293. path: {
  1294. type: DataTypes.STRING,
  1295. allowNull: false,
  1296. },
  1297. url: {
  1298. type: DataTypes.VIRTUAL(DataTypes.STRING, ['path']),
  1299. get() {
  1300. return `https://my-cool-domain.com/${this.get('path')}`;
  1301. },
  1302. },
  1303. });
  1304. User.hasOne(Image);
  1305. Image.belongsTo(User);
  1306. await this.sequelize.sync({ force: true });
  1307. await User.create(
  1308. {
  1309. name: 'some user',
  1310. image: {
  1311. path: 'folder1/folder2/logo.png',
  1312. },
  1313. },
  1314. {
  1315. include: {
  1316. model: Image,
  1317. },
  1318. },
  1319. );
  1320. const users = await User.findAll({
  1321. attributes: ['name'],
  1322. include: [
  1323. {
  1324. model: Image,
  1325. attributes: ['url'],
  1326. },
  1327. ],
  1328. });
  1329. for (const user of users) {
  1330. expect(user.get('name')).to.equal('some user');
  1331. expect(user.image.get('url')).to.equal(
  1332. 'https://my-cool-domain.com/folder1/folder2/logo.png',
  1333. );
  1334. expect(user.image.get('path')).to.equal('folder1/folder2/logo.png');
  1335. }
  1336. });
  1337. });
  1338. });
  1339. describe('findAndCountAll', () => {
  1340. beforeEach(async function () {
  1341. await this.User.bulkCreate([
  1342. { username: 'user', data: 'foobar', intVal: 5 },
  1343. { username: 'user2', data: 'bar', intVal: 5 },
  1344. { username: 'bobby', data: 'foo', intVal: 5 },
  1345. ]);
  1346. const users = await this.User.findAll();
  1347. this.users = users;
  1348. });
  1349. if (current.dialect.supports.transactions) {
  1350. it('supports transactions', async function () {
  1351. const sequelize = await Support.createSingleTransactionalTestSequelizeInstance(
  1352. this.sequelize,
  1353. );
  1354. const User = sequelize.define('User', { username: DataTypes.STRING });
  1355. await User.sync({ force: true });
  1356. const t = await sequelize.startUnmanagedTransaction();
  1357. await User.create({ username: 'foo' }, { transaction: t });
  1358. const info1 = await User.findAndCountAll();
  1359. const info2 = await User.findAndCountAll({ transaction: t });
  1360. expect(info1.count).to.equal(0);
  1361. expect(info2.count).to.equal(1);
  1362. await t.rollback();
  1363. });
  1364. }
  1365. it('handles where clause {only}', async function () {
  1366. const info = await this.User.findAndCountAll({
  1367. where: { id: { [Op.ne]: this.users[0].id } },
  1368. });
  1369. expect(info.count).to.equal(2);
  1370. expect(Array.isArray(info.rows)).to.be.ok;
  1371. expect(info.rows.length).to.equal(2);
  1372. });
  1373. it('handles where clause with ordering {only}', async function () {
  1374. const info = await this.User.findAndCountAll({
  1375. where: { id: { [Op.ne]: this.users[0].id } },
  1376. order: [['id', 'ASC']],
  1377. });
  1378. expect(info.count).to.equal(2);
  1379. expect(Array.isArray(info.rows)).to.be.ok;
  1380. expect(info.rows.length).to.equal(2);
  1381. });
  1382. it('handles offset', async function () {
  1383. const info = await this.User.findAndCountAll({ offset: 1 });
  1384. expect(info.count).to.equal(3);
  1385. expect(Array.isArray(info.rows)).to.be.ok;
  1386. expect(info.rows.length).to.equal(2);
  1387. });
  1388. it('handles limit', async function () {
  1389. const info = await this.User.findAndCountAll({ limit: 1 });
  1390. expect(info.count).to.equal(3);
  1391. expect(Array.isArray(info.rows)).to.be.ok;
  1392. expect(info.rows.length).to.equal(1);
  1393. });
  1394. it('handles offset and limit', async function () {
  1395. const info = await this.User.findAndCountAll({ offset: 1, limit: 1 });
  1396. expect(info.count).to.equal(3);
  1397. expect(Array.isArray(info.rows)).to.be.ok;
  1398. expect(info.rows.length).to.equal(1);
  1399. });
  1400. it('handles offset with includes', async function () {
  1401. const Election = this.sequelize.define('Election', {
  1402. name: DataTypes.STRING,
  1403. });
  1404. const Citizen = this.sequelize.define('Citizen', {
  1405. name: DataTypes.STRING,
  1406. });
  1407. // Associations
  1408. Election.belongsToMany(Citizen, {
  1409. as: 'Voters',
  1410. through: 'ElectionsVotes',
  1411. inverse: { as: 'Votes' },
  1412. });
  1413. Election.belongsTo(Citizen);
  1414. Citizen.hasMany(Election);
  1415. await this.sequelize.sync();
  1416. // Add some data
  1417. const alice = await Citizen.create({ name: 'Alice' });
  1418. const bob = await Citizen.create({ name: 'Bob' });
  1419. await Election.create({ name: 'Some election' });
  1420. const election = await Election.create({ name: 'Some other election' });
  1421. await election.setCitizen(alice);
  1422. await election.setVoters([alice, bob]);
  1423. const elections = await Election.findAndCountAll({
  1424. offset: 5,
  1425. limit: 1,
  1426. where: {
  1427. name: 'Some election',
  1428. },
  1429. include: [
  1430. 'citizen', // Election creator
  1431. { model: Citizen, as: 'Voters' }, // Election voters
  1432. ],
  1433. });
  1434. expect(elections.count).to.equal(1);
  1435. expect(elections.rows.length).to.equal(0);
  1436. });
  1437. it('handles attributes', async function () {
  1438. const info = await this.User.findAndCountAll({
  1439. where: { id: { [Op.ne]: this.users[0].id } },
  1440. attributes: ['data'],
  1441. });
  1442. expect(info.count).to.equal(2);
  1443. expect(Array.isArray(info.rows)).to.be.ok;
  1444. expect(info.rows.length).to.equal(2);
  1445. expect(info.rows[0].dataValues).to.not.have.property('username');
  1446. expect(info.rows[1].dataValues).to.not.have.property('username');
  1447. });
  1448. it('handles grouped rows', async function () {
  1449. const info = await this.User.findAndCountAll({
  1450. attributes: [
  1451. [Sequelize.fn('sum', Sequelize.col('intVal')), 'sum'],
  1452. Sequelize.col('intVal'),
  1453. ],
  1454. group: [Sequelize.col('intVal')],
  1455. countGroupedRows: true,
  1456. raw: true,
  1457. });
  1458. expect(info.count).to.equal(1);
  1459. expect(Array.isArray(info.rows)).to.be.ok;
  1460. const row = info.rows[0];
  1461. if (dialectName === 'mysql') {
  1462. row.sum = Number(row.sum);
  1463. }
  1464. expect(info.rows).to.deep.equal([row]);
  1465. });
  1466. });
  1467. describe('all', () => {
  1468. beforeEach(async function () {
  1469. await this.User.bulkCreate([
  1470. { username: 'user', data: 'foobar' },
  1471. { username: 'user2', data: 'bar' },
  1472. ]);
  1473. });
  1474. if (current.dialect.supports.transactions) {
  1475. it('supports transactions', async function () {
  1476. const sequelize = await Support.createSingleTransactionalTestSequelizeInstance(
  1477. this.sequelize,
  1478. );
  1479. const User = sequelize.define('User', { username: DataTypes.STRING });
  1480. await User.sync({ force: true });
  1481. const t = await sequelize.startUnmanagedTransaction();
  1482. await User.create({ username: 'foo' }, { transaction: t });
  1483. const users1 = await User.findAll();
  1484. const users2 = await User.findAll({ transaction: t });
  1485. expect(users1.length).to.equal(0);
  1486. expect(users2.length).to.equal(1);
  1487. await t.rollback();
  1488. });
  1489. }
  1490. it('should return all users', async function () {
  1491. const users = await this.User.findAll();
  1492. expect(users.length).to.equal(2);
  1493. });
  1494. });
  1495. it('should support logging', async function () {
  1496. const spy = sinon.spy();
  1497. await this.User.findAll({
  1498. where: {},
  1499. logging: spy,
  1500. });
  1501. expect(spy.called).to.be.ok;
  1502. });
  1503. describe('rejectOnEmpty mode', () => {
  1504. it('works from model options', async () => {
  1505. const Model = current.define(
  1506. 'Test',
  1507. {
  1508. username: DataTypes.STRING(100),
  1509. },
  1510. {
  1511. rejectOnEmpty: true,
  1512. },
  1513. );
  1514. await Model.sync({ force: true });
  1515. await expect(
  1516. Model.findAll({
  1517. where: {
  1518. username: 'some-username-that-is-not-used-anywhere',
  1519. },
  1520. }),
  1521. ).to.eventually.be.rejectedWith(Sequelize.EmptyResultError);
  1522. });
  1523. it('throws custom error with initialized', async () => {
  1524. const Model = current.define(
  1525. 'Test',
  1526. {
  1527. username: DataTypes.STRING(100),
  1528. },
  1529. {
  1530. rejectOnEmpty: new Sequelize.ConnectionError('Some Error'), // using custom error instance
  1531. },
  1532. );
  1533. await Model.sync({ force: true });
  1534. await expect(
  1535. Model.findAll({
  1536. where: {
  1537. username: 'some-username-that-is-not-used-anywhere-for-sure-this-time',
  1538. },
  1539. }),
  1540. ).to.eventually.be.rejectedWith(Sequelize.ConnectionError);
  1541. });
  1542. it('throws custom error with instance', async () => {
  1543. const Model = current.define(
  1544. 'Test',
  1545. {
  1546. username: DataTypes.STRING(100),
  1547. },
  1548. {
  1549. rejectOnEmpty: Sequelize.ConnectionError, // using custom error instance
  1550. },
  1551. );
  1552. await Model.sync({ force: true });
  1553. await expect(
  1554. Model.findAll({
  1555. where: {
  1556. username: 'some-username-that-is-not-used-anywhere-for-sure-this-time',
  1557. },
  1558. }),
  1559. ).to.eventually.be.rejectedWith(Sequelize.ConnectionError);
  1560. });
  1561. });
  1562. });