Получить данные дерева из Mongoose в функции рекурсивного узла

Я пытаюсь получить данные в формате дерева из Mongodb рекурсивно, используя Mongoose в приложении Node Express. Я пытаюсь сделать это с помощью обещаний, и я явно делаю это неправильно.

Скажем, у вас есть таблица mongodb foos ':

{
    "_id": {
        "$oid": "5b5fb27232db7b13a1090577"
    },
    "bazs": [
        {
            "$oid": "5b626a2e325c181bdf4fba08"
        }
    ],
    "bars": [
        {
            "$oid": "5b5fb3cc32db7b13a1090579"
        }
    ],
    "name": "Parent1",
    "accountId": "5b5fb27132db7b13a1090576",
    "__v": 0
}

Итак, у этого есть два разных типа дочерних объектов. Кроме того, объекты baz имеют другие объекты baz в качестве дочерних, а также объекты bar. Скажем, это таблица с идентификатором bars:

{
    "_id": {
        "$oid": "5b626a2e325c181bdf4fba08"
    },
    "bazs": [
        {
            "$oid": "5b5fb3cc32db7b75fa01"
        }
    ],
    "bars": [
        {
            "$oid": "5b5fb3c7cdaf740a500a"
        }
    ],
    "accountId": "5b5fb27132db7b13a1090576",
    "parent": {
        "$oid": "5b5fb27232db7b13a1090577"
    },
    "__v": 0
}

Я использовал рекурсивную функцию в преобразователе, ожидая извлечения типа объекта с помощью Mongoose.

const getObjectRecursive = (type, id)=>{     //'type' will be 'Foo' or 'Bar'
    return new Promise((resolve, reject)=>{
        type.findOne.call(type, {_id:id},{}, err=>{
            if (err){
                reject(err);
            }
        })
            .then((rslt)=>{
                const result = rslt;
                const bars = result.bars;
                const bazs = result.bazs;
                result.children = {};
                result.children.bars = tasks.map(async barId=>{
                    const barIdString = barId.toString();
                    await getBarRecursive(barIdString, err=>{
                        if (err){
                            reject(err);
                        }
                    });
                });
                result.children.bazs = bazs.map(async eventId=>{
                    const bazIdString = bazId.toString();
                    await Baz.findOne({_id:eventIdString}, {},err=>{
                        if (err){
                            reject(err);
                        }
                    });
                });
                resolve(result);
            })
    });

}

И я использую его в этих двух функциях:

const getBarRecursive = (taskId)=>{
    return getObjectRecursive(Bar, taskId);

}

const getFooRecursive=(categoryId)=>{
    return getObjectRecursive(Foo,categoryId);
};

Которые вызываются этим маршрутом:

router.get('/:id', verifyToken, (req,res)=>{
    Branch.getFooRecursive(req.params.id)
        .then(
            (foo)=>{
                res.status(200).send(cat);
            },
            (err)=>{
                res.status(500).send(err);
            }
        )
});

Это предназначено для рекурсивного возврата объекта дерева и всех дочерних объектов. Происходит то, что дочерние объекты result.children.bars и result.children.bazs становятся обещаниями, которые никогда не выполняются.

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

Как я могу заставить приложение извлекать детей на любую глубину?

ОБНОВЛЕНИЕ: это все еще не работает. Я изменил рекурсивную функцию на это:

const getObjectRecursive = async (type, id)=>{     
    const rslt = await type.findOne.call(type, {_id:id},{}, {}, err=>{
        if (err){
            return({err: err});
        }
    });

    const result = rslt.toObject();
    const bars = result.bars;
    const bazs = result.bazs;
    result.children = {};
    result.children.bars = tasks.map(async barId=>{
        const barIdString = barId.toString();
        await getBarRecursive(barIdString, err=>{
            if (err){
                reject(err);
            }
        });
    });
    result.children.bazs = bazs.map(async eventId=>{
        const bazIdString = bazId.toString();
        await Baz.findOne({_id:eventIdString}, {},err=>{
            if (err){
                reject(err);
            }
        });
    });
    return result
};

В остальном все то же самое. Что происходит сейчас, так это то, что промисы, содержащие результат bar, не выполняются до тех пор, пока ПОСЛЕ того, как результат не будет отправлен клиенту, поэтому я получаю пустой объект на его месте, например:

{
    "bars": [
        "5b626a2e325c181bdf4fba08"
    ],
    "bazs": [],
    "_id": "5b5fb27232db7b13a1090577",
    "__v": 0,
    "children": {
        "bars": [
            {}
        ],
        "bazs": []
    }
}

Некоторые вещи, которые я пробовал:

  • размещение bars.map(async barId=>... и т. д. внутри оператора Promises.all и присвоение его result.children в операторе .then(...)
  • поместив bars.map(async barId=>... и т. д. в отдельную асинхронную функцию, а затем скажем result.children.bars = await getAllTasks(tasks)

Как я могу убедиться, что мое обещание выполнено до отправки результата?

В примере активно используется stackoverflow.com/questions/23803743/…. Мангуст поддерживает обещания. Почему вы продолжаете использовать обратный вызов findOne? И вы смешиваете эти вещи с async / await. Проблема, вероятно, была бы решена, если бы вы везде использовали async / await.

Estus Flask 04.08.2018 02:09

Значит, вы говорите, что вместо того, чтобы заставить findObjectRecursive вернуть обещание, превратить его и getFooRecursive и getBarRecursive в асинхронные функции?

jimboweb 04.08.2018 02:23

Вы все равно можете использовать рекурсию, если вам нужно, но с гораздо более чистым потоком управления, который оставляет меньше места для ошибок. const result = await type.findOne(...) и др.

Estus Flask 04.08.2018 02:25

Я попробую это завтра

jimboweb 04.08.2018 08:43

Итак, мои обещания были выполнены, спасибо. Похоже, у меня есть еще одна проблема, когда материал, который я добавляю к своим возвращаемым объектам, не включается в то, что возвращается, но это что-то еще. Спасибо.

jimboweb 05.08.2018 02:02

Рад, что помог.

Estus Flask 05.08.2018 02:03

Для всех, кому интересно, проблема заключалась в том, что я пытался добавить свойства к объекту Mongoose, поэтому они не возвращались. Я изменил строку const result = rslt на const result = rslt.toObject(), что превратило ее в простой (не мангуст) объект, чтобы я мог добавлять к нему что-то.

jimboweb 05.08.2018 02:08

Кстати, это хорошая идея сделать это по умолчанию, если вам не нужно обратное. Делается это как const result = await type.findOne(...).lean().

Estus Flask 05.08.2018 02:11

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

jimboweb 07.08.2018 01:48

Здесь много проблем. Я пытался их объяснить. Я не могу гарантировать, что это сработает, потому что логика зависит от реальной структуры данных. Поскольку существует множество запросов и вложенных обещаний, может быть лучше переместить некоторые из этих запросов на сторону Mongo или реструктурировать данные. Обилие findOne указывает на то, что вместо этого может быть один find.

Estus Flask 07.08.2018 12:15
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
10
194
1

Ответы 1

Mongoose уже давно поддерживает обещания, нет необходимости использовать обратные вызовы. Функция reject не определена, ее использование приведет к исключению. Также здесь неоправдан call. Также есть несколько несоответствий: tasks против bars и eventId против bazId. Все они помешают нормальной работе кода

Должно получиться что-то вроде:

const getObjectRecursive = async (type, id)=>{     
    const result = await type.findOne({_id:id}).lean();
    const barsPromises = result.bars.map(barId=>{
        return getBarRecursive(barId.toString()); // shouldn't it accept 2 args?
    });
    const bazsPromises = result.bazs.map(bazId =>{
        return Baz.findOne({_id:bazId.toString()}).lean();
    });

    const [bars, bazs] = await Promise.all([
      Promise.all(barsPromises),
      Promise.all(bazsPromises)
    ]);
    result.children = { bars, bazs };

    return result;
};

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