Схемы, модели и виртуальные свойства Mongoose
Вы определите схемы Mongoose с типами, проверкой и параметрами по умолчанию, создадите модели и добавите виртуальные свойства.
«Схемы, модели и виртуальные свойства Mongoose» — бесплатный урок MongoDB Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения MongoDB Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс MongoDB Academy содержит 4 уроков всего.
Что такое Mongoose?
Mongoose — это объектно-документный преобразователь (ODM) для MongoDB и Node.js. Он работает поверх официального драйвера MongoDB и добавляет уровень абстракции: схемы для проверки структуры документов, модели для взаимодействия с базой данных, обработчики промежуточного ПО для логики до и после операций, а также виртуальные свойства для вычисляемых полей. Mongoose — самая популярная библиотека MongoDB в экосистеме Node.js; она особенно удобна для серверных приложений с чётко определёнными моделями данных.
// Install Mongoose
// npm install mongoose
const mongoose = require('mongoose');
// Connect to MongoDB
await mongoose.connect(process.env.MONGODB_URI);
console.log('Mongoose connected to MongoDB');Определение схемы
Schema Mongoose определяет структуру, типы и ограничения документов в коллекции. Каждому ключу схемы соответствует поле документа. Для каждого поля можно указать тип, обязательность, значение по умолчанию, минимальное и максимальное значения, перечисление и множество других параметров проверки. Схемы являются единственным источником истины для структуры документов в приложении Mongoose.
const { Schema } = mongoose;
const userSchema = new Schema({
name: {
type: String,
required: [true, 'Name is required'],
trim: true,
maxlength: 100
},
email: {
type: String,
required: true,
unique: true,
lowercase: true // automatically converts to lowercase before saving
},
age: {
type: Number,
min: [0, 'Age cannot be negative'],
max: 150
},
role: {
type: String,
enum: ['admin', 'user', 'guest'],
default: 'user'
},
createdAt: {
type: Date,
default: Date.now
}
});Типы схем: поддерживаемые типы
Mongoose поддерживает широкий набор типов схем. Наиболее распространённые из них — String, Number, Date, Boolean, Buffer, mongoose.Schema.Types.ObjectId (для ссылок), Array (обозначается как [Type]) и Mixed (любое значение без проверки типа). Mongoose также поддерживает вложенные схемы (дочерние документы): для этого определение Schema вкладывают в определение поля другой Schema.
const { Schema } = mongoose;
const { ObjectId } = Schema.Types;
const orderSchema = new Schema({
customerId: { type: ObjectId, ref: 'User', required: true }, // reference to User model
items: [
{
productId: { type: ObjectId, ref: 'Product' },
quantity: { type: Number, min: 1 },
price: Number
}
],
total: Number,
status: { type: String, default: 'pending' },
metadata: Schema.Types.Mixed, // accepts any shape
tags: [String], // array of strings
shippedAt: Date
});Параметр временных меток
Передача { timestamps: true } вторым аргументом конструктору Schema заставляет Mongoose автоматически управлять полями createdAt и updatedAt каждого документа. Поле createdAt устанавливается один раз при добавлении, а updatedAt обновляется при каждом сохранении. Вам не нужно задавать эти поля вручную — Mongoose обрабатывает их прозрачно. Это рекомендуемый подход для всех схем в рабочих приложениях.
const productSchema = new Schema(
{
name: { type: String, required: true },
price: { type: Number, required: true },
category: String,
stock: { type: Number, default: 0 }
},
{ timestamps: true } // adds createdAt and updatedAt automatically
);
// Documents will have:
// { name: 'Laptop', price: 999, createdAt: Date, updatedAt: Date }Создание модели
Model — это конструктор, скомпилированный из Schema. Он предоставляет интерфейс для запросов и записи документов в коллекцию. Вызовите mongoose.model('ModelName', schema), чтобы создать модель: первый аргумент — имя коллекции в единственном числе (Mongoose автоматически образует множественное число: 'User' → коллекция 'users'). Модели следует создавать один раз и экспортировать как модули.
// Define schema
const userSchema = new mongoose.Schema({
name: String,
email: { type: String, unique: true },
role: { type: String, default: 'user' }
}, { timestamps: true });
// Compile the model
const User = mongoose.model('User', userSchema);
// This creates/uses the 'users' collection
module.exports = User;
// Usage in another file:
// const User = require('./models/user');
// const user = await User.findOne({ email: 'alice@example.com' });Создание документов с помощью new Model()
Создайте новый экземпляр документа с помощью конструктора модели: new User({ name: '...', ... }). Это создаёт объект документа в памяти с проверкой, но НЕ сохраняет его в базе данных. Вызовите .save() у экземпляра, чтобы сохранить его, или используйте статическое сокращение User.create(), объединяющее оба шага. Перед сохранением Mongoose проверяет документ по схеме и выдаёт ValidationError, если ограничения нарушены.
// Method 1: new + save (two-step)
const user = new User({
name: 'Alice',
email: 'alice@example.com',
role: 'admin'
});
await user.save(); // validates then saves to 'users' collection
// Method 2: User.create() shorthand
const user2 = await User.create({
name: 'Bob',
email: 'bob@example.com'
});
console.log('Created user ID:', user2._id);
// Method 3: insertMany for bulk
await User.insertMany([
{ name: 'Carol', email: 'carol@example.com' },
{ name: 'Dave', email: 'dave@example.com' }
]);Виртуальные свойства
Виртуальные свойства — это вычисляемые свойства, которые не хранятся в базе данных, а вычисляются на основе других полей документа. В коде приложения они ведут себя как обычные поля документа, но никогда не записываются в MongoDB. Распространённые варианты использования: объединение firstName и lastName в виртуальное свойство fullName, вычисление age по полю birthDate или создание общедоступного url на основе _id.
const personSchema = new mongoose.Schema({
firstName: String,
lastName: String,
birthDate: Date
});
// Virtual: combines firstName and lastName
personSchema.virtual('fullName').get(function () {
return this.firstName + ' ' + this.lastName;
// Use regular function (not arrow function) to access 'this'
});
// Virtual with a setter for convenience
personSchema.virtual('fullName').get(function () {
return this.firstName + ' ' + this.lastName;
}).set(function (v) {
this.firstName = v.split(' ')[0];
this.lastName = v.split(' ')[1];
});
const Person = mongoose.model('Person', personSchema);
const p = new Person({ firstName: 'Alice', lastName: 'Smith' });
console.log(p.fullName); // 'Alice Smith'Включение виртуальных свойств в вывод JSON
По умолчанию виртуальные свойства не включаются при преобразовании документа в JSON (например, при отправке в ответе API). Чтобы включить их, задайте { toJSON: { virtuals: true } } в параметрах схемы или явно вызовите doc.toJSON({ virtuals: true }). В приложениях Express вызов res.json(doc) автоматически вызывает toJSON(), поэтому установка toJSON: { virtuals: true } в схеме — самый простой способ всегда включать виртуальные свойства.
const userSchema = new mongoose.Schema(
{
firstName: String,
lastName: String
},
{
toJSON: { virtuals: true }, // include virtuals in res.json()
toObject: { virtuals: true } // include virtuals in .toObject()
}
);
userSchema.virtual('fullName').get(function () {
return this.firstName + ' ' + this.lastName;
});
const user = new User({ firstName: 'Alice', lastName: 'Smith' });
console.log(JSON.stringify(user)); // includes 'fullName': 'Alice Smith'Пользовательская проверка в схемах
Схемы Mongoose поддерживают пользовательские функции проверки для каждого поля. Функция проверки получает значение поля и должна вернуть true для допустимого значения или false (либо выдать ошибку) для недопустимого. Также можно указать собственное сообщение об ошибке. Пользовательские проверки выполняются до .save() и могут быть асинхронными (это полезно для проверок уникальности на уровне базы данных, дополняющих уникальный индекс).
const productSchema = new mongoose.Schema({
name: String,
price: {
type: Number,
required: true,
validate: {
validator: function (v) {
return v > 0; // price must be positive
},
message: props => 'Price must be positive, got ' + props.value
}
},
sku: {
type: String,
validate: {
validator: function (v) {
return /^[A-Z]{2}-\d{4}$/.test(v); // format: AB-1234
},
message: 'SKU must match format AB-1234'
}
}
});Методы и статические методы схем
Схемы поддерживают методы экземпляра (доступные для каждого документа) и статические методы (вызываемые у класса Model). Методы экземпляра получают доступ к this конкретного документа, поэтому идеально подходят для операций с отдельными документами, например сравнения паролей или форматирования вывода. Статические методы полезны для распространённых запросов или фабричных функций, не работающих с конкретным экземпляром.
const userSchema = new mongoose.Schema({ email: String, passwordHash: String });
// Instance method: available on each user document
userSchema.methods.checkPassword = function (candidatePassword) {
return bcrypt.compare(candidatePassword, this.passwordHash);
};
// Static method: called on the User model
userSchema.statics.findByEmail = function (email) {
return this.findOne({ email: email.toLowerCase() });
};
const User = mongoose.model('User', userSchema);
// Usage:
const user = await User.findByEmail('alice@example.com'); // static
const valid = await user.checkPassword('secret'); // instance methodДочерние документы и вложенные объекты схемы
Mongoose различает встроенные дочерние документы (определённые как массив схем) и вложенные объекты схемы (обычное определение схемы внутри поля). Каждый дочерний документ в массиве получает собственный _id, и с ним можно работать как с отдельным документом через doc.items.id(subId). Вложенные объекты используют жизненный цикл родительского документа. Используйте дочерние документы в массиве для упорядоченных наборов записей (позиций заказа, комментариев), а вложенные объекты — для встроенных структур «один к одному» (адреса, метаданных).
// Nested object — no array, no _id per entry
const userSchema = new Schema({
address: {
street: String,
city: String,
zip: String
}
});
// Array subdocuments — each item gets its own _id
const orderSchema = new Schema({
items: [
{
productId: Schema.Types.ObjectId,
quantity: Number,
price: Number
// _id auto-added to each item
}
]
});
// Access subdocument by ID:
const item = order.items.id(someItemId);Быстрая проверка
Проверьте, насколько хорошо Вы понимаете концепции MongoDB и баз данных NoSQL из этого урока.
Итоги урока
В этом уроке Вы узнали, что: схемы Mongoose определяют структуру документов, типы и правила проверки, модели компилируются из схем и предоставляют интерфейс для запросов и записи (Model.find(), new Model() и т. д.), а виртуальные свойства вычисляются в памяти, но никогда не сохраняются в MongoDB — они полезны для производных полей, таких как fullName или url. Далее мы рассмотрим API запросов Mongoose, включая цепочки вызовов, .lean() и сравнение с нативным драйвером.
Изучай JavaScript с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 30
- Уроки
- 120
Часто задаваемые вопросы
Урок «Схемы, модели и виртуальные свойства Mongoose» бесплатный?
Да — полный текст урока «Схемы, модели и виртуальные свойства Mongoose» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс MongoDB Academy, подпишись на CoddyKit PRO. Курс MongoDB Academy содержит 4 уроков всего.
Чему я научусь в уроке «Схемы, модели и виртуальные свойства Mongoose»?
Вы определите схемы Mongoose с типами, проверкой и параметрами по умолчанию, создадите модели и добавите виртуальные свойства. Ты практикуешь MongoDB Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать MongoDB Academy?
Предыдущий опыт не требуется. MongoDB Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Схемы, модели и виртуальные свойства Mongoose»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке MongoDB Academy?
Да. Каждый урок MongoDB Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Подключение с помощью официального драйвера Node.js
- Схемы, модели и виртуальные свойства Mongoose
- Запросы Mongoose, цепочки и документы без преобразования
- Промежуточное ПО Mongoose: предварительные и последующие хуки