¿Cómo puedo devolver una consulta / promesa de base de fuego anidada en Express?

Estoy trabajando en NodeJS con firebase-admin. He estructurado mi base de datos de base de fuego como esta

nodo de usuario:

user | +--- uid (-27JfjDfBetveYQNfStackOverFlow) | +-- firstname +-- lastname +-- mainCompanyId: '' +-- .... userCompanies | +--- uid (-27JfjDfBetveYQNfStackOverFlow) | +--- companyId (-KnStackOverFlowF7DezRD0P) : true +--- companyId (-MyStackOverFlowF99ezRD0V) : true +--- .......... : true companies | +--- companyId (-KnStackOverFlowF7DezRD0P) | +-- name +-- createdAt +-- updatedAt +-- createdBy +-- ........ 

Las referencias ya están definidas globalmente en el servidor:

  • companiesRef = db.ref (’empresas’)
  • usersRef = db.ref (‘usuarios’)
  • userCompaniesRef = db.ref (‘userCompanies’)

Lo que trato de hacer es obtener todas las empresas relacionadas con el usuario . Para unir los datos, he creado el nodo Empresas de usuarios en la base de datos y he guardado el ID de empresa como una clave. Una vez que estoy recuperando los datos, recorro las claves y obtengo la identificación de la compañía. En express he creado una ruta llamada / api / companies . Si bash devolver el resultado al cliente, obtengo una matriz vacía.

Esta es mi ruta:

 app.get('/api/companies/', function (req, res) { // getting the current session. The variable 'sess' is stored globally. sess = req.session; // get the current user from the session var user = sess.user; // get the mainCompanyId from the user var mainCompanyId = user.companyId; // prepare object to return it back var myObj = { mainCompany: mainCompanyId, // ignore. Just a property to see users first created company companies: [], // this is the important property/array where I want to store all the companies someOtherProperties: true, // ignore ..... : ..... }; userCompaniesRef // db.ref('userCompanies') .child(user.uid) // -27JfjDfBetveYQNfStackOverFlow .once('value', function (userCompaniesSnap) { // get all companies which are related to the user var userCompaniesGroupList = userCompaniesSnap; // check if user has companies if (userCompaniesGroupList !== null) { // loop through all companies and get the companyId by Key userCompaniesGroupList.forEach(userCompaniesGroupListSnap => { var companyId = userCompaniesGroupListSnap.key; // -KnStackOverFlowF7DezRD0P companiesRef // db.ref('companies') .child(companyId) .once('value', companySnap => { // get the current company var company = companySnap.val(); // push it to the prepared object myObj.companies.push(company); }); // companiesRef.once('value) }); // userCompaniesGroupList.forEach } // if userCompaniesGroupList !== null }); // query userCompaniesRef res.status(200).send(myObj); }); 

Pero después de res.send obtengo este resultado:

Matriz vacía

No sé cuál es el problema. El empuje está funcionando bien. Una vez hecha la promesa, myObj.companies tiene una matriz vacía. ¿Cómo puede manejar estas consultas anidadas y devolver todos los datos en una matriz?

—ACTUALIZAR—

Lo he intentado con promesas. Sigue igual, me devuelvo una matriz vacía. Aquí está el código:

 // prepare object to return it back var myObj = { mainCompany: mainCompanyId, companies: [] }; var getCompanies = function () { return new Promise(function (resolve, reject) { userCompaniesRef // db.ref('userCompanies') .child(user.uid) // -27JfjDfBetveYQNfStackOverFlow .once('value', function (userCompaniesSnap) { if (userCompaniesSnap.val() !== null) resolve(userCompaniesSnap); }); }); } var getCompaniesByList = function (companyList) { var companyListArray = []; return new Promise(function (resolve, reject) { // loop through all companies and get the companyId by Key companyList.forEach(userCompaniesGroupListSnap => { var companyId = userCompaniesGroupListSnap.key; // -KnStackOverFlowF7DezRD0P companiesRef // db.ref('companies') .child(companyId) .once('value', companySnap => { // get the current company var company = companySnap.val(); // push it to the prepared object companyListArray.push(company); // updatedObjectFinal.push(myObj); }); // companiesRef.once('value) }); // userCompaniesGroupList.forEach resolve(companyListArray); }); } getCompanies().then(function (compList) { console.log('compList:', compList.val()); return getCompaniesByList(compList); // compList has the data. }).then(function (endResult) { console.log('endResult: ', endResult); // endResult is empty again.. res.status(200).send(endResult); }); 

En este caso, he intentado obtener todas las empresas por ID de usuario. Luego traté de pasar esta lista a la siguiente promesa de obtener cada identificación de cada compañía, empujar a la compañía a una matriz y devolver esta matriz al final. Pero todavía está vacío ..

ACTUALIZACIÓN 3: PROBLEMA RESUELTO

 app.get('/api/companies/', function (req, res) { // getting the current session sess = req.session; // save the user var user = sess.user; var userId = user.uid; var getCompanies = function () { return new Promise(function (resolve, reject) { userCompaniesRef // db.ref('userCompanies') .child(userId) // -27JfjDfBetveYQNfStackOverFlow .once('value', function (userCompaniesSnap) { // prepare an array var companies = []; if (userCompaniesSnap.val() !== null) { // loop through the keys and save the ids userCompaniesSnap.forEach(function (companyItem) { // console.log('companyIte,', companyItem.key); companies.push(companyItem.key); }); // get the latest item of the array to get the latest key var latestCompanyId = companies[companies.length - 1] // prepare optional object to resolve var finalCompaniesList = { companies: companies, lastCompanyId: latestCompanyId } resolve(finalCompaniesList); } }); }); } var getCompaniesByList = function (companyArray) { var companyListArray = []; return new Promise(function (resolve, reject) { // loop through all companies and get the companyId by Key companyArray.companies.forEach(userCompaniesGroupList => { var companyId = userCompaniesGroupList; // -KnStackOverFlowF7DezRD0P var companyTest = companiesRef // db.ref('companies') .child(companyId) .once('value', companySnap => { // get the current company var company = companySnap.val(); // push it to the prepared object companyListArray.push(company); if (company.id === companyArray.lastCompanyId) resolve(companyListArray); // I am resolving here now the data. }); // companiesRef.once('value) }); // userCompaniesGroupList.forEach }); } getCompanies().then(function (compList) { return getCompaniesByList(compList); }).then(function (endResult) { res.status(200).send(endResult); }); }); 

Muchas gracias a Frank! He encontrado una solución para mi problema. Lo que he hecho ahora es que, en getCompanies , he ejecutado un forEach para completar previamente una matriz con los ID y obtener el último ID de compañía en la matriz. Una vez que obtuve la última ID, creé un objeto personalizado y guardé la última ID en latestCompanyId y devolví la matriz. Así que ahora conozco la última identificación y pude ejecutar el método de resolución dentro de foreach en la promesa de complemento.

Por lo que puedo ver rápidamente, está cayendo en la progtwigción asíncrona 101: los datos se cargan desde Firebase de forma asíncrona. Cuando escribes tu resultado, los datos aún no se han cargado.

Para resolver esto, mueva la escritura de la respuesta a la callback que se activa cuando todos los datos están disponibles:

 userCompaniesRef // db.ref('userCompanies') .child(user.uid) // -27JfjDfBetveYQNfStackOverFlow .once('value', function (userCompaniesSnap) { // get all companies which are related to the user var userCompaniesGroupList = userCompaniesSnap; // check if user has companies if (userCompaniesGroupList !== null) { // loop through all companies and get the companyId by Key userCompaniesGroupList.forEach(userCompaniesGroupListSnap => { var companyId = userCompaniesGroupListSnap.key; // -KnStackOverFlowF7DezRD0P companiesRef // db.ref('companies') .child(companyId) .once('value', companySnap => { // get the current company var company = companySnap.val(); // push it to the prepared object myObj.companies.push(company); // send response to client res.status(200).send(myObj); }); // companiesRef.once('value) }); // userCompaniesGroupList.forEach } // if userCompaniesGroupList !== null }); // query userCompaniesRef });