node.js - 如何使用 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'
我是以完全错误的方式接近这个吗?感谢您提供任何帮助!
解决方案
您的主要问题是您返回的值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) {}
}
推荐阅读
- sql - 在 SQL Server 2012 上触发视图未触发
- android - 设置 View.VISIBLE 并不总是让我的视图可见
- c# - 将所有绑定的文本框设置为修剪
- bootstrap-4 - Bootstrap4 输入验证将“无效反馈”添加到“表单组”
- javascript - 如何将输入值 1 放入输入 2 中使用 jquery
- sass - Nuxt Sass/Scss @imports 在导入的 node_modules 中
- vba - 堆中的 VBA PowerPoint 形状
- mysql - 在同一个表中为 2 个不同的 group by 函数创建 2 列
- datepicker - 向使用日历的控件添加“今天”按钮
- javascript - 如何在 D3 强制布局中编辑用户搜索的选择大小(或颜色...)?