Node.jsМодуль 5: Базы данных
Отношения и связи
One-to-Many, Many-to-Many, populate, join
Цель урока
В этом уроке ты научишься:
- Создавать связи между коллекциями/таблицами
- Использовать populate в Mongoose
- Работать с JOIN в SQL
Типы связей
| Тип | Описание | Пример |
|---|---|---|
| One-to-One | Один к одному | User → Profile |
| One-to-Many | Один ко многим | User → Posts |
| Many-to-Many | Многие ко многим | Post → Tags |
MongoDB (Mongoose)
One-to-Many
// schemas/user.js
const userSchema = new mongoose.Schema({
name: String,
email: String
});
// schemas/post.js
const postSchema = new mongoose.Schema({
title: String,
content: String,
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
}
});
// Виртуальное поле для обратной связи
userSchema.virtual('posts', {
ref: 'Post',
localField: '_id',
foreignField: 'author'
});Populate
// Получить пост с автором
const post = await Post.findById(id).populate('author');
// { _id: ..., title: '...', author: { _id: ..., name: 'Иван' } }
// Выбор полей
const post = await Post.findById(id).populate('author', 'name email');
// Множественный populate
const post = await Post.findById(id)
.populate('author')
.populate('comments');
// Вложенный populate
const post = await Post.findById(id)
.populate({
path: 'comments',
populate: { path: 'author', select: 'name' }
});
// Получить пользователя с постами
const user = await User.findById(id).populate('posts');Many-to-Many
// schemas/post.js
const postSchema = new mongoose.Schema({
title: String,
tags: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Tag'
}]
});
// schemas/tag.js
const tagSchema = new mongoose.Schema({
name: { type: String, unique: true }
});
// Использование
const post = await Post.findById(id).populate('tags');
// Создание с тегами
const post = await Post.create({
title: 'Мой пост',
tags: [tagId1, tagId2]
});
// Добавление тега
await Post.findByIdAndUpdate(id, {
$push: { tags: newTagId }
});PostgreSQL (Prisma)
One-to-Many
model User {
id Int @id @default(autoincrement())
name String
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id])
authorId Int
}// Получить пост с автором
const post = await prisma.post.findUnique({
where: { id: 1 },
include: { author: true }
});
// Получить пользователя с постами
const user = await prisma.user.findUnique({
where: { id: 1 },
include: { posts: true }
});
// Создать пост для пользователя
const post = await prisma.post.create({
data: {
title: 'Мой пост',
author: { connect: { id: 1 } }
}
});
// Создать пользователя с постами
const user = await prisma.user.create({
data: {
name: 'Иван',
posts: {
create: [
{ title: 'Пост 1' },
{ title: 'Пост 2' }
]
}
}
});Many-to-Many
model Post {
id Int @id @default(autoincrement())
title String
tags Tag[]
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts Post[]
}// Создать пост с тегами
const post = await prisma.post.create({
data: {
title: 'Мой пост',
tags: {
connectOrCreate: [
{ where: { name: 'JavaScript' }, create: { name: 'JavaScript' } },
{ where: { name: 'Node.js' }, create: { name: 'Node.js' } }
]
}
},
include: { tags: true }
});
// Добавить тег к посту
await prisma.post.update({
where: { id: 1 },
data: {
tags: { connect: { id: 5 } }
}
});
// Удалить тег из поста
await prisma.post.update({
where: { id: 1 },
data: {
tags: { disconnect: { id: 5 } }
}
});Практика
Задание: Блог со связями
Задача: Создай модели для блога: User, Post, Comment.
Решение (Mongoose):
// User
const userSchema = new mongoose.Schema({
name: String,
email: String
});
userSchema.virtual('posts', {
ref: 'Post',
localField: '_id',
foreignField: 'author'
});
// Post
const postSchema = new mongoose.Schema({
title: String,
content: String,
author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }]
});
// Comment
const commentSchema = new mongoose.Schema({
text: String,
author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
post: { type: mongoose.Schema.Types.ObjectId, ref: 'Post' }
});
// Запрос
const post = await Post.findById(id)
.populate('author', 'name')
.populate({
path: 'comments',
populate: { path: 'author', select: 'name' }
});Проверь себя
- Что делает
populateв Mongoose? - Как создать Many-to-Many связь в Prisma?
- Что такое виртуальное поле?