123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- 'use strict';
- const at = require('lodash/at');
- const { beforeAll2, expectsql, sequelize } = require('../../support');
- const { DataTypes, Model, Op } = require('@sequelize/core');
- const {
- _validateIncludedElements,
- } = require('@sequelize/core/_non-semver-use-at-your-own-risk_/model-internals.js');
- const sql = sequelize.queryGenerator;
- describe('QueryGenerator#generateJoin', () => {
- const expectJoin = function (path, options, expectation) {
- Model._conformIncludes(options, options.model);
- options = _validateIncludedElements(options);
- const include = at(options, path)[0];
- const join = sql.generateJoin(include, {
- options,
- subQuery:
- options.subQuery === undefined
- ? options.limit && options.hasMultiAssociation
- : options.subQuery,
- });
- return expectsql(`${join.join} ${join.body} ON ${join.condition}`, expectation);
- };
- const vars = beforeAll2(() => {
- const User = sequelize.define(
- 'User',
- {
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
- field: 'id_user',
- },
- companyId: {
- type: DataTypes.INTEGER,
- field: 'company_id',
- },
- },
- {
- tableName: 'user',
- },
- );
- const Task = sequelize.define(
- 'Task',
- {
- title: DataTypes.STRING,
- userId: {
- type: DataTypes.INTEGER,
- field: 'user_id',
- },
- },
- {
- tableName: 'task',
- },
- );
- const Company = sequelize.define(
- 'Company',
- {
- name: DataTypes.STRING,
- ownerId: {
- type: DataTypes.INTEGER,
- field: 'owner_id',
- },
- public: {
- type: DataTypes.BOOLEAN,
- },
- },
- {
- tableName: 'company',
- },
- );
- const Profession = sequelize.define(
- 'Profession',
- {
- name: DataTypes.STRING,
- },
- {
- tableName: 'profession',
- },
- );
- User.Tasks = User.hasMany(Task, { as: 'Tasks', foreignKey: 'userId', inverse: 'User' });
- User.Company = User.belongsTo(Company, { as: 'Company', foreignKey: 'companyId' });
- User.Profession = User.belongsTo(Profession, { as: 'Profession', foreignKey: 'professionId' });
- Profession.Professionals = Profession.hasMany(User, {
- as: 'Professionals',
- foreignKey: 'professionId',
- inverse: 'Profession',
- });
- Company.Employees = Company.hasMany(User, {
- as: 'Employees',
- foreignKey: 'companyId',
- inverse: 'Company',
- });
- Company.Owner = Company.belongsTo(User, { as: 'Owner', foreignKey: 'ownerId' });
- return { User, Task, Company, Profession };
- });
- /*
- * BelongsTo
- */
- it('Generates a join query for a belongsTo association', () => {
- const { User } = vars;
- expectJoin(
- 'include[0]',
- {
- model: User,
- include: [User.Company],
- },
- {
- default: 'LEFT OUTER JOIN [company] AS [Company] ON [User].[company_id] = [Company].[id]',
- },
- );
- });
- it('Generates a belongsTo join query with an extra OR "on" condition', () => {
- const { User } = vars;
- expectJoin(
- 'include[0]',
- {
- model: User,
- include: [
- {
- association: User.Company,
- where: { public: true },
- or: true,
- },
- ],
- },
- {
- default:
- 'INNER JOIN [company] AS [Company] ON [User].[company_id] = [Company].[id] OR [Company].[public] = true',
- ibmi: 'INNER JOIN "company" AS "Company" ON "User"."company_id" = "Company"."id" OR "Company"."public" = 1',
- sqlite3:
- 'INNER JOIN `company` AS `Company` ON `User`.`company_id` = `Company`.`id` OR `Company`.`public` = 1',
- mssql:
- 'INNER JOIN [company] AS [Company] ON [User].[company_id] = [Company].[id] OR [Company].[public] = 1',
- },
- );
- });
- it('Generates a nested belongsTo join query', () => {
- const { Profession, User } = vars;
- expectJoin(
- 'include[0].include[0]',
- {
- model: Profession,
- include: [
- {
- association: Profession.Professionals,
- limit: 3,
- include: [User.Company],
- },
- ],
- },
- {
- default:
- 'LEFT OUTER JOIN [company] AS [Professionals->Company] ON [Professionals].[company_id] = [Professionals->Company].[id]',
- },
- );
- });
- it('supports subQuery = true', () => {
- const { User } = vars;
- expectJoin(
- 'include[0]',
- {
- model: User,
- subQuery: true,
- include: [User.Company],
- },
- {
- default: 'LEFT OUTER JOIN [company] AS [Company] ON [User].[companyId] = [Company].[id]',
- },
- );
- });
- it('supports subQuery = true with required = false and nested WHERE', () => {
- const { User } = vars;
- expectJoin(
- 'include[0]',
- {
- model: User,
- subQuery: true,
- include: [
- {
- association: User.Company,
- required: false,
- where: { name: 'ABC' },
- },
- ],
- },
- {
- default:
- "LEFT OUTER JOIN [company] AS [Company] ON [User].[companyId] = [Company].[id] AND [Company].[name] = 'ABC'",
- mssql:
- "LEFT OUTER JOIN [company] AS [Company] ON [User].[companyId] = [Company].[id] AND [Company].[name] = N'ABC'",
- },
- );
- });
- it('supports "right = true"', () => {
- const { User } = vars;
- expectJoin(
- 'include[0]',
- {
- model: User,
- subQuery: true,
- include: [
- {
- association: User.Company,
- right: true,
- },
- ],
- },
- {
- default: `${sequelize.dialect.supports['RIGHT JOIN'] ? 'RIGHT' : 'LEFT'} OUTER JOIN [company] AS [Company] ON [User].[companyId] = [Company].[id]`,
- },
- );
- });
- it('supports nested includes with subQuery = true', () => {
- const { Company, User } = vars;
- expectJoin(
- 'include[0].include[0]',
- {
- subQuery: true,
- model: User,
- include: [
- {
- association: User.Company,
- include: [Company.Owner],
- },
- ],
- },
- {
- default:
- 'LEFT OUTER JOIN [user] AS [Company->Owner] ON [Company].[owner_id] = [Company->Owner].[id_user]',
- },
- );
- });
- it('supports double nested includes', () => {
- const { Company, User } = vars;
- expectJoin(
- 'include[0].include[0].include[0]',
- {
- model: User,
- subQuery: true,
- include: [
- {
- association: User.Company,
- include: [
- {
- association: Company.Owner,
- include: [User.Profession],
- },
- ],
- },
- ],
- },
- {
- default:
- 'LEFT OUTER JOIN [profession] AS [Company->Owner->Profession] ON [Company->Owner].[professionId] = [Company->Owner->Profession].[id]',
- },
- );
- });
- it('supports nested includes with required = true', () => {
- const { Company, User } = vars;
- expectJoin(
- 'include[0].include[0]',
- {
- model: User,
- subQuery: true,
- include: [
- {
- association: User.Company,
- required: true,
- include: [Company.Owner],
- },
- ],
- },
- {
- default:
- 'LEFT OUTER JOIN [user] AS [Company->Owner] ON [Company].[owner_id] = [Company->Owner].[id_user]',
- },
- );
- });
- it('supports required = true', () => {
- const { User } = vars;
- expectJoin(
- 'include[0]',
- {
- model: User,
- subQuery: true,
- include: [{ association: User.Company, required: true }],
- },
- {
- default: 'INNER JOIN [company] AS [Company] ON [User].[companyId] = [Company].[id]',
- },
- );
- });
- // /*
- // * HasMany
- // */
- it('supports hasMany', () => {
- const { User } = vars;
- expectJoin(
- 'include[0]',
- {
- model: User,
- include: [User.Tasks],
- },
- { default: 'LEFT OUTER JOIN [task] AS [Tasks] ON [User].[id_user] = [Tasks].[user_id]' },
- );
- });
- it('supports hasMany with subQuery = true', () => {
- const { User } = vars;
- expectJoin(
- 'include[0]',
- {
- model: User,
- subQuery: true,
- include: [User.Tasks],
- },
- {
- // The primary key of the main model will be aliased because it's coming from a subquery that the :M join is not a part of
- default: 'LEFT OUTER JOIN [task] AS [Tasks] ON [User].[id] = [Tasks].[user_id]',
- },
- );
- });
- it('supports hasMany with "on" condition', () => {
- const { User } = vars;
- expectJoin(
- 'include[0]',
- {
- model: User,
- include: [
- {
- association: User.Tasks,
- on: {
- [Op.or]: [
- { '$User.id_user$': { [Op.col]: 'Tasks.user_id' } },
- { '$Tasks.user_id$': 2 },
- ],
- },
- },
- ],
- },
- {
- default:
- 'LEFT OUTER JOIN [task] AS [Tasks] ON [User].[id_user] = [Tasks].[user_id] OR [Tasks].[user_id] = 2',
- },
- );
- });
- it('supports hasMany with "on" condition (2)', () => {
- const { User } = vars;
- expectJoin(
- 'include[0]',
- {
- model: User,
- include: [
- {
- association: User.Tasks,
- on: { user_id: { [Op.col]: 'User.alternative_id' } },
- },
- ],
- },
- {
- default: 'LEFT OUTER JOIN [task] AS [Tasks] ON [Tasks].[user_id] = [User].[alternative_id]',
- },
- );
- });
- it('supports nested hasMany', () => {
- const { Company, User } = vars;
- expectJoin(
- 'include[0].include[0]',
- {
- subQuery: true,
- model: User,
- include: [
- {
- association: User.Company,
- include: [
- {
- association: Company.Owner,
- on: {
- [Op.or]: [
- { '$Company.owner_id$': { [Op.col]: 'Company.Owner.id_user' } },
- { '$Company.Owner.id_user$': 2 },
- ],
- },
- },
- ],
- },
- ],
- },
- {
- default:
- 'LEFT OUTER JOIN [user] AS [Company->Owner] ON [Company].[owner_id] = [Company->Owner].[id_user] OR [Company->Owner].[id_user] = 2',
- },
- );
- });
- });
|