在 AngularFire 中检索嵌套 Firestore 查询的 RxJS Observable
Posted
技术标签:
【中文标题】在 AngularFire 中检索嵌套 Firestore 查询的 RxJS Observable【英文标题】:Retrieving an RxJS Observable of a nested Firestore query in AngularFire 【发布时间】:2021-08-23 20:05:07 【问题描述】:我正在尝试将带有 DocumentReference
s 的 Object 的 Observable 转换为我的整个 Object 的 Observable。
我的 Firestore 查询返回 QuestDocument
的 Observable,如下所示(去除原始类型):
export interface QuestDocument
...
owner: DocumentReference<User>;
...
collaborators?: DocumentReference<User>[];
categories?: DocumentReference<Category>[];
在我的转换器中,我可以调用其他 Firestore 服务来检索 DocumentReference
s 到 User
和 Category
的值(扁平结构,所以这里没有问题)。
我的目标是创建一个 Quest
类型的 Observable,但我的嵌套 Observable 没有被正确解析。
export interface Quest
...
owner: User;
...
collaborators?: User[];
categories?: Category[];
这是我目前所拥有的:
doc$(docId: string): Observable<Quest>
return this.doc(docId).valueChanges()
.pipe(
mergeMap(questDoc =>
const owner$ = this.userService.doc$(questDoc.owner.id);
const collaborators$ = forkJoin(questDoc.collaborators.map(
(userRef: DocumentReference) =>
return this.userService.doc$(userRef.id)
));
const categories$ = forkJoin(questDoc.categories.map(
(categoryRef: DocumentReference) => this.categoryService.doc$(categoryRef.id)
));
const joined = forkJoin(
owner: owner$,
collaborators: collaborators$,
categories: categories$
);
joined.subscribe(data => console.log(data));
return joined.pipe(
map(value =>
return Object.defineProperties(questDoc,
qid: value: docId ,
owner: value: value.owner ,
collaborators: value: value.collaborators ,
categories: value: value.categories
) as Quest;
)
)
)
);
所有类型都匹配,我应该收到一个Observable<Quest>
,但是当我尝试打印该值时,它返回undefined
,而第二个.pipe()
永远不会到达。
【问题讨论】:
forkJoin
只会在所有源 observables 都发射并完成后发射。对categoryService.doc$()
和userService.doc$()
的调用会返回完整的可观察对象吗?如果没有,您可以将 .pipe(first())
添加到您的 joined
forkJoin 中的 3 个来源中的每一个。
我不确定 completion 在 Observable 方面的含义,因为我对这些概念还很陌生。两个服务的.doc$()
调用返回各自类文档类型的Observables(参见QuestDocument
),映射到实现的类型(仅将文档的ID 添加到Category
和User
)。检索类文档类型的底层函数是 AngularFire 的 Firestore SDK 的扩展。
【参考方案1】:
您没有正确使用forkJoin
运算符。
一个完整的 observable 将不再发出数据。把它当作一个封闭的管道。
ForkJoin
将等待所有流完成(关闭),然后再发出一个数据。
如果您使用 this.afs.collection(...).doc(...).valueChanges()
获取数据,这些 observable 将保持活动状态。每次在 firestore 中更新数据时,它们都会发出。
要完成它们,请使用 take(1)
或 first()
(它们将发出一次然后完成),或使用 combineLatest()
组合活动流并实时更新您的数据(不要忘记取消订阅 onDestroy以防止任何内存泄漏)。
这是一个完整流的示例:
doc$(docId: string): Observable<Quest>
return this.doc(docId).valueChanges()
.pipe(
mergeMap(questDoc =>
// observable will emit then complete thanks to the "take(1)"
const owner$ = this.userService.doc$(questDoc.owner.id).pipe(take(1));
const collaborators$ = forkJoin(questDoc.collaborators.map(
(userRef: DocumentReference) =>
// same thing here
return this.userService.doc$(userRef.id).pipe(take(1))
));
const categories$ = forkJoin(questDoc.categories.map(
// and here
(categoryRef: DocumentReference) => this.categoryService.doc$(categoryRef.id).pipe(take(1))
));
const joined = forkJoin(
owner: owner$,
collaborators: collaborators$,
categories: categories$
);
// joined.subscribe(data => console.log(data));
return joined.pipe(
// NEVER subscribe within a pipe, use a tap operator for side effects
tap(data => console.log(data)),
map(value =>
return Object.defineProperties(questDoc,
qid: value: docId ,
owner: value: value.owner ,
collaborators: value: value.collaborators ,
categories: value: value.categories
) as Quest;
)
)
)
);
【讨论】:
非常感谢!我被这个问题困扰了很长一段时间,从来没有真正考虑过完成/底层的 Observables 仍然处于活动状态。还要感谢指向实时更新可能性的指针 (combineLatest()
),因为那是我接下来要尝试的 :)以上是关于在 AngularFire 中检索嵌套 Firestore 查询的 RxJS Observable的主要内容,如果未能解决你的问题,请参考以下文章
AngularFire2 检索不订阅更改(Firestore)
使用 AngularFire 和 Firestore 使用 Ng2 智能表显示嵌套对象
在 ngFor 中使用带有 angularfire2 的 firebase-src 指令失败
更新 .snapshotChanges() 代码以使用 angularfire2 5.0..0 rc-8、firebase 5.0.2