Node.jsМодуль 5: Базы данных
MongoDB и Mongoose
Работа с MongoDB через Mongoose ODM
Цель урока
В этом уроке ты научишься:
- Подключаться к MongoDB
- Создавать схемы и модели
- Выполнять CRUD операции
Установка
npm install mongooseПодключение
const mongoose = require('mongoose');
async function connectDB() {
try {
await mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
});
console.log('MongoDB подключена');
} catch (error) {
console.error('Ошибка подключения:', error.message);
process.exit(1);
}
}
// Обработка событий
mongoose.connection.on('disconnected', () => {
console.log('MongoDB отключена');
});
module.exports = connectDB;Схемы и модели
Создание схемы
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Имя обязательно'],
trim: true,
minlength: [2, 'Минимум 2 символа'],
maxlength: [50, 'Максимум 50 символов']
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
match: [/^\S+@\S+\.\S+$/, 'Невалидный email']
},
password: {
type: String,
required: true,
minlength: 6,
select: false // Не возвращать по умолчанию
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user'
},
age: {
type: Number,
min: 18,
max: 120
},
isActive: {
type: Boolean,
default: true
},
tags: [String],
profile: {
bio: String,
avatar: String
}
}, {
timestamps: true // createdAt, updatedAt
});
const User = mongoose.model('User', userSchema);
module.exports = User;CRUD операции
Create
// Создание одного документа
const user = await User.create({
name: 'Иван',
email: 'ivan@example.com',
password: 'hashedpassword'
});
// Или через new
const user = new User({ name: 'Иван', email: 'ivan@example.com' });
await user.save();
// Создание нескольких
await User.insertMany([
{ name: 'Иван', email: 'ivan@example.com' },
{ name: 'Мария', email: 'maria@example.com' }
]);Read
// Все документы
const users = await User.find();
// С фильтром
const admins = await User.find({ role: 'admin' });
// Один документ
const user = await User.findById('507f1f77bcf86cd799439011');
const user = await User.findOne({ email: 'ivan@example.com' });
// Выбор полей
const users = await User.find().select('name email');
const users = await User.find().select('-password');
// Сортировка
const users = await User.find().sort({ createdAt: -1 });
const users = await User.find().sort('-createdAt name');
// Пагинация
const users = await User.find()
.skip(10)
.limit(10);
// Подсчёт
const count = await User.countDocuments({ role: 'admin' });Update
// Найти и обновить
const user = await User.findByIdAndUpdate(
id,
{ name: 'Новое имя' },
{ new: true, runValidators: true }
);
// Обновить несколько
await User.updateMany(
{ isActive: false },
{ $set: { role: 'inactive' } }
);
// Операторы обновления
await User.findByIdAndUpdate(id, {
$set: { name: 'Иван' },
$inc: { age: 1 },
$push: { tags: 'новый' },
$pull: { tags: 'старый' }
});Delete
// Удалить один
await User.findByIdAndDelete(id);
await User.deleteOne({ email: 'ivan@example.com' });
// Удалить несколько
await User.deleteMany({ isActive: false });Операторы запросов
// Сравнение
await User.find({ age: { $gt: 18 } }); // >
await User.find({ age: { $gte: 18 } }); // >=
await User.find({ age: { $lt: 30 } }); // <
await User.find({ age: { $lte: 30 } }); // <=
await User.find({ age: { $ne: 25 } }); // !=
// Логические
await User.find({ $and: [{ age: { $gt: 18 } }, { role: 'user' }] });
await User.find({ $or: [{ role: 'admin' }, { isActive: true }] });
// Массивы
await User.find({ tags: 'javascript' }); // Содержит
await User.find({ tags: { $in: ['js', 'ts'] } }); // Любой из
await User.find({ tags: { $all: ['js', 'ts'] } }); // Все
// Regex
await User.find({ name: /иван/i });
await User.find({ email: { $regex: 'example.com$' } });Middleware (хуки)
// Перед сохранением
userSchema.pre('save', async function(next) {
if (this.isModified('password')) {
this.password = await bcrypt.hash(this.password, 10);
}
next();
});
// После сохранения
userSchema.post('save', function(doc) {
console.log('Пользователь создан:', doc._id);
});
// Перед поиском
userSchema.pre(/^find/, function(next) {
this.find({ isActive: true });
next();
});Методы и статики
// Методы экземпляра
userSchema.methods.comparePassword = async function(password) {
return bcrypt.compare(password, this.password);
};
const user = await User.findOne({ email }).select('+password');
const isMatch = await user.comparePassword('password123');
// Статические методы
userSchema.statics.findByEmail = function(email) {
return this.findOne({ email });
};
const user = await User.findByEmail('ivan@example.com');Виртуальные поля
userSchema.virtual('fullName').get(function() {
return `${this.firstName} ${this.lastName}`;
});
// Включить в JSON
userSchema.set('toJSON', { virtuals: true });Практика
Задание: Модель Post
Задача: Создай модель для постов блога.
Решение:
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true,
trim: true
},
content: {
type: String,
required: true
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
tags: [String],
likes: {
type: Number,
default: 0
},
isPublished: {
type: Boolean,
default: false
}
}, { timestamps: true });
module.exports = mongoose.model('Post', postSchema);Проверь себя
- Что делает
select: falseв схеме? - Как выполнить пагинацию?
- Что такое middleware в Mongoose?