123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- 'use strict';
- const upperFirst = require('lodash/upperFirst');
- const chai = require('chai');
- const expect = chai.expect;
- const Support = require('../support');
- const { DataTypes, sql } = require('@sequelize/core');
- describe(Support.getTestDialectTeaser('Include'), () => {
- describe('findOne', () => {
- it('should include a non required model, with conditions and two includes N:M 1:M', async function () {
- 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 });
- const D = this.sequelize.define('D', { name: DataTypes.STRING(40) }, { paranoid: true });
- // Associations
- A.hasMany(B);
- B.belongsTo(D);
- B.belongsToMany(C, {
- through: 'BC',
- });
- C.belongsToMany(B, {
- through: 'BC',
- });
- D.hasMany(B);
- await this.sequelize.sync({ force: true });
- await A.findOne({
- include: [
- {
- model: B,
- required: false,
- include: [{ model: C, required: false }, { model: D }],
- },
- ],
- });
- });
- it('should work with a 1:M to M:1 relation with a where on the last include', async function () {
- const Model = this.sequelize.define('Model', {});
- const Model2 = this.sequelize.define('Model2', {});
- const Model4 = this.sequelize.define('Model4', { something: { type: DataTypes.INTEGER } });
- Model.belongsTo(Model2);
- Model2.hasMany(Model);
- Model2.hasMany(Model4);
- Model4.belongsTo(Model2);
- await this.sequelize.sync({ force: true });
- await Model.findOne({
- include: [
- {
- model: Model2,
- include: [{ model: Model4, where: { something: 2 } }],
- },
- ],
- });
- });
- it('should include a model with a where condition but no required', async function () {
- const User = this.sequelize.define('User', {}, { paranoid: false });
- const Task = this.sequelize.define(
- 'Task',
- {
- deletedAt: {
- type: DataTypes.DATE,
- },
- },
- { paranoid: false },
- );
- User.hasMany(Task, { foreignKey: 'userId' });
- Task.belongsTo(User, { foreignKey: 'userId' });
- await this.sequelize.sync({
- force: true,
- });
- const user0 = await User.create();
- await Task.bulkCreate([
- { userId: user0.get('id'), deletedAt: new Date() },
- { userId: user0.get('id'), deletedAt: new Date() },
- { userId: user0.get('id'), deletedAt: new Date() },
- ]);
- const user = await User.findOne({
- include: [{ model: Task, where: { deletedAt: null }, required: false }],
- });
- expect(user).to.be.ok;
- expect(user.tasks.length).to.equal(0);
- });
- it('should include a model with a where clause when the PK field name and attribute name are different', async function () {
- const User = this.sequelize.define('User', {
- id: {
- type: DataTypes.UUID,
- defaultValue: sql.uuidV4,
- field: 'main_id',
- primaryKey: true,
- },
- });
- const Task = this.sequelize.define('Task', {
- searchString: { type: DataTypes.STRING },
- });
- User.hasMany(Task, { foreignKey: 'userId' });
- Task.belongsTo(User, { foreignKey: 'userId' });
- await this.sequelize.sync({
- force: true,
- });
- const user0 = await User.create();
- await Task.bulkCreate([
- { userId: user0.get('id'), searchString: 'one' },
- { userId: user0.get('id'), searchString: 'two' },
- ]);
- const user = await User.findOne({
- include: [{ model: Task, where: { searchString: 'one' } }],
- });
- expect(user).to.be.ok;
- expect(user.tasks.length).to.equal(1);
- });
- it('should include a model with a through.where and required true clause when the PK field name and attribute name are different', async function () {
- const A = this.sequelize.define('a', {});
- const B = this.sequelize.define('b', {});
- const AB = this.sequelize.define('a_b', {
- name: {
- type: DataTypes.STRING(40),
- field: 'name_id',
- primaryKey: true,
- },
- });
- A.belongsToMany(B, { through: AB });
- B.belongsToMany(A, { through: AB });
- await this.sequelize.sync({ force: true });
- const [a0, b] = await Promise.all([A.create({}), B.create({})]);
- await a0.addB(b, { through: { name: 'Foobar' } });
- const a = await A.findOne({
- include: [{ model: B, through: { where: { name: 'Foobar' } }, required: true }],
- });
- expect(a).to.not.equal(null);
- expect(a.get('bs')).to.have.length(1);
- });
- it('should still pull the main record when an included model is not required and has where restrictions without matches', async function () {
- const A = this.sequelize.define('a', {
- name: DataTypes.STRING(40),
- });
- const B = this.sequelize.define('b', {
- name: DataTypes.STRING(40),
- });
- A.belongsToMany(B, { through: 'a_b' });
- B.belongsToMany(A, { through: 'a_b' });
- await this.sequelize.sync({ force: true });
- await A.create({
- name: 'Foobar',
- });
- const a = await A.findOne({
- where: { name: 'Foobar' },
- include: [{ model: B, where: { name: 'idontexist' }, required: false }],
- });
- expect(a).to.not.equal(null);
- expect(a.get('bs')).to.deep.equal([]);
- });
- it('should support a nested include (with a where)', async function () {
- const A = this.sequelize.define('A', {
- name: DataTypes.STRING,
- });
- const B = this.sequelize.define('B', {
- flag: DataTypes.BOOLEAN,
- });
- const C = this.sequelize.define('C', {
- name: DataTypes.STRING,
- });
- A.hasOne(B);
- B.belongsTo(A);
- B.hasMany(C);
- C.belongsTo(B);
- await this.sequelize.sync({ force: true });
- const a = await A.findOne({
- include: [
- {
- model: B,
- where: { flag: true },
- include: [
- {
- model: C,
- },
- ],
- },
- ],
- });
- expect(a).to.not.exist;
- });
- it('should support a belongsTo with the targetKey option', async function () {
- const User = this.sequelize.define('User', {
- username: { type: DataTypes.STRING, unique: true },
- });
- const Task = this.sequelize.define('Task', { title: DataTypes.STRING });
- User.removeAttribute('id');
- Task.belongsTo(User, { foreignKey: 'user_name', targetKey: 'username' });
- await this.sequelize.sync({ force: true });
- const newUser = await User.create({ username: 'bob' });
- const newTask = await Task.create({ title: 'some task' });
- await newTask.setUser(newUser);
- const foundTask = await Task.findOne({
- where: { title: 'some task' },
- include: [{ model: User }],
- });
- expect(foundTask).to.be.ok;
- expect(foundTask.user.username).to.equal('bob');
- });
- it('should support many levels of belongsTo (with a lower level having a where)', async function () {
- const A = this.sequelize.define('a', {});
- const B = this.sequelize.define('b', {});
- const C = this.sequelize.define('c', {});
- const D = this.sequelize.define('d', {});
- const E = this.sequelize.define('e', {});
- const F = this.sequelize.define('f', {});
- const G = this.sequelize.define('g', {
- name: DataTypes.STRING,
- });
- const H = this.sequelize.define('h', {
- name: DataTypes.STRING,
- });
- A.belongsTo(B);
- B.belongsTo(C);
- C.belongsTo(D);
- D.belongsTo(E);
- E.belongsTo(F);
- F.belongsTo(G);
- G.belongsTo(H);
- await this.sequelize.sync({ force: true });
- const [a0, b] = await Promise.all([
- A.create({}),
- (function (singles) {
- let promise = Promise.resolve();
- let previousInstance;
- let b;
- for (const model of singles) {
- const values = {};
- if (model.name === 'g') {
- values.name = 'yolo';
- }
- promise = (async () => {
- await promise;
- const instance = await model.create(values);
- if (previousInstance) {
- await previousInstance[`set${upperFirst(model.name)}`](instance);
- previousInstance = instance;
- return;
- }
- previousInstance = b = instance;
- })();
- }
- promise = promise.then(() => {
- return b;
- });
- return promise;
- })([B, C, D, E, F, G, H]),
- ]);
- await a0.setB(b);
- const a = await A.findOne({
- include: [
- {
- model: B,
- include: [
- {
- model: C,
- include: [
- {
- model: D,
- include: [
- {
- model: E,
- include: [
- {
- model: F,
- include: [
- {
- model: G,
- where: {
- name: 'yolo',
- },
- include: [{ model: H }],
- },
- ],
- },
- ],
- },
- ],
- },
- ],
- },
- ],
- },
- ],
- });
- expect(a.b.c.d.e.f.g.h).to.be.ok;
- });
- it('should work with combinding a where and a scope', async function () {
- const User = this.sequelize.define(
- 'User',
- {
- id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
- name: DataTypes.STRING,
- },
- { underscored: true },
- );
- const Post = this.sequelize.define(
- 'Post',
- {
- id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, unique: true },
- owner_id: { type: DataTypes.INTEGER, unique: 'combiIndex' },
- owner_type: {
- type: DataTypes.ENUM(['user', 'org']),
- defaultValue: 'user',
- unique: 'combiIndex',
- },
- private: { type: DataTypes.BOOLEAN, defaultValue: false },
- },
- { underscored: true },
- );
- User.hasMany(Post, {
- foreignKey: 'owner_id',
- scope: { owner_type: 'user' },
- as: 'UserPosts',
- foreignKeyConstraints: false,
- });
- Post.belongsTo(User, { foreignKey: 'owner_id', as: 'Owner', foreignKeyConstraints: false });
- await this.sequelize.sync({ force: true });
- await User.findOne({
- where: { id: 2 },
- include: [{ model: Post, as: 'UserPosts', where: { private: true } }],
- });
- });
- });
- });
|