#javascript #node.js #mongodb #express #mongoose
Вопрос:
Я столкнулся с проблемой, которая уже была решена, но так как я один работаю над этим решением, я хочу поделиться, посмотрите другие возможные решения проблемы, с которой я столкнулся.
Я работаю над приложением, которое позволяет пользователю читать «Книги». В каждом приложении может быть одна или несколько книг, но это не проблема.
Поскольку каждая книга отличается от другой, она может иметь разную структуру. Это означает, что я хочу сохранить книгу с несколькими уровнями глав: Раздел 1, Раздел 1.3.4 и т. Д….
Таким образом, у нас есть «индекс» глав, и в каждой главе также могут храниться другие главы. Если глава является ее последним уровнем, мы показываем ссылку на страницы (другая коллекция).
Давайте приложим к нему руки:
const BookSchema = new mongoose.Schema(
{
title: {
type: String,
required: true
},
description: {
type: String,
required: false
},
notes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Note',
autopopulate: { select: { body: 0 } }
}
],
chapters: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Book',
autopopulate: true
}
]
},
{
timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' },
}
);
Мы храним страницы из другой коллекции под названием Notes
.
Основной указатель — это книжная схема, а вложенные главы позволяют создать заданную структуру. У нас есть коллекция BookSchema
с одним основным BookSchema
с жестко закодированным названием, которое позволяет идентифицировать его.
На интерфейсе я мог бы легко манипулировать этой основной моделью, добавляя новые главы или примечания к этой главе. Однако есть некоторые операции, которые необходимо выполнить:
- You get from the frontend the model already populated. So any update operation should be sure to decouple the different nested levels and find the given _id for each chapter and update it. In my code i managed to create this update operation:
static async updateChapters(chapter) {
return new Promise(async (resolve, reject) => {
try {
chapter._id = chapter._id || new mongoose.Types.ObjectId();
if (chapter.chapters.length > 0) {
chapter.chapters = await Promise.all(chapter.chapters.map(c =>
this.updateChapters(c)));
}
await Book.findByIdAndUpdate(chapter._id, chapter, { upsert: true });
resolve(chapter._id);
} catch (error) {
reject(error);
}
})
};
If you remove a chapter, we need to track what are the missing chapters in the structure, also we need to find all its subchapters and remove them. I managed to get this solution:
static async purgeChapters() {
return new Promise(async (resolve, reject) => {
try {
let chapters = await Book.find();
let book = chapters.filter(c => c.title === 'temario').shift();
let allIds = chapters.map(c => String(c._id));
let linkedIds = chapters.map(c => c.chapters.map(c => String(c._id))).flat();
let unlinkedIds = allIds.filter(id => !linkedIds.includes(id)).filter(id => id !== String(book._id));
let getChildrenIds = function(parent) {
let childrens = [];
childrens = parent.chapters.map(c => getChildrenIds(c));
childrens.push(parent._id);
return childrens.flat();
}
let lostIds = chapters.filter(c => unlinkedIds.includes(String(c._id))).map(c => getChildrenIds(c)).flat();
await Book.deleteMany({ _id:{ $in: lostIds } })
resolve();
} catch (error) {
reject(error);
}
})
};
Im not 100% happy about it. With the mongoose-autopopulate
library its very easy to perform fetch operations (im still pending to put a depth parameter to populate notes based on the depth)
Но для операций обновления требуется вручную отделить все вложенные модели, а также найти «потерянные» главы, которые не связаны ни с одной книгой, и т.д….
Я нахожу этот код довольно запутанным, и перебор всех моделей кажется хорошим решением, но не чистым.
Есть какие-либо предложения по улучшению этого кода?
Спасибо