How can I return a nested firebase query/promise in express?

戏子无情 提交于 2019-12-08 13:01:41

问题


I am working on NodeJS with firebase-admin. I've structured my firebase database like this

user node:

user
|
+--- uid (-27JfjDfBetveYQNfStackOverFlow)
      |
      +-- firstname
      +-- lastname
      +-- mainCompanyId: ''
      +-- ....

userCompanies
|
+--- uid (-27JfjDfBetveYQNfStackOverFlow)
      |
      +--- companyId (-KnStackOverFlowF7DezRD0P) : true
      +--- companyId (-MyStackOverFlowF99ezRD0V) : true
      +--- .......... : true

companies
| 
+--- companyId (-KnStackOverFlowF7DezRD0P)
        |
        +-- name
        +-- createdAt
        +-- updatedAt
        +-- createdBy
        +-- ........

The refs are already defined globally on the server:

  • companiesRef = db.ref('companies')
  • usersRef = db.ref('users')
  • userCompaniesRef = db.ref('userCompanies')

What I try to do is to get all companies which are related to the user. To join the data I've created the node userCompanies in the database and saved the companyId as a key. Once I am retrieving the data, I loop through the keys and get the company by their id. In express I've created a route called /api/companies. If I try to return the result back to the client I got an empty array.

This is my route:

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);
});

But after res.send I get this result:

I don't know what here is the problem. The push is working fine. Once the promise is done, myObj.companies has an empty array. How can a handle this nested queries and return all data in one array ?

---UPDATE---

I've tried with promises. Still the same, I get an empty array back. Here is the code:

// 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);
});

In this case I've tried to get all companies by user id. Then I tried to pass this list to the next promise to get each company by their ID, push the company to an array and return this array back on the end. But is still empty..

UPDATE 3: PROBLEM SOLVED

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);
});

});

Big thanks to Frank! I've found a solution for my problem. What I have done now is, in getCompanies I've run a forEach to pre-fill an array with the ids and to get the latest companyId in the array. Once I got the latest id, I've created a custom object and saved the latest id in latestCompanyId and returned the array back. So I know now the latest id and I was able to run the resolve method inside the foreach in the snap promise.


回答1:


As far as I can quickly see you're falling for async programming 101: data is loaded from Firebase asynchronously. When you write your result, the data hasn't loaded yet.

To solve this, move the writing of the response into the callback that fires when all data is available:

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
});


来源:https://stackoverflow.com/questions/45097858/how-can-i-return-a-nested-firebase-query-promise-in-express

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!