Node JS сохраняет несколько записей в Mongo за одну транзакцию

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

В итоге:

В моем приложении node js есть запрос на получение. Когда пользователь обращается к этому URL-адресу GET, я хочу, чтобы он получал записи для этого пользователя, однако, если их нет, я хочу создать набор записей для этого пользователя в соответствии с заранее определенным списком. (Мне нужно что-то вроде фабрика, чтобы создать пакет или записи и дать результаты, когда это будет сделано.)

Создание единой записи работает. Но мне нужно создать 4. Если я попытаюсь создать больше одного, я получу ...

UnhandledPromiseRejectionWarning: ошибка [ERR_HTTP_HEADERS_SENT]: невозможно установить заголовки после их отправки клиенту или просто ошибка в зависимости от того, какой вариант я использую.

По сути, как только вы создаете более 1 записи, создание этой записи возвращает ответ, который я хотел бы «объединить», и когда это будет сделано, отправить объединенный результат обратно.

Моя модель очень простая. Выглядит это так:

/*____             _        _     __  __           _      _ 
 |  _ \           | |      | |   |  /  |         | |    | |
 | |_) |_   _  ___| | _____| |_  | \  / | ___   __| | ___| |
 |  _ <| | | |/ __| |/ / _ \ __| | |/| |/ _ \ / _` |/ _ \ |
 | |_) | |_| | (__|   <  __/ |_  | |  | | (_) | (_| |  __/ |
 |____/ \__,_|\___|_|\_\___|\__| |_|  |_|\___/ \__,_|\___|_|
*/
const mongoose = require('mongoose');
const dbSchema = mongoose.Schema;

const bucketSchema = new dbSchema({
    name: {
        type: String,
        required: true,
        unique: false
    },
    createdby: {
        type: dbSchema.Types.ObjectId,
        ref: 'user'
    },
    createdon: {
        type: Date,
        default: Date.now
    },
    isforarchive: {
        type: Boolean,
        required: true,
        default: false
    },
    cards: [
        {
            card: {
                type: dbSchema.Types.ObjectId,
                ref: 'card'
            }
        }
    ]
});

module.exports = bucket = mongoose.model('bucket', bucketSchema);

Этот код работает, когда я создаю одну запись. Однако мне нужно создать 4 сегмента для пользователя: - ToDo, Busy, Done и Archive.

Приведенный ниже код РАБОТАЕТ, НО СОЗДАЕТ ТОЛЬКО ОДИН ВЕДРО - ToDo.

const ToDo = new Bucket({
    name: 'To-Do',
    createdby: null,
    createdon: Date.now(),
    isforarchive: false,
    cards: [ {} ]
});

// @route get:/api/tms/buckets
// @desc Gets user buckets or creates a set of buckets for the user if they do not exist.
// @access Private
router.get(
    '/',
    passport.authenticate('jwt', {
        session: false
    }),
    (req, res) => {
        console.info('About to search buckets...');
        Bucket.find({ createdby: req.user.id })
            .then((found) => {
                console.info('Checking if found');
                console.info(`Length ${found.length}`); // Zero when none found
                if (found.length > 0) {
                    res.json(found);
                } else {
                    console.info('Creating ToDo');
                    ToDo.createdby = req.user.id;
                    ToDo.save().then((saved) => res.json(saved));
                }
            })
            .catch((err) => {
                const errors = {};
                errors.getbuckets = 'Encountered a problem getting buckets.';
                errors.error = err;
                res.status(400).json(errors);
            });
    }
);

Чтобы попытаться помочь мне, я создал вспомогательный класс ./buckethelper.js, чтобы помочь мне. Я пробовал обещания и т. д., Но не могу обойти проблему создания первой записи, отправляющей ответ, вызывающий указанную выше ошибку.

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

Вот мой вспомогательный класс.

const Bucket = require('./model');
/*
//PLEASE NOTE THAT THIS SECTION IS COMMENTED OUT.
function CreateBucket(Name, CreatedBy, CreatedOn, IsForArchive, Cards) {
    this.name = Name;
    this.createdby = CreatedBy;
    this.createdon = CreatedOn;
    this.isforarchive = IsForArchive;
    this.cards = Cards;
}

const Buckets = [];
Buckets.push(new CreateBucket('To-Do', '', Date.now(), false, [ {} ]));
Buckets.push(new CreateBucket('Busy', '', Date.now(), false, [ {} ]));
Buckets.push(new CreateBucket('Done', '', Date.now(), false, [ {} ]));
Buckets.push(new CreateBucket('Archive', '', Date.now(), true, [ {} ]));
*/
const ToDo = new Bucket({
    name: 'To-Do',
    createdby: null,
    createdon: Date.now(),
    isforarchive: false,
    cards: [ {} ]
});
const Busy = new Bucket({
    name: 'Busy',
    createdby: null,
    createdon: Date.now(),
    isforarchive: false,
    cards: [ {} ]
});
const Done = new Bucket({
    name: 'Done',
    createdby: null,
    createdon: Date.now(),
    isforarchive: false,
    cards: [ {} ]
});
const Archive = new Bucket({
    name: 'Archive',
    createdby: null,
    createdon: Date.now(),
    isforarchive: true,
    cards: [ {} ]
});



var result = [];

function fnSaveToDo(UserId) {
    console.info(`Saving To Do ...`);
    ToDo.createdby = UserId;
    result.push(Bucket.save(ToDo));
}

function fnSaveBusy(UserId) {
    Busy.createdby = UserId;
    result.push(Bucket.insert(Busy));
}

function fnSaveDone(UserId) {
    Done.createdby = UserId;
    result.push(Bucket.insert(Done));
}

function fnSaveArchive(UserId) {
    Archive.createdby = UserId;
    result.push(Bucket.insert(Archive));
    return result;//I want to return the combined results
}

//Synchronous call stack that execute in series not parallel
function CreateBuckets(UserId, fnSaveToDo, fnSaveBusy, fnSaveDone, fnSaveArchive) {
    console.info('Started the saving process ...');
    fnSaveToDo(UserId);
    fnSaveBusy(UserId);
    fnSaveDone(UserId);
    fnSaveArchive(UserId);
 }
 CreateBuckets(
    function() {
        console.info('User id callback?');
    },
    function() {
        console.info('ToDo saved ...');
    },
    function() {
        console.info('Busy saved ...');
    },
    function() {
        console.info('Done saved ...');
    },
    function() {
        console.info('Archive saved ...');
    }
 );

 module.exports = CreateBuckets;

Код, который вызывает моего помощника, выглядит примерно так, это подмножество приведенного выше кода;

Bucket.find({ createdby: req.user.id })
            .then((found) => {
                console.info('Checking if found');
                console.info(`Length ${found.length}`); // Zero when none found
                if (found.length > 0) {
                    res.json(found);
                } else {
                    //console.info('Creating ToDo');
                    res.json(CreateBuckets(req.user.id,fnSaveToDo,fnSaveBusy, fnSaveDone, fnSaveArchive));
                    // ToDo.createdby = req.user.id;
                    // ToDo.save().then((saved) => res.json(saved));    
                }
            })
            .catch((err) => {
                const errors = {};
                errors.getbuckets = 'Encountered a problem getting buckets.';
                errors.error = err;
                res.status(400).json(errors);
            });

Я надеюсь, что мое описание проблемы было ясным.

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

С уважением Крейг

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
517
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете использовать здесь метод insertMany, чтобы выполнить массовую вставку и использовать async / await в своих операциях, поскольку метод возвращает обещание.

Например, ваш маршрут можно переписать как

router.get('/', passport.authenticate('jwt', {session: false }), async (req, res) => {
    try {
        console.info('About to search buckets...');
        const found = await Bucket.find({ createdby: req.user.id })

        console.info('Checking if found');
        console.info(`Length ${found.length}`); // Zero when none found
        if (found.length > 0) {
            res.json(found);
        } else {
            console.info('Creating ToDos');
            const todos = ['To-Do', 'Busy', 'Done', 'Archive'].map(name => (
                {
                    name,
                    createdby: req.user.id,
                    createdon: Date.now(),
                    isforarchive: (name == 'Archive'),
                    cards: [ {} ]
                }
            ));
            const saved = await ToDo.insertMany(todos);
            res.json(saved);
        }
    } catch (err) {
        const errors = {};
        errors.getbuckets = 'Encountered a problem getting buckets.';
        errors.error = err;
        res.status(400).json(errors);
    }
});
Ответ принят как подходящий

Спасибо за ваше решение. Однако я не мог заставить Ждите работать. Вот мое решение. Я проведу рефакторинг, но он работает.

const DefaultBuckets = [ 'To Do', 'Busy', 'Done', 'Archive' ];

// @route get:/api/tms/buckets
// @desc Gets user buckets or creates a set of buckets for the user if they do not exist.
// @access Private
router.get(
    '/',
    passport.authenticate('jwt', {
        session: false
    }),
    (req, res) => {
        console.info('About to search buckets...');
        Bucket.find({ createdby: req.user.id })
            .then((found) => {
                console.info('Checking if found');
                console.info(`Length ${found.length}`); // Zero when none found
                if (found.length > 0) {
                    console.info('returning what is in db');
                    res.json(found);
                } else {
                    // console.info('Creating buckets ...');
                    const defaults = DefaultBuckets.map((name) => ({
                        name,
                        createdby: req.user.id,
                        createdon: Date.now(),
                        isforarchive: name == 'Archive',
                        cards: [ {} ]
                    }));
                    Bucket.insertMany(defaults, (err, docs) => {
                        let errors = {};
                        // Docs are the saved records from the db
                        if (docs) {
                            res.json(docs);
                        }else{
                            errors.message = 'Could not create buckets';
                            errors.InsertMany = err;
                            res.status(400).json(errors);
                        }                   
                    });
                }
            })
            .catch((err) => {
                const errors = {};
                errors.getbuckets = 'Encountered a problem getting buckets.';
                errors.error = err;
                res.status(400).json(errors);
            });
    }
);

Другие вопросы по теме