123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- 'use strict';
- const chai = require('chai');
- const expect = chai.expect;
- const sinon = require('sinon');
- const Support = require('../support');
- const { DataTypes, Op } = require('@sequelize/core');
- describe(Support.getTestDialectTeaser('Include'), () => {
- before(function () {
- this.clock = sinon.useFakeTimers();
- });
- after(function () {
- this.clock.restore();
- });
- describe('findAndCountAll', () => {
- it('should be able to include two required models with a limit. Result rows should match limit.', async function () {
- const Project = this.sequelize.define('Project', {
- id: { type: DataTypes.INTEGER, primaryKey: true },
- name: DataTypes.STRING(40),
- });
- const Task = this.sequelize.define('Task', {
- name: DataTypes.STRING(40),
- fk: DataTypes.INTEGER,
- });
- const Employee = this.sequelize.define('Employee', {
- name: DataTypes.STRING(40),
- fk: DataTypes.INTEGER,
- });
- Project.hasMany(Task, { foreignKey: 'fk', foreignKeyConstraints: false });
- Project.hasMany(Employee, { foreignKey: 'fk', foreignKeyConstraints: false });
- Task.belongsTo(Project, { foreignKey: 'fk', foreignKeyConstraints: false });
- Employee.belongsTo(Project, { foreignKey: 'fk', foreignKeyConstraints: false });
- // Sync them
- await this.sequelize.sync({ force: true });
- // Create an enviroment
- await Promise.all([
- Project.bulkCreate([
- { id: 1, name: 'No tasks' },
- { id: 2, name: 'No tasks no employees' },
- { id: 3, name: 'No employees' },
- { id: 4, name: 'In progress A' },
- { id: 5, name: 'In progress B' },
- { id: 6, name: 'In progress C' },
- ]),
- Task.bulkCreate([
- { name: 'Important task', fk: 3 },
- { name: 'Important task', fk: 4 },
- { name: 'Important task', fk: 5 },
- { name: 'Important task', fk: 6 },
- ]),
- Employee.bulkCreate([
- { name: 'Jane Doe', fk: 1 },
- { name: 'John Doe', fk: 4 },
- { name: 'Jane John Doe', fk: 5 },
- { name: 'John Jane Doe', fk: 6 },
- ]),
- ]);
- // Find all projects with tasks and employees
- const availableProjects = 3;
- const limit = 2;
- const result = await Project.findAndCountAll({
- include: [
- {
- model: Task,
- required: true,
- },
- {
- model: Employee,
- required: true,
- },
- ],
- limit,
- });
- expect(result.count).to.equal(availableProjects);
- expect(result.rows.length).to.equal(
- limit,
- 'Complete set of available rows were not returned.',
- );
- });
- it('should be able to include a required model. Result rows should match count', async function () {
- const User = this.sequelize.define(
- 'User',
- { name: DataTypes.STRING(40) },
- { paranoid: true },
- );
- const SomeConnection = this.sequelize.define(
- 'SomeConnection',
- {
- m: DataTypes.STRING(40),
- fk: DataTypes.INTEGER,
- u: DataTypes.INTEGER,
- },
- { paranoid: true },
- );
- const A = this.sequelize.define('A', { name: DataTypes.STRING(40) }, { paranoid: true });
- const B = this.sequelize.define('B', { name: DataTypes.STRING(40) }, { paranoid: true });
- const C = this.sequelize.define('C', { name: DataTypes.STRING(40) }, { paranoid: true });
- // Associate them
- User.hasMany(SomeConnection, { foreignKey: 'u', foreignKeyConstraints: false });
- SomeConnection.belongsTo(User, { foreignKey: 'u', foreignKeyConstraints: false });
- SomeConnection.belongsTo(A, { foreignKey: 'fk', foreignKeyConstraints: false });
- SomeConnection.belongsTo(B, { foreignKey: 'fk', foreignKeyConstraints: false });
- SomeConnection.belongsTo(C, { foreignKey: 'fk', foreignKeyConstraints: false });
- A.hasMany(SomeConnection, { foreignKey: 'fk', foreignKeyConstraints: false });
- B.hasMany(SomeConnection, { foreignKey: 'fk', foreignKeyConstraints: false });
- C.hasMany(SomeConnection, { foreignKey: 'fk', foreignKeyConstraints: false });
- // Sync them
- await this.sequelize.sync({ force: true });
- // Create an enviroment
- await Promise.all([
- User.bulkCreate([
- { name: 'Youtube' },
- { name: 'Facebook' },
- { name: 'Google' },
- { name: 'Yahoo' },
- { name: '404' },
- ]),
- SomeConnection.bulkCreate([
- // Lets count, m: A and u: 1
- { u: 1, m: 'A', fk: 1 }, // 1 // Will be deleted
- { u: 2, m: 'A', fk: 1 },
- { u: 3, m: 'A', fk: 1 },
- { u: 4, m: 'A', fk: 1 },
- { u: 5, m: 'A', fk: 1 },
- { u: 1, m: 'B', fk: 1 },
- { u: 2, m: 'B', fk: 1 },
- { u: 3, m: 'B', fk: 1 },
- { u: 4, m: 'B', fk: 1 },
- { u: 5, m: 'B', fk: 1 },
- { u: 1, m: 'C', fk: 1 },
- { u: 2, m: 'C', fk: 1 },
- { u: 3, m: 'C', fk: 1 },
- { u: 4, m: 'C', fk: 1 },
- { u: 5, m: 'C', fk: 1 },
- { u: 1, m: 'A', fk: 2 }, // 2 // Will be deleted
- { u: 4, m: 'A', fk: 2 },
- { u: 2, m: 'A', fk: 2 },
- { u: 1, m: 'A', fk: 3 }, // 3
- { u: 2, m: 'A', fk: 3 },
- { u: 3, m: 'A', fk: 3 },
- { u: 2, m: 'B', fk: 2 },
- { u: 1, m: 'A', fk: 4 }, // 4
- { u: 4, m: 'A', fk: 2 },
- ]),
- A.bulkCreate([
- { name: 'Just' },
- { name: 'for' },
- { name: 'testing' },
- { name: 'proposes' },
- { name: 'only' },
- ]),
- B.bulkCreate([{ name: 'this should not' }, { name: 'be loaded' }]),
- C.bulkCreate([{ name: 'because we only want A' }]),
- ]);
- // Delete some of conns to prove the concept
- await SomeConnection.destroy({
- where: {
- m: 'A',
- u: 1,
- fk: [1, 2],
- },
- });
- this.clock.tick(1000);
- // Last and most important queries ( we connected 4, but deleted 2, witch means we must get 2 only )
- const result = await A.findAndCountAll({
- include: [
- {
- model: SomeConnection,
- required: true,
- where: {
- m: 'A', // Pseudo Polymorphy
- u: 1,
- },
- },
- ],
- limit: 5,
- });
- expect(result.count).to.equal(2);
- expect(result.rows.length).to.equal(2);
- });
- it('should count on a where and not use an uneeded include', async function () {
- const Project = this.sequelize.define('Project', {
- id: { type: DataTypes.INTEGER, allowNull: false, primaryKey: true, autoIncrement: true },
- project_name: { type: DataTypes.STRING },
- });
- const User = this.sequelize.define('User', {
- id: { type: DataTypes.INTEGER, allowNull: false, primaryKey: true, autoIncrement: true },
- user_name: { type: DataTypes.STRING },
- });
- User.hasMany(Project);
- await User.sync({ force: true });
- await Project.sync({ force: true });
- const results = await Promise.all([
- User.create(),
- Project.create(),
- Project.create(),
- Project.create(),
- ]);
- const user = results[0];
- const userId = user.id;
- await user.setProjects([results[1], results[2], results[3]]);
- const result = await User.findAndCountAll({
- where: { id: userId },
- include: [Project],
- distinct: true,
- });
- expect(result.rows.length).to.equal(1);
- expect(result.rows[0].projects.length).to.equal(3);
- expect(result.count).to.equal(1);
- });
- it('should return the correct count and rows when using a required belongsTo and a limit', async function () {
- const s = this.sequelize;
- const Foo = s.define('Foo', {});
- const Bar = s.define('Bar', {});
- Foo.hasMany(Bar);
- Bar.belongsTo(Foo);
- await s.sync({ force: true });
- // Make five instances of Foo
- await Foo.bulkCreate([{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]);
- // Make four instances of Bar, related to the last four instances of Foo
- await Bar.bulkCreate([{ fooId: 2 }, { fooId: 3 }, { fooId: 4 }, { fooId: 5 }]);
- // Query for the first two instances of Foo which have related Bars
- const result0 = await Foo.findAndCountAll({
- include: [{ model: Bar, required: true }],
- limit: 2,
- });
- const items = await Foo.findAll({
- include: [{ model: Bar, required: true }],
- limit: 2,
- });
- expect(items.length).to.equal(2);
- const result = result0;
- expect(result.count).to.equal(4);
- // The first two of those should be returned due to the limit (Foo
- // instances 2 and 3)
- expect(result.rows.length).to.equal(2);
- });
- it('should return the correct count and rows when using a required belongsTo with a where condition and a limit', async function () {
- const Foo = this.sequelize.define('Foo', {});
- const Bar = this.sequelize.define('Bar', { m: DataTypes.STRING(40) });
- Foo.hasMany(Bar);
- Bar.belongsTo(Foo);
- await this.sequelize.sync({ force: true });
- await Foo.bulkCreate([{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]);
- // Make four instances of Bar, related to the first two instances of Foo
- await Bar.bulkCreate([
- { fooId: 1, m: 'yes' },
- { fooId: 1, m: 'yes' },
- { fooId: 1, m: 'no' },
- { fooId: 2, m: 'yes' },
- ]);
- // Query for the first instance of Foo which have related Bars with m === 'yes'
- const result = await Foo.findAndCountAll({
- include: [{ model: Bar, where: { m: 'yes' } }],
- limit: 1,
- distinct: true,
- });
- // There should be 2 instances matching the query (Instances 1 and 2), see the findAll statement
- expect(result.count).to.equal(2);
- // The first one of those should be returned due to the limit (Foo instance 1)
- expect(result.rows.length).to.equal(1);
- });
- it('should correctly filter, limit and sort when multiple includes and types of associations are present.', async function () {
- const TaskTag = this.sequelize.define('TaskTag', {
- id: { type: DataTypes.INTEGER, allowNull: false, primaryKey: true, autoIncrement: true },
- name: { type: DataTypes.STRING },
- });
- const Tag = this.sequelize.define('Tag', {
- id: { type: DataTypes.INTEGER, allowNull: false, primaryKey: true, autoIncrement: true },
- name: { type: DataTypes.STRING },
- });
- const Task = this.sequelize.define('Task', {
- id: { type: DataTypes.INTEGER, allowNull: false, primaryKey: true, autoIncrement: true },
- name: { type: DataTypes.STRING },
- });
- const Project = this.sequelize.define('Project', {
- id: { type: DataTypes.INTEGER, allowNull: false, primaryKey: true, autoIncrement: true },
- m: { type: DataTypes.STRING },
- });
- const User = this.sequelize.define('User', {
- id: { type: DataTypes.INTEGER, allowNull: false, primaryKey: true, autoIncrement: true },
- name: { type: DataTypes.STRING },
- });
- Project.belongsTo(User);
- Task.belongsTo(Project);
- Task.belongsToMany(Tag, { through: TaskTag });
- // Sync them
- await this.sequelize.sync({ force: true });
- // Create an enviroment
- await User.bulkCreate([{ name: 'user-name-1' }, { name: 'user-name-2' }]);
- await Project.bulkCreate([
- { m: 'A', userId: 1 },
- { m: 'A', userId: 2 },
- ]);
- await Task.bulkCreate([
- { projectId: 1, name: 'Just' },
- { projectId: 1, name: 'for' },
- { projectId: 2, name: 'testing' },
- { projectId: 2, name: 'proposes' },
- ]);
- // Find All Tasks with Project(m=a) and User(name=user-name-2)
- const result = await Task.findAndCountAll({
- limit: 1,
- offset: 0,
- order: [['id', 'DESC']],
- include: [
- {
- model: Project,
- where: { [Op.and]: [{ m: 'A' }] },
- include: [
- {
- model: User,
- where: { [Op.and]: [{ name: 'user-name-2' }] },
- },
- ],
- },
- { model: Tag },
- ],
- });
- expect(result.count).to.equal(2);
- expect(result.rows.length).to.equal(1);
- });
- it('should properly work with sequelize.function', async function () {
- const sequelize = this.sequelize;
- const User = this.sequelize.define('User', {
- id: { type: DataTypes.INTEGER, allowNull: false, primaryKey: true, autoIncrement: true },
- first_name: { type: DataTypes.STRING },
- last_name: { type: DataTypes.STRING },
- });
- const Project = this.sequelize.define('Project', {
- id: { type: DataTypes.INTEGER, allowNull: false, primaryKey: true, autoIncrement: true },
- name: { type: DataTypes.STRING },
- });
- User.hasMany(Project);
- await this.sequelize.sync({ force: true });
- await User.bulkCreate([
- { first_name: 'user-fname-1', last_name: 'user-lname-1' },
- { first_name: 'user-fname-2', last_name: 'user-lname-2' },
- { first_name: 'user-xfname-1', last_name: 'user-xlname-1' },
- ]);
- await Project.bulkCreate([
- { name: 'naam-satya', userId: 1 },
- { name: 'guru-satya', userId: 2 },
- { name: 'app-satya', userId: 2 },
- ]);
- const result = await User.findAndCountAll({
- limit: 1,
- offset: 1,
- where: sequelize.or(
- { first_name: { [Op.like]: '%user-fname%' } },
- { last_name: { [Op.like]: '%user-lname%' } },
- ),
- include: [
- {
- model: Project,
- required: true,
- where: {
- name: {
- [Op.in]: ['naam-satya', 'guru-satya'],
- },
- },
- },
- ],
- });
- expect(result.count).to.equal(2);
- expect(result.rows.length).to.equal(1);
- });
- });
- });
|