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

tquggr8v  于 2023-04-20  发布在  Node.js
关注(0)|答案(5)|浏览(131)

我使用节点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).

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

ltskdhd1

ltskdhd11#

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: Add Custom Object to db

jv2fixgn

jv2fixgn2#

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

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

注意:它只对没有嵌套对象的对象起作用。
您也可以使用class-transformer和它的classToPlain()沿着exposeUnsetFields option来省略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}));
drnojrws

drnojrws3#

如果你有一个FirebaseFirestore.Timestamp对象,那么不要使用JSON.parse(JSON.stringify(obj))classToPlain(obj),因为它们会在存储到Firestore时损坏它。
最好使用{...obj}方法。

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

注意:不要对document类中的任何嵌套对象使用new操作符,它将不起作用。相反,为嵌套对象属性创建interfacetype,如下所示:

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));
rjzwgtxy

rjzwgtxy4#

将值序列化为有效的Firestore Document数据,包括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()
  )));
hwazgwia

hwazgwia5#

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

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(),
});

相关问题