Javascript 具有多个值的循环的Firestore查询

Javascript 具有多个值的循环的Firestore查询,javascript,firebase,google-cloud-firestore,google-cloud-functions,Javascript,Firebase,Google Cloud Firestore,Google Cloud Functions,我试图使用字符串中的数据检索许多Firestore文档。其思想是,对于数组中的每个值,我将使用Firestore查询检索与该查询匹配的文档,并将其推送到另一个数组中。我在实现这一目标时遇到了一些问题。到目前为止,我已经尝试: exports.findMultipleItems = functions.https.onRequest((request, response) => { var list = ["item1", "item2", "item3", "item4"];

我试图使用字符串中的数据检索许多Firestore文档。其思想是,对于数组中的每个值,我将使用Firestore查询检索与该查询匹配的文档,并将其推送到另一个数组中。我在实现这一目标时遇到了一些问题。到目前为止,我已经尝试:

exports.findMultipleItems = functions.https.onRequest((request, response) => {
    var list = ["item1", "item2", "item3", "item4"];

    var outputList = [];

    for (var i = 0; i < list.length; i++) {
        console.log("Current item: " + list[i]);
        let queryRef = db.collection("items").where('listedItems', 'array-contains', list[i]).get()
            .then(snapshot => {
                if (snapshot.empty) {
                    console.log('No matching documents.');
                }

                snapshot.forEach(doc => {
                    outputList.push(doc.data());
                });
                return;
            })
            .catch(err => {
                console.log('Error getting documents', err);
            });
    }

    response.send(JSON.stringify(outputList));

});
exports.findMultipleItems=functions.https.onRequest((请求,响应)=>{
变量列表=[“项目1”、“项目2”、“项目3”、“项目4”];
var outputList=[];
对于(变量i=0;i{
if(snapshot.empty){
console.log('没有匹配的文档');
}
snapshot.forEach(doc=>{
outputList.push(doc.data());
});
返回;
})
.catch(错误=>{
console.log('获取文档时出错',err);
});
}
send(JSON.stringify(outputList));
});
我不完全确定,但我认为其中一个问题是for循环是在查询有机会完成之前完成的


另外,这是使用AdminSDK通过云函数运行的。

您的
queryRef
实际上不是一个参考。这是一个承诺,它会在你的get/then/catch完成后解决。您需要使用这些承诺来确定它们何时全部完成。数组只有在它们全部完成后才会被填充,只有这样,使用该数组发送响应才是安全的

将所有承诺收集到一个数组中,并使用Promise.all()获取一个新的承诺,该承诺在全部完成后解析:

exports.findMultipleItems = functions.https.onRequest((request, response) => {
    var list = ["item1", "item2", "item3", "item4"];

    var outputList = [];
    const promises = [];

    for (var i = 0; i < list.length; i++) {
        console.log("Current item: " + list[i]);
        let promise = db.collection("items").where('listedItems', 'array-contains', list[i]).get()
            .then(snapshot => {
                if (snapshot.empty) {
                    console.log('No matching documents.');
                }

                snapshot.forEach(doc => {
                    outputList.push(doc.data());
                });
                return;
            })
            .catch(err => {
                console.log('Error getting documents', err);
            });
        promises.push(promise);
    }

    Promise.all(promises).then(() => {
        response.send(JSON.stringify(outputList));
    }
    .catch(err => {
        response.status(500);
    })

});
exports.findMultipleItems=functions.https.onRequest((请求,响应)=>{
变量列表=[“项目1”、“项目2”、“项目3”、“项目4”];
var outputList=[];
常量承诺=[];
对于(变量i=0;i{
if(snapshot.empty){
console.log('没有匹配的文档');
}
snapshot.forEach(doc=>{
outputList.push(doc.data());
});
返回;
})
.catch(错误=>{
console.log('获取文档时出错',err);
});
承诺。推动(承诺);
}
承诺。所有(承诺)。然后(()=>{
send(JSON.stringify(outputList));
}
.catch(错误=>{
答复.现状(500);
})
});
您可能希望使用这些教程更好地了解如何处理云函数中的承诺:


您需要查看承诺,不要在这样的循环中调用承诺。首先,您需要将代码分块,从DB(
异步
)返回结果,并使用
promise.all()
处理多个承诺

utils.getData = async (item) => {
        try {
                const result = await db.collection("items").where('listedItems', 'array-contains', item).get();
                return result;
        } catch (err) {
                throw err;
        }
};

utils.getDataFromDB = async () => {
        try {
                const list = ["item1", "item2", "item3", "item4"];

                const outputList = [];
                const promises = [];

                for (var i = 0; i < list.length; i++) {
                        console.log("Current item: " + list[i]);
                        const element = list[i];                   
                        promises.push(utils.getData(elem));
                }

                const result = await Promise.all(promises);
                result.forEach((r) => {
                        if (r.empty) {
                                console.log('No matching documents.');
                        } else {
                                snapshot.forEach(doc => {
                                        outputList.push(doc.data());
                                });
                        }
                });
                return outputList;
        } catch (err) {
                throw err;
        }
}

module.exports = utils;
utils.getData=async(项)=>{
试一试{
const result=await db.collection(“items”)。其中('listedItems','array contains',item.).get();
返回结果;
}捕捉(错误){
犯错误;
}
};
utils.getDataFromDB=async()=>{
试一试{
常量列表=[“项目1”、“项目2”、“项目3”、“项目4”];
常量输出列表=[];
常量承诺=[];
对于(变量i=0;i{
if(r.empty){
console.log('没有匹配的文档');
}否则{
snapshot.forEach(doc=>{
outputList.push(doc.data());
});
}
});
返回输出列表;
}捕捉(错误){
犯错误;
}
}
module.exports=utils;

这是我对完全惯用的解决方案的尝试。它不需要中间变量(不可能有竞争条件),并且可以很好地分离关注点

function data_for_snapshot( snapshot ) {
    if ( snapshot && !snapshot.empty )
        return snapshot.map( doc => doc.data() );

    return [];
}   

function query_data( search ) {
    return new Promise( (resolve, reject) => {
        db
        .collection("items")
        .where('listedItems', 'array-contains', search)
        .get()
        .then( snapshot => resolve(snapshot) )
        .catch( resolve( [] ) );
    });
}

function get_data( items )
{
    return new Promise( (resolve) => {
        Promise
            .all( items.map( item => query_data(item) ) )
            .then( (snapshots) => {
                resolve( snapshots.flatMap( 
                        snapshot => data_for_snapshot(snapshot) 
                ));
            });
    });
}

get_data( ["item1", "item2", "item3", "item4"] ).then( function(data) {
    console.log( JSON.stringify(data) );
});
我使用了一个简单的模型进行测试,因为我无法访问那个特定的数据库,但它应该可以工作

function query_data( search ) {
    return new Promise( (resolve, reject) => {
        setTimeout( () => { 
            resolve([{
                data: function() { return search.toUpperCase() },
                empty: false
            }])
        });
    });
}

异步编程可能很困难,但这里没有足够的信息来了解可能出现的问题。您提到您正在使用云函数-请编辑问题以显示整个函数。正确处理云函数中的承诺非常重要。目前,我所能看到的是
输出utList
将仅在查询完成后填充,并且没有任何东西等待这些查询完成。我已经添加了整个函数。我该如何等待查询完成?我了解到“最好不要过多更改操作代码”“惯用的”方法是使用高阶列表函数,并从承诺中发出“返回值”,而不是依赖副作用数组。