А так же о всякой фигне
В этот раз мы углубимся в join реквесты, сеттеры и ещё пару вещей, а для затравки разберём пару простых методов:
let value = await model.count({});
Вернёт целое значение, количество строк в таблице. Можно использовать условия, так же как и в findAll:
let value = await model.count({ where: { someVal: 123 }});
let minVal = await model.min('fieldName', {}); let maxVal = await model.max('fieldname', {});
fieldName - имя колонки. Условия поиска передаются вторым параметром:
let minVal = await model.min('fieldName', { where: { ... } }); let maxVal = await model.max('fieldname', { where: { ... } });
Давайте возьмём 2 таблички из прошлого примера:
posts: id | title | body comments: id | posts_id | userName | comment
Между ними нужно добавить ассоциацию:
posts.belongsTo(comments, { as: 'post_comments', foreignKey: 'id', targetKey: 'posts_id'});
И прежде чем выполнять запрос, нужно добавить инклюд:
let queryParams = { where: {}, include: [ { model: sequelize.models.comments, as: 'post_comments', //attributes: [ 'id', 'name' ] } ] }; let data1 = await sequelize.models.posts.findAll(queryParams);
Эта конструкция выполнит следующий запрос:
SELECT `posts`.`id`, `posts`.`title`, `posts`.`body`, `posts`.`createdAt`, `posts`.`updatedAt`, `post_comments`.`id` AS `post_comments.id`, `post_comments`.`posts_id` AS `post_comments.posts_id`, `post_comments`.`userName` AS `post_comments.userName`, `post_comments`.`comment` AS `post_comments.comment`, `post_comments`.`createdAt` AS `post_comments.createdAt`, `post_comments`.`updatedAt` AS `post_comments.updatedAt` FROM `posts` AS `posts` LEFT OUTER JOIN `comments` AS `post_comments` ON `posts`.`id` = `post_comments`.`posts_id`;
Но в отличии от оригинального sql, данные по комментам будут лежать не в одной строке, а в отдельном объекте который мы назвали post_comments:
{ id: 1, title: 'Post title 1', body: 'Ololo ololo ya voditel NLO', createdAt: 2019-01-26T07:22:50.034Z, updatedAt: 2019-01-26T07:22:50.034Z, post_comments: comments { dataValues: { id: 1, posts_id: 1, userName: 'Ololoev_O', comment: 'Preved medved', createdAt: 2019-01-27T05:20:05.348Z, updatedAt: 2019-01-27T05:41:47.133Z }, } }
Если данных для post_comments не будет то там будет лежать нулл.
Так же как и в прошлом примере нужно сделать инклюд в параметрах:
let queryParams = { where: {}, include: [ { model: sequelize.models.comments, as: 'post_comments', required: true, // ОТЛИЧИЯ ТУТ <<<---- //attributes: [ 'id', 'name' ] } ] }; let data1 = await sequelize.models.posts.findAll(queryParams);
Отличия заключаются в поле "required: true". В результате выполнится запрос:
SELECT `posts`.`id`, `posts`.`title`, `posts`.`body`, `posts`.`createdAt`, `posts`.`updatedAt`, `post_comments`.`id` AS `post_comments.id`, `post_comments`.`posts_id` AS `post_comments.posts_id`, `post_comments`.`userName` AS `post_comments.userName`, `post_comments`.`comment` AS `post_comments.comment`, `post_comments`.`createdAt` AS `post_comments.createdAt`, `post_comments`.`updatedAt` AS `post_comments.updatedAt` FROM `posts` AS `posts` INNER JOIN `comments` AS `post_comments` ON `posts`.`id` = `post_comments`.`posts_id`;
И в этом случае, если данных для post_comments не будет, то НЕ вернётся вся строка целиком.
Ещё больше всяких интересных примеров с джоинами можно посмотреть в доке: http://docs.sequelizejs.com/manual/tutorial/associations.html
Простая сортировка по какому-нить столбцу:
let queryParams = { where: {}, order: [ [ 'id', 'ASC' ] ] }; let data1 = await sequelize.models.posts.findAll(queryParams);
Если нужно отсортировать по вложенной модели:
let queryParams = { where: {}, order: [ [ 'имя_ассоциации', 'столбец', 'тип сортировки' ] ] }; let data1 = await sequelize.models.posts.findAll(queryParams);
Ну и если нужно отсортировать по вложенной модели, которая вложена в ещё одну модель то:
let queryParams = { where: {}, order: [ [ 'имя_ассоциации', 'имя_ещё_более_глубокой_ассоциации', 'столбец', 'тип сортировки' ] ] }; let data1 = await sequelize.models.posts.findAll(queryParams);
let queryParams = { where: {}, group: [ 'title' ] }; let data1 = await sequelize.models.posts.findAll(queryParams);
Эквивалентно запросу:
select * from posts group by title
Для примера, добавить в табличку posts поле randomVal
randomVal: { type: Sequelize.DataTypes.STRING, set (val) { // делаем что-то с переданным val // И присваиваем значение this.setDataValue('randomVal', `${val}-test-${Math.random()}`); } }
И когда мы будем добавлять данные в табличку, то значение будет автоматически перегенерироваться:
await sequelize.models.posts.create({ title: 'Setter-test1', body: 'Setter-body', randomVal: '123' });
Получим:
{ id: 1, title: 'Setter-test1', body: 'Setter-body', randomVal: '123-test-0.7252707392753635', createdAt: 2019-02-05T17:34:03.918Z, updatedAt: 2019-02-05T17:34:03.918Z }
Сеттеры очень удобно использовать для полей, которые автоматически генерируются, или генерируются на основе каких-то других полей.
Тут лучше чем в оригинальной документации не напишешь:
const ValidateMe = sequelize.define('foo', { foo: { type: Sequelize.STRING, validate: { is: ["^[a-z]+$",'i'], // will only allow letters is: /^[a-z]+$/i, // same as the previous example using real RegExp not: ["[a-z]",'i'], // will not allow letters isEmail: true, // checks for email format (foo@bar.com) isUrl: true, // checks for url format (http://foo.com) isIP: true, // checks for IPv4 (129.89.23.1) or IPv6 format isIPv4: true, // checks for IPv4 (129.89.23.1) isIPv6: true, // checks for IPv6 format isAlpha: true, // will only allow letters isAlphanumeric: true, // will only allow alphanumeric characters, so "_abc" will fail isNumeric: true, // will only allow numbers isInt: true, // checks for valid integers isFloat: true, // checks for valid floating point numbers isDecimal: true, // checks for any numbers isLowercase: true, // checks for lowercase isUppercase: true, // checks for uppercase notNull: true, // won't allow null isNull: true, // only allows null notEmpty: true, // don't allow empty strings equals: 'specific value', // only allow a specific value contains: 'foo', // force specific substrings notIn: [['foo', 'bar']], // check the value is not one of these isIn: [['foo', 'bar']], // check the value is one of these notContains: 'bar', // don't allow specific substrings len: [2,10], // only allow values with length between 2 and 10 isUUID: 4, // only allow uuids isDate: true, // only allow date strings isAfter: "2011-11-05", // only allow date strings after a specific date isBefore: "2011-11-05", // only allow date strings before a specific date max: 23, // only allow values <= 23 min: 23, // only allow values >= 23 isCreditCard: true, // check for valid credit card numbers // custom validations are also possible: isEven(value) { if (parseInt(value) % 2 != 0) { throw new Error('Only even values are allowed!') // we also are in the model's context here, so this.otherField // would get the value of otherField if it existed } } } } });
По умолчанию на все autoincrement поля сикулайз автоматически добавить BTREE индекс. А вот на остальные поля, индексы нужно добавлять вручную, например давайте добавим тот же BTREE на наше поле randomVal. Модифицируем нашу модельку:
let posts = sequelize.define('posts', { id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.DataTypes.INTEGER }, title: { type: Sequelize.DataTypes.STRING, allowNull: false }, body: { type: Sequelize.DataTypes.STRING, }, randomVal: { type: Sequelize.DataTypes.STRING, set (val) { // делаем что-то с переданным val // И присваиваем значение this.setDataValue('randomVal', `${val}-test-${Math.random()}`); } } }, { timestamps: true, // Колонки createdAt и updatedAt будут созданы автоматически indexes: [ // вот тут и описываются индексы <<<---- { name: 'posts_randomVal', // Имя индекса method: 'BTREE', // Тип fields: [ 'randomVal' ] // По каким колонкам }, ] });
И при синхронизации моделей, будет выполнен запрос:
CREATE INDEX `posts_randomVal` ON `posts` (`randomVal`);
Как использовать ORM Sequelize
Sequelize - это ORM библиотека для nodejs. Sequelize поддерживает PostgreSQL, MySQL, SQLite и MSSQL диалекты.
Есть такой проект, "near protocol", это очередной блокчейн. В отличии от биткоина и эфира, он в теории, должен быть быстрым и масштабируемым. Прям сейчас проект "is currently under heavy development".