хуйу нас не матерятся
В этот раз мы углубимся в 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`);
Спасибо, очень помогла ваша статья!