withConverter()
使用轉換器將AppModelType
類型的使用者物件轉換為DbModelType
類型的 Firestore 數據。
使用轉換器可讓您在從 Firestore 儲存和擷取物件時指定通用類型參數。
在這種情況下,「AppModel」是在應用程式中用於將相關資訊和功能打包在一起的類別。例如,這樣的類別可以具有複雜的巢狀資料類型的屬性、用於記憶的屬性、Firestore 不支援的類型的屬性(例如symbol
和bigint
),以及執行複合操作的輔助函數。此類類別不適合和/或無法儲存到 Firestore 資料庫中。相反,此類別的實例需要轉換為具有專有原始屬性的「普通舊 JavaScript 物件」(POJO),可能嵌套在其他 POJO 或 POJO 陣列中。在此上下文中,此類型稱為“DbModel”,並且是適合持久保存到 Firestore 中的物件。為了方便起見,應用程式可以實作FirestoreDataConverter
並將轉換器註冊到 Firestore 對象,例如DocumentReference
或Query
,在儲存到 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() 與merge 和mergeFields 結合使用,必須使用PartialWithFieldValue<AppModelType> 定義toFirestore() 。 WithFieldValue<T> 型別擴展了T ,也允許將 FieldValue(例如deleteField())用作屬性值。 |
toFirestore(模型對象,選項) | 由 Firestore SDK 調用,將AppModelType 類型的自訂模型物件轉換為DbModelType 類型的純 JavaScript 物件(適合直接寫入 Firestore 資料庫)。與setDoc()一起使用,並使用merge:true 或mergeFields 。 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()
與merge
和mergeFields
一起使用, toFirestore()
必須使用PartialWithFieldValue<AppModelType>
定義。
WithFieldValue<T>
型別擴展了T
,也允許將 FieldValue(例如deleteField())用作屬性值。
簽名:
toFirestore(modelObject: WithFieldValue<AppModelType>): WithFieldValue<DbModelType>;
參數
範圍 | 類型 | 描述 |
---|---|---|
模型對象 | 帶字段值<應用程式模型類型> |
返回:
帶字段值<資料庫模型類型>
FirestoreDataConverter.toFirestore()
由 Firestore SDK 調用,將AppModelType
類型的自訂模型物件轉換為DbModelType
類型的純 JavaScript 物件(適合直接寫入 Firestore 資料庫) 。與setDoc()一起使用,並使用merge:true
或mergeFields
。
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');
}
}