Firestore 不支持带有自定义原型的 JavaScript 对象?

问题描述 投票:0回答:5

我正在使用节点 Bigquery Package 来运行一个简单的作业。查看作业的结果(比如

data
),
effective_date
属性看起来像这样:

 effective_date: BigQueryDate { value: '2015-10-02' }

这显然是返回的

data
对象中的一个对象。

将返回的json导入Firestore报错如下:

UnhandledPromiseRejectionWarning: Error: Argument "data" is not a 
valid Document. Couldn't serialize object of type "BigQueryDate". 
Firestore doesn't support JavaScript objects with custom prototypes 
(i.e. objects that were created via the 'new' operator).

有没有一种优雅的方式来处理这个问题?是否需要遍历结果并转换/删除所有对象?

node.js google-cloud-firestore google-cloud-datastore
5个回答
24
投票

firestore Node.js 客户端不支持自定义类的序列化。

你会在本期找到更多解释:
https://github.com/googleapis/nodejs-firestore/issues/143
“我们明确决定不支持 Web 和 Node.JS 客户端自定义类的序列化”

解决方案是将嵌套对象转换为普通对象。例如通过使用 lodash 或 JSON.stringify.

firestore.collection('collectionName')
    .doc('id')
    .set(JSON.parse(JSON.stringify(myCustomObject)));

这里是相关的帖子:
Firestore:将自定义对象添加到 db


11
投票

另一种资源消耗较少的方法:

firestore
  .collection('collectionName')
  .doc('id')
  .set(Object.assign({}, myCustomObject));

注意:它仅适用于没有嵌套对象的对象。

您也可以使用

class-transformer
,它是
classToPlain()
exposeUnsetFields
选项
来省略
undefined
值。

npm install class-transformer
or
yarn add class-transformer
import {classToPlain} from 'class-transformer';

firestore
  .collection('collectionName')
  .doc('id')
  .set(instanceToPlain(myCustomObject, {exposeUnsetFields: false}));

6
投票

如果您有一个

FirebaseFirestore.Timestamp
对象,那么不要使用
JSON.parse(JSON.stringify(obj))
classToPlain(obj)
,因为它们会在存储到 Firestore 时损坏它。

{...obj}
方法比较好

firestore
  .collection('collectionName')
  .doc('id')
  .set({...obj});

注意:不要对文档类中的任何嵌套对象使用

new
运算符,它不会起作用。相反,为嵌套对象属性创建一个
interface
type
,如下所示:

interface Profile {
    firstName: string;
    lastName: string;
}

class User {
    id = "";
    isPaid = false;
    profile: Profile = {
        firstName: "",
        lastName: "",
    };
}

const user = new User();

user.profile.firstName = "gorv";

await firestore.collection("users").add({...user});

如果你真的想存储由深度嵌套的更多类对象组成的类对象,那么使用这个函数首先将它转换为普通对象,同时保留

FirebaseFirestore.Timestamp
方法。

const toPlainFirestoreObject = (o: any): any => {
  if (o && typeof o === "object" && !Array.isArray(o) && !isFirestoreTimestamp(o)) {
    return {
      ...Object.keys(o).reduce(
        (a: any, c: any) => ((a[c] = toPlainFirestoreObject(o[c])), a),
        {}
      ),
    };
  }
  return o;
};

function isFirestoreTimestamp(o: any): boolean {
  if (o && 
    Object.getPrototypeOf(o).toMillis &&
    Object.getPrototypeOf(o).constructor.name === "Timestamp"
  ) {
    return true;
  }
  return false;
}


const user = new User();

user.profile = new Profile();

user.profile.address = new Address();

await firestore.collection("users").add(toPlainFirestoreObject(user));

0
投票

将值序列化为有效的 Firestore 文档数据,包括

object
及其子项和
Array
及其项

export function serializeFS(value) {
    const isDate = (value) => {
        if(value instanceof Date || value instanceof firestore.Timestamp){
            return true;
        }
        try {
            if(value.toDate() instanceof Date){
                return true;
            }
        } catch (e){}

        return false;
    };

    if(value == null){
        return null;
    }
    if(
        typeof value == "boolean" ||
        typeof value == "bigint" ||
        typeof value == "string" ||
        typeof value == "symbol" ||
        typeof value == "number" ||
        isDate(value) ||
        value instanceof firestore.FieldValue
    ) {
        return value;
    }

    if(Array.isArray(value)){
        return (value as Array<any>).map((v) => serializeFS(v));
    }

    const res = {};
    for(const key of Object.keys(value)){
        res[key] = serializeFS(value[key]);
    }
    return res;
}

用法:

await db().collection('products').doc()
  .set(serializeFS(
     new ProductEntity('something', 123, FieldValue.serverTimestamp()
  )));

0
投票

我在将模块转换为 Firestore 中的类时遇到了这个问题。问题是我以前使用的是 admin firestore 实例并引用来自@google-cloud 的一些字段信息,而不是使用 firebase admin 实例中的方法

const admin = require('firebase-admin');
const { FieldValue } = require('@google-cloud/firestore');

await accountDocRef.set({
  createdAt: FieldValue.serverTimestamp(),
});

应该改用 admin 包中的引用:

const admin = require('firebase-admin');

await accountDocRef.set({
  createdAt: admin.firestore.FieldValue.serverTimestamp(),
});
© www.soinside.com 2019 - 2024. All rights reserved.