FirestoreDataConverter interface

withConverter()使用转换器将AppModelType类型的用户对象转换为DbModelType类型的 Firestore 数据

使用转换器允许您在从 Firestore 存储和检索对象时指定通用类型参数。

在这种情况下,“AppModel”是在应用程序中用于将相关信息和功能打包在一起的类。例如,这样的类可以具有复杂的嵌套数据类型的属性、用于记忆的属性、Firestore 不支持的类型的属性(例如symbolbigint ),以及执行复合操作的辅助函数。此类类不适合和/或不可能存储到 Firestore 数据库中。相反,此类的实例需要转换为具有专有原始属性的“普通旧 JavaScript 对象”(POJO),可能嵌套在其他 POJO 或 POJO 数组中。在此上下文中,此类型称为“DbModel”,并且是适合持久保存到 Firestore 中的对象。为了方便起见,应用程序可以实现FirestoreDataConverter并将转换器注册到 Firestore 对象,例如DocumentReferenceQuery ,在存储到 Firestore 时自动将AppModel转换为DbModel ,并在从 Firestore 检索时自动将DbModel转换为AppModel

签名:

export declare interface FirestoreDataConverter<AppModelType, DbModelType extends DocumentData = DocumentData> 

方法

方法描述
fromFirestore(快照,选项)由 Firestore SDK 调用,将 Firestore 数据转换为AppModelType类型的对象。您可以通过调用: snapshot.data(options)来访问您的数据。一般来说,从snapshot.data()返回的数据可以转换为DbModelType ;但是,这并不能得到保证,因为 Firestore 不会在数据库上强制实施架构。例如,来自应用程序的先前版本的写入或来自不使用类型转换器的另一个客户端的写入可能写入具有不同属性和/或属性类型的数据。实现需要选择是从不合格的数据中正常恢复还是抛出错误。要重写此方法,请参阅。
toFirestore(模型对象)由 Firestore SDK 调用,将AppModelType类型的自定义模型对象转换为DbModelType类型的纯 JavaScript 对象(适合直接写入 Firestore 数据库)。要将set()mergemergeFields结合使用,必须使用PartialWithFieldValue<AppModelType>定义toFirestore() WithFieldValue<T>类型扩展了T ,还允许将 FieldValue(例如deleteField())用作属性值。
toFirestore(模型对象,选项)由 Firestore SDK 调用,将AppModelType类型的自定义模型对象转换为DbModelType类型的纯 JavaScript 对象(适合直接写入 Firestore 数据库)。与setDoc()一起使用,并使用merge:truemergeFields PartialWithFieldValue<T>类型扩展了Partial<T>以允许将 FieldValue(例如arrayUnion())用作属性值。它还通过允许省略嵌套字段来支持嵌套Partial

FirestoreDataConverter.fromFirestore()

由 Firestore SDK 调用,将 Firestore 数据转换为AppModelType类型的对象。您可以通过调用以下方式访问您的数据: snapshot.data(options)

一般来说, snapshot.data()返回的数据可以转换为DbModelType ;但是,这并不能得到保证,因为 Firestore 不会在数据库上强制实施架构。例如,来自应用程序的先前版本的写入或来自不使用类型转换器的另一个客户端的写入可能写入具有不同属性和/或属性类型的数据。实现需要选择是从不合格的数据中正常恢复还是抛出错误。

要重写此方法,请参阅。

签名:

fromFirestore(snapshot: QueryDocumentSnapshot<DocumentData, DocumentData>, options?: SnapshotOptions): AppModelType;

参数

范围类型描述
快照查询文档快照<文档数据,文档数据>包含您的数据和元数据的QueryDocumentSnapshot
选项快照选项初始调用data()时的SnapshotOptions

返回:

应用程序模型类型

FirestoreDataConverter.toFirestore()

由 Firestore SDK 调用,将AppModelType类型的自定义模型对象转换为DbModelType类型的纯 JavaScript 对象(适合直接写入 Firestore 数据库) 。将set()mergemergeFields一起使用toFirestore()必须使用PartialWithFieldValue<AppModelType>定义

WithFieldValue<T>类型扩展了T ,还允许将 FieldValue(例如deleteField())用作属性值。

签名:

toFirestore(modelObject: WithFieldValue<AppModelType>): WithFieldValue<DbModelType>;

参数

范围类型描述
模型对象带字段值<应用程序模型类型>

返回:

带字段值<数据库模型类型>

FirestoreDataConverter.toFirestore()

由 Firestore SDK 调用,将AppModelType类型的自定义模型对象转换为DbModelType类型的纯 JavaScript 对象(适合直接写入 Firestore 数据库) 。与setDoc()一起使用,并使用merge:truemergeFields

PartialWithFieldValue<T>类型扩展了Partial<T>以允许将 FieldValue(例如arrayUnion())用作属性值。它还通过允许省略嵌套字段来支持嵌套Partial

签名:

toFirestore(modelObject: PartialWithFieldValue<AppModelType>, options: SetOptions): PartialWithFieldValue<DbModelType>;

参数

范围类型描述
模型对象带字段值的部分<应用程序模型类型>
选项设置选项

返回:

带字段值的部分<数据库模型类型>

例子

简单的例子

const numberConverter = {
    toFirestore(value: WithFieldValue<number>) {
        return { value };
    },
    fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions) {
        return snapshot.data(options).value as number;
    }
};

async function simpleDemo(db: Firestore): Promise<void> {
    const documentRef = doc(db, 'values/value123').withConverter(numberConverter);

    // converters are used with `setDoc`, `addDoc`, and `getDoc`
    await setDoc(documentRef, 42);
    const snapshot1 = await getDoc(documentRef);
    assertEqual(snapshot1.data(), 42);

    // converters are not used when writing data with `updateDoc`
    await updateDoc(documentRef, { value: 999 });
    const snapshot2 = await getDoc(documentRef);
    assertEqual(snapshot2.data(), 999);
}

高级示例

// The Post class is a model that is used by our application.
// This class may have properties and methods that are specific
// to our application execution, which do not need to be persisted
// to Firestore.
class Post {
    constructor(
        readonly title: string,
        readonly author: string,
        readonly lastUpdatedMillis: number
    ) {}
    toString(): string {
        return `${this.title} by ${this.author}`;
    }
}

// The PostDbModel represents how we want our posts to be stored
// in Firestore. This DbModel has different properties (`ttl`,
// `aut`, and `lut`) from the Post class we use in our application.
interface PostDbModel {
    ttl: string;
    aut: { firstName: string; lastName: string };
    lut: Timestamp;
}

// The `PostConverter` implements `FirestoreDataConverter` and specifies
// how the Firestore SDK can convert `Post` objects to `PostDbModel`
// objects and vice versa.
class PostConverter implements FirestoreDataConverter<Post, PostDbModel> {
    toFirestore(post: WithFieldValue<Post>): WithFieldValue<PostDbModel> {
        return {
            ttl: post.title,
            aut: this._autFromAuthor(post.author),
            lut: this._lutFromLastUpdatedMillis(post.lastUpdatedMillis)
        };
    }

    fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions): Post {
        const data = snapshot.data(options) as PostDbModel;
        const author = `${data.aut.firstName} ${data.aut.lastName}`;
        return new Post(data.ttl, author, data.lut.toMillis());
    }

    _autFromAuthor(
        author: string | FieldValue
    ): { firstName: string; lastName: string } | FieldValue {
        if (typeof author !== 'string') {
            // `author` is a FieldValue, so just return it.
            return author;
        }
        const [firstName, lastName] = author.split(' ');
        return {firstName, lastName};
    }

    _lutFromLastUpdatedMillis(
        lastUpdatedMillis: number | FieldValue
    ): Timestamp | FieldValue {
        if (typeof lastUpdatedMillis !== 'number') {
            // `lastUpdatedMillis` must be a FieldValue, so just return it.
            return lastUpdatedMillis;
        }
        return Timestamp.fromMillis(lastUpdatedMillis);
    }
}

async function advancedDemo(db: Firestore): Promise<void> {
    // Create a `DocumentReference` with a `FirestoreDataConverter`.
    const documentRef = doc(db, 'posts/post123').withConverter(new PostConverter());

    // The `data` argument specified to `setDoc()` is type checked by the
    // TypeScript compiler to be compatible with `Post`. Since the `data`
    // argument is typed as `WithFieldValue<Post>` rather than just `Post`,
    // this allows properties of the `data` argument to also be special
    // Firestore values that perform server-side mutations, such as
    // `arrayRemove()`, `deleteField()`, and `serverTimestamp()`.
    await setDoc(documentRef, {
        title: 'My Life',
        author: 'Foo Bar',
        lastUpdatedMillis: serverTimestamp()
    });

    // The TypeScript compiler will fail to compile if the `data` argument to
    // `setDoc()` is _not_ compatible with `WithFieldValue<Post>`. This
    // type checking prevents the caller from specifying objects with incorrect
    // properties or property values.
    // @ts-expect-error "Argument of type { ttl: string; } is not assignable
    // to parameter of type WithFieldValue<Post>"
    await setDoc(documentRef, { ttl: 'The Title' });

    // When retrieving a document with `getDoc()` the `DocumentSnapshot`
    // object's `data()` method returns a `Post`, rather than a generic object,
    // which would have been returned if the `DocumentReference` did _not_ have a
    // `FirestoreDataConverter` attached to it.
    const snapshot1: DocumentSnapshot<Post> = await getDoc(documentRef);
    const post1: Post = snapshot1.data()!;
    if (post1) {
        assertEqual(post1.title, 'My Life');
        assertEqual(post1.author, 'Foo Bar');
    }

    // The `data` argument specified to `updateDoc()` is type checked by the
    // TypeScript compiler to be compatible with `PostDbModel`. Note that
    // unlike `setDoc()`, whose `data` argument must be compatible with `Post`,
    // the `data` argument to `updateDoc()` must be compatible with
    // `PostDbModel`. Similar to `setDoc()`, since the `data` argument is typed
    // as `WithFieldValue<PostDbModel>` rather than just `PostDbModel`, this
    // allows properties of the `data` argument to also be those special
    // Firestore values, like `arrayRemove()`, `deleteField()`, and
    // `serverTimestamp()`.
    await updateDoc(documentRef, {
        'aut.firstName': 'NewFirstName',
        lut: serverTimestamp()
    });

    // The TypeScript compiler will fail to compile if the `data` argument to
    // `updateDoc()` is _not_ compatible with `WithFieldValue<PostDbModel>`.
    // This type checking prevents the caller from specifying objects with
    // incorrect properties or property values.
    // @ts-expect-error "Argument of type { title: string; } is not assignable
    // to parameter of type WithFieldValue<PostDbModel>"
    await updateDoc(documentRef, { title: 'New Title' });
    const snapshot2: DocumentSnapshot<Post> = await getDoc(documentRef);
    const post2: Post = snapshot2.data()!;
    if (post2) {
        assertEqual(post2.title, 'My Life');
        assertEqual(post2.author, 'NewFirstName Bar');
    }
}