以下片段
const start = new Date(this.date + 'T' + this.time);
console.log(start); // Thu Sep 12 2019 04:00:00 GMT+0200
const tournament:Tournament = {
start: firebase.firestore.Timestamp.fromDate(start)
}
将此锦标赛对象传递给可调用云函数,其唯一目的是保存作为文档传递的
tournament
会将 start
字段保存为具有属性 seconds
和 miliseconds
的地图,而不是 Firestore 中的时间戳.
我也尝试只做
start: start
但这也没有带来在 Firestore 中保存时间戳的预期结果。
仅供参考,这是功能代码精简后的样子:
firestore.collection('tournaments').doc(slugUrl).set(tournamentData)
(tournamentData 是从前端传递过来的对象)
您的 Timestamp 对象必须先序列化为 JSON,然后才能将其发送到函数。默认序列化将时间戳分成自然秒和纳秒值生成的对象。所有类型信息都丢失了。
在您的函数中,您将不得不从传递给函数的数据中读取这些单独的值,使用其双参数构造函数将它们转换回适当的时间戳对象,然后将该对象写入 Cloud Firestore。才会保存为时间戳类型的字段。
我能够通过使用 msgpack 而不是 JSON 进行序列化来解决这个问题,因为它支持 时间对象的转换。转换 Firestore Timestamp 对象很简单,因为它们基于 Temporal Instant.
我的用例是通过 PubSub 将
Timestamp
对象从一个 Cloud Function 发送到另一个 Cloud Function。下面的示例是围绕该概念构建的,但足够通用,可以扩展到其他流程。 (例如,将数据从 Web 应用程序发送到 Cloud Functions,反之亦然)
首先定义一个可以编码和解码
ExtensionCodec
类型的自定义msgpackTimestamp
,然后用Encoder
和Decoder
对象注册扩展,这样当它们用于序列化数据时,它们知道如何处理这些类型.
下面的代码片段在变成一个包时效果最好,这样它就可以在多个函数/应用程序之间共享,否则必须将它复制并粘贴到它后面的每个示例中才能工作。我从下面的每个示例中省略了这个片段以保持它们干燥。
import {
Decoder,
Encoder,
ExtensionCodec,
decodeTimestampToTimeSpec,
encodeTimeSpecToTimestamp,
} from '@msgpack/msgpack'
import { Timestamp } from 'firebase-admin/firestore'
const firestoreTimestampType = 0
const extensionCodec: ExtensionCodec = new ExtensionCodec()
extensionCodec.register({
type: firestoreTimestampType,
encode(input: unknown): Uint8Array | null {
if (input instanceof Timestamp) {
const sec = input.seconds
const nsec = input.nanoseconds
return encodeTimeSpecToTimestamp({ sec, nsec })
} else {
return null
}
},
decode(data: Uint8Array): Timestamp {
const timeSpec = decodeTimestampToTimeSpec(data)
const sec = timeSpec.sec
const nsec = timeSpec.nsec
return new Timestamp(sec, nsec)
},
})
// Always use these instances to encode/decode objects that are or
// contain Timestamp type objects or else they will decode as Map
// objects the same way they do with JSON serialization
export const encoder = new Encoder({ extensionCodec })
export const decoder = new Decoder({ extensionCodec })
接下来配置发送数据的功能,使用自定义编码器对其进行编码:
import { type ClientConfig, PubSub } from '@google-cloud/pubsub'
import { Timestamp } from 'firebase-admin/firestore'
// Replace with your actual projectId and topic name
const pubSub = new PubSub('some-project-id' as ClientConfig).topic('some-topic-name')
interface CustomObject {
timestamp: Timestamp
}
export const sendingCloudFunction = async (): Promise<void> => {
const seconds = Math.floor(Date.now() / 1000)
const nanoseconds = 0
const timestamp: Timestamp = new Timestamp(seconds, nanoseconds)
const customObject: CustomObject = { timestamp }
// Encode the custom object with msgpack
const encodedObject: Uint8Array = encoder.encode(customObject)
// Pass this data on to the next function by publishing to PubSub
const data: Buffer = Buffer.from(
encodedObject.buffer,
encodedObject.byteOffset,
encodedObject.byteLength
)
await pubSub.publishMessage({ data })
}
最后,配置接收数据的函数以使用自定义解码器对其进行解码:
import { type PubsubMessage } from '@google-cloud/pubsub/build/src/publisher'
import { Timestamp } from 'firebase-admin/firestore'
interface CustomObject {
timestamp: Timestamp
}
export const receivingCloudFunction = async (
message: PubsubMessage,
): Promise<void> => {
if (message.data === null || message.data === undefined) {
throw new Error('PubSub message must be defined')
}
// Decode the PubSub data back into a msgpack-encoded object
const encodedObject: Buffer = Buffer.from(message.data.toString(), 'base64')
// Decode the msgpack-encoded object back into CustomObject
const decodedObject = decoder.decode(encodedObject) as CustomObject
if (decodedObject.timestamp instanceof Timestamp) {
console.log(`Successfully decoded ${decodedObject.timestamp.toDate().toString()} as type Timestamp`)
}
}
当
receivingCloudFunction
被调用时,它会记录类似于:
成功解码 2023 年 3 月 28 日星期二 12:02:11 GMT-0700(太平洋夏令时)类型时间戳