Sequelize — это основанная на обещаниях ORM Node.js для Postgres, MySQL, MariaDB, SQLite и Microsoft SQL Server.
Sequalize обеспечивает надежную поддержку транзакций, отношения и т.д.
Допустим у вас есть модель:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
module.exports = (sequelize, DataTypes) => { const SupplierProfile = sequelize.define('SupplierProfile', { supplierId: { autoIncrement: true, primaryKey: true, type: DataTypes.INTEGER }, partnerId: DataTypes.INTEGER, active: DataTypes.INTEGER, name: DataTypes.STRING(256), typeDownload: DataTypes.STRING(256), deliveryPeriod: DataTypes.STRING(256), createdId: DataTypes.INTEGER, updatedId: DataTypes.INTEGER }, { tableName: 'coderun_supplier_profile' }) SupplierProfile.associate = function (models) { SupplierProfile.hasMany(models.Statistic, { foreignKey: 'supplierId', targetKey: 'supplierId', as: 'statistic' }) } return SupplierProfile } |
В этом случае SupplierProfile ассоциирована с Statistic как один к многим.
Например:
1-й записи таблице SupplierProfile с полем supplierId = 1 соответствует 15 записей из Statistic с supplierId = 1
В запросе нам необходимо получить строго 1-у запись из Statistic, первое что приходит в голову это использовать в includ ключ limit с значением 1:
Не верный запрос:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
rsItems = await SupplierProfile.findAll({ attributes: ['supplierId', 'partnerId', 'typeDownload'], where: where, raw: false, offset: offset, limit: rows, order: [orderProfile], include: [ { model: Partners, attributes: ['name', 'code', 'partnerId'], as: 'partner' }, { model: Statistic, attributes: ['loadedItem', 'updatedAt', 'statisticId'], as: 'statistic', order: [ ['statisticId', 'DESC'] ], limit: 1 } ], logging: (sql) => { console.log(sql) } }) |
С ходу этот запрос вызовет ошибку, самая частая из них это «TypeError: Cannot read property ‘push’ of undefined»
Дело в том что, пытаясь ограничить данные из Statistic мы вынуждаем sequalize сделать два запроса в базу данных, но когда приоходят данные от первого запроса — sequalize уже не знает как связать данные из Statistic и SupplierProfile, потому что в атрибутах Statistic не указано поле через которое связаны Statistic и SupplierProfile. В нашем случае это supplierId
Тогда правельный запрос с ограничением limit 1 для findAll и ассоцииации hasMany будет следующий:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
rsItems = await SupplierProfile.findAll({ attributes: ['supplierId', 'partnerId', 'typeDownload'], where: where, raw: false, offset: offset, limit: rows, order: [orderProfile], include: [ { model: Partners, attributes: ['name', 'code', 'partnerId'], as: 'partner' }, { model: Statistic, attributes: ['supplierId', 'loadedItem', 'updatedAt', 'statisticId'], as: 'statistic', order: [ ['statisticId', 'DESC'] ], limit: 1 } ], logging: (sql) => { console.log(sql) } }) |
Как видно в модели Statistic к значениям attributes мы добавили поле supplierId.
Тогда если разложить на чистые sql запросы которые делает sequalize, получаем следующее:
1 -й запрос:
Извлекаются все записи из таблицы SupplierProfile
1 2 3 4 5 6 7 8 9 10 11 |
SELECT `SupplierProfile`.`supplierId`, `SupplierProfile`.`partnerId`, `SupplierProfile`.`typeDownload`, `partner`.`name` AS `partner.name`, `partner`.`code` AS `partner.code`, `partner`.`partnerId` AS `partner.partnerId` FROM `coderun_supplier_profile` AS `SupplierProfile` LEFT OUTER JOIN `coderun_partners` AS `partner` ON `SupplierProfile`.`partnerId` = `partner`.`partnerId` WHERE `SupplierProfile`.`active` = '1' ORDER BY `SupplierProfile`.`supplierId` DESC LIMIT 0, 20 |
2-й запрос(только часть):
По каждой записи из SupplierProfile делается SELECT в таблицу Statistic
1 2 3 4 5 6 7 8 9 10 11 |
SELECT `Statistic`.* FROM (SELECT * FROM (SELECT `supplierId`, `loadedItem`, `updatedAt`, `statisticId` FROM `coderun_supplier_statistic` AS `Statistic` WHERE `Statistic`.`supplierId` = 12 ORDER BY `Statistic`.`statisticId` DESC LIMIT 1) AS sub UNION ALL SELECT * FROM (SELECT `supplierId`, `loadedItem`, `updatedAt`, `statisticId` FROM `coderun_supplier_statistic` AS `Statistic` WHERE `Statistic`.`supplierId` = 11 .......... |