我正在使用节点 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).
有没有一种优雅的方式来处理这个问题?是否需要遍历结果并转换/删除所有对象?
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
另一种资源消耗较少的方法:
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}));
如果您有一个
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));
将值序列化为有效的 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()
)));
我在将模块转换为 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(),
});