首页 > 解决方案 > 如何使用 Node.js 在 Firebase 中执行加入?

问题描述

我有一个带有药房和相关位置的数据库。

数据库布局

我的目标是归还所有药房及其相应的位置。我一直在尝试这样做

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import { Dispensary } from './model/Dispensary';
import { Location } from './model/Location';

export const getDispensaries = functions.https.onRequest(async (req, res) => {
    let dispensaries: Dispensary[] = []
    await admin.database().ref("/dispensary").once("value")
      .then(function(snapshot) {
        snapshot.forEach(function(childSnapshot) {

            var dispensaryValues = childSnapshot.val();
            var name =  dispensaryValues["name"];
            var locationID = dispensaryValues["locationID"];

            admin.database().ref("/location").child(locationID).once("value")
                .then(function(innerSnapShot) {
                    innerSnapShot.forEach(function(innerChildSnapShot) {

                        var locationValues = innerChildSnapShot.val();
                        var latitude = locationValues["latitude"];
                        var longitude =  locationValues["longitude"];

                        let location: Location = new Location(latitude, longitude);
                        let dispensary: Dispensary = new Dispensary(name, location);
                        dispensaries.push(dispensary);
                    });
                }).catch(error => {
                    console.log(error);
            });
      });
    }).catch(error => {
        console.log(error);
    });
    res.send(dispensaries)
});

我的 Dispensary 和 Location 类看起来像这样

export class Dispensary {
    name: string;
    location: Location;

    constructor(name: string, location: Location) {
        this.name = name;
        this.location = location;
    }
}

export class Location {
    latitude: number;
    longitude: number;

    constructor(latitude: number, longitude: number) {
        this.latitude = latitude;
        this.longitude = longitude;
    }
}

现在这段代码返回一个空数组[]。我尝试只获取药房并且这有效,但是当我从药房调用中删除 async/await 时,它再次返回一个空数组。

此外,当我尝试向位置调用添加等待时,它告诉我我缺少异步,但是当我添加异步时它告诉我Type Promise<void> is not assignable to type 'void'

我是以完全错误的方式接近这个吗?感谢您提供任何帮助!

标签: node.jsdatabasetypescriptfirebasefirebase-realtime-database

解决方案


您的主要问题是您返回的值dispensaries没有首先等待每个位置被检索、解析、组装然后添加到dispensaries数组中。

下面的代码通过正确的操作流程将两个位置压缩在一起。

export const getDispensaries = functions.https.onRequest((req, res) => {
    // STEP: Define reused database locations
    const DISPENSARY_REF = admin.database().ref("/dispensary");
    const LOCATION_REF = admin.database().ref("/location");

    // STEP: Fetch list of dispensaries
    DISPENSARY_REF.once("value")
        .then(function (allDispensariesSnapshot) {
            // STEP: Define array to store all tasks so they can be run in parallel
            const dispensaryPromises: Promise<Dispensary>[] = [];

            // DataSnapshot.forEach() is syncronous - so we can't use async/await syntax here
            // STEP: Iterate over each dispensary in the database
            allDispensariesSnapshot.forEach(function (dispensarySnapshot) {
                // STEP: Extract dispensary data
                // Ref: /dispensary/someDispensaryID
                // Available Data: { locationID, name }
                const dispensaryValues = dispensarySnapshot.val();
                const { locationID, name } = dispensaryValues;

                // Fetch location data & resolve with completed dispensary object
                // STEP: Fetch needed data from other database locations
                const currentDispensaryPromise = LOCATION_REF.child(locationID).once("value")
                    .then(function (locationSnapshot) {
                        // STEP: Extract data
                        // Ref: /location/someLocationID
                        // Available Data: { latitude, longitude }
                        const locationValues = locationSnapshot.val();
                        const { latitude, longitude } = locationValues;

                        // STEP: Assemble objects
                        const location: Location = new Location(latitude, longitude);

                        // STEP: Return the final object
                        return new Dispensary(name, location);
                    });

                // STEP: Add this task to list of tasks
                dispensaryPromises.push(currentDispensaryPromise);
            });

            // STEP: wait for all tasks
            return Promise.all(dispensaryPromises);
        })
        .then(dispensaries => {
            // Objects were retrieved successfully
            // STEP: Return data to client
            res.json(dispensaries);
        })
        .catch(error => {
            // Something went wrong
            // STEP: Log & return error to client
            console.log(error); // log full error here
            res.status(500).json({error: error.message}); // but only return error message to client
        });
});

如果在稍后的时刻,您需要获取多个位置,您将更新currentDispensaryPromise为:

// Fetch other data & resolve with completed dispensary object
// STEP: Fetch needed data from other locations
const currentDispensaryPromise = Promise.all([
    LOCATION_REF.child(locationID).once("value"),
    USER_REF.child(ownerID).child('name').once("value") // with locations that have lots of data, fetch only what's needed
])
    .then(([locationSnapshot, ownerNameSnapshot]) => {
        // STEP: Extract data
        // Ref: /location/someLocationID
        // Available Data: { latitude, longitude }
        const locationValues = locationSnapshot.val();
        const { latitude, longitude } = locationValues;

        // Ref: /user/someOwnerID/name (string)
        const ownerName = ownerNameSnapshot.val();

        // STEP: Assemble objects
        const location: Location = new Location(latitude, longitude);
        const owner: Owner = new Owner(ownerID, ownerName);

        // STEP: Return the final object
        return new Dispensary(name, location, owner);
    });

注意:如果这种药房和位置数据的配对经常使用,请考虑将这些对象合并在一起,而不是单独存储它们。

其他注意事项

Dictionary和不需要使用对象Location,只需将对象的形状指定为类型就足够了。

type Dispensary = {
    name: number;
    location: Location;
}

type Location = {
    latitude: number;
    longitude: number;
}

const {name, locationID} = dispensarySnapshot.val();
const location = locationSnapshot.val() as Location;
return {name, location} as Dispensary;

但是,如果您想将它们保留为对象,则可以展平您的定义:

class Dispensary {
    constructor(public name: string, public location: Location) {}
}

class Location {
    constructor(public latitude: number, public longitude: number) {}
}

推荐阅读