Оптимальное решение для неопределенной рекурсивной вложенной модели в мангусте

#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)

Но для операций обновления требуется вручную отделить все вложенные модели, а также найти «потерянные» главы, которые не связаны ни с одной книгой, и т.д….

Я нахожу этот код довольно запутанным, и перебор всех моделей кажется хорошим решением, но не чистым.

Есть какие-либо предложения по улучшению этого кода?

Спасибо