从文档引用数组中获取Firestore文档

3

目前,我正在处理一组与Firestore相关的相当复杂的查询。

我试图一次性填充一个数组,其中包含对其他文档的引用,并读取这些文档并将信息放入数组中。

更具体地说,在此示例中,我有一个集合中的4个引用。我想获取这些引用,然后使用这些引用查询4个文档,并使用这些文档填充一个数组。

顺序如下:使用下面的函数执行对tags子集合中所有文档的查询:

getTagsOnPage(workspaceId: string, pageId: string) {
    // get all of the references in the tags sub-collection, and puts them in an array
    // get all id's, do not get data
    return this.afs
      .collection("users")
      .doc(`${auth().currentUser.uid}`)
      .collection<Workspace>("workspaces")
      .doc(`${workspaceId}`)
      .collection<Page>("pages")
      .doc(`${pageId}`)
      .collection("tags")
      .snapshotChanges()
      .pipe(
        map((actions) => {
          return actions.map((a) => {
            const ref = a.payload.doc.get("reference");
            return ref; // return an array of (id: reference) key-value pairs
          });
        })
      );
  }

这段代码实现订阅功能,运行良好:
this.pageService
      .getTagsOnPage(this.workspaceId, this.currentPage.id)
      .subscribe((data) => {
        temp = data;
      });

data的数据如下,通过控制台输出:

(3) ["/users/ucB5cF4Pj3PWhRn10c9mvqQbS7x2/workspaces/It1…1tSnPI5GJrniY82vZL/localTags/1p5Tscwn14PyK6zRaFHX", "/users/ucB5cF4Pj3PWhRn10c9mvqQbS7x2/workspaces/It1tSnPI5GJrniY82vZL/localTags/lFKoB0jngmtnALut2QS2", "/users/ucB5cF4Pj3PWhRn10c9mvqQbS7x2/workspaces/It1tSnPI5GJrniY82vZL/localTags/r6sf2SX6v87arU2rKsD5"]

现在,我开始困惑的是执行下一组数据读取的方式。
我的初始做法是尝试使用for循环(对于该数组的长度),但这需要进行多个嵌套订阅,我不认为这在这种情况下是可能的。
我对rxjs比较陌生,只使用过map和switchMap操作符。在这种情况下,我想使用类似于mergeMap和/或flatMap的东西,但是实际上,我不确定如何在这种情况下使其工作。另外,处理需要根据获取的documentReferences数组抓取文档的for循环也让我感到困扰。
这是我当前的实现,它非常混乱;我希望能够理解我的意图。基本上,通过getTagsOnPage获取引用数组,等待可观察对象结束,使用switchMap获取数据数组并循环遍历它; 理想情况下,订阅每个ref并添加到tagData中,然后返回结果:
let tagData;
    this.pageService.getTagsOnPage(this.workspaceId, this.currentPage.id).pipe(
      switchMap((data) => {
        let references = data;
        for (let j = 0; j < references.length; j++) {
          let ref = this.afs.doc(`${references[j]}`);
          ref.snapshotChanges().pipe(
            map((actions) => {
              const data = actions.payload.data();
              tagData.push(data);
            })
          );
          // push the data (different data var)
        }
      })
    );
    return tagData;

有点混乱,但我认为一旦我知道正确的运算符使用方法,这将变得更加清晰。

同时,目前返回一个空数组。当我使用switchMap时会出现以下错误:

Argument of type '(data: any[]) => void' is not assignable to parameter of type '(value: any[], index: number) => ObservableInput<any>'.
  Type 'void' is not assignable to type 'ObservableInput<any>'.


感谢您的帮助!

combineLatest 可能是您的情况下正确的运算符。您可以将 getTagsOnPage 的结果 mapthis.afs.doc().snapshotChanges() 并将其传递给该运算符。 - Buczkowski
1个回答

1
使用 switchMap 时出现错误的原因是你没有返回 Observable。
当使用任何 "Higher Order Mapping Operators" (switchMap、concatMap、mergeMap 等) 时,提供的函数必须返回一个 observable。正如错误所述: "Type void is not assignable to type ObservableInput<any>",你没有返回任何东西 (void)。
接下来不太正确的是,在循环中,你引用了 ref.snapshotChanges().pipe(),但你从未订阅它。就像你可能知道的那样,observables 是惰性的,除非有订阅者,否则不会触发。
只要在 switchMap() 中返回一个 observable,它就会自动订阅并发出其值。
让我们换个思路考虑一下,不要循环遍历第一个调用的结果,执行它们,然后将值推入数组中。相反,我们可以将结果转换为可观察流,该流会发出其各自调用的所有结果,并将它们合并成一个数组。但是......有一个微妙的区别:我建议不要在流外部使用单独的tagData数组,而是让您的可观察对象返回您需要的tagData形式,作为Observable<TagData[]>
我认为像这样做会对您有所帮助:
tagData$ = this.pageService.getTagsOnPage(this.workspaceId, this.currentPage.id).pipe(
    switchMap(references => from(references)),
    mergeMap(ref => this.afs.doc(ref).snapshotChanges()),      
    map(actions => actions.payload.data()),
    scan((allTagData, tagData) => [...allTagData, tagData], [])
  })
);

让我们分解一下!

  1. 我们使用 from 从数组中创建一个可观察对象,逐个发出每个“引用”。

  2. mergeMap 将订阅我们正在创建的可观察对象并发出其发射物。

  3. map 简单地将值转换为您所需的形状。

  4. scan 积累您的发射物并在每次更改时发出。如果您不想等到所有调用都返回才发出,请改用 reduce()

现在,你只需执行:tagData$.subscribe()并使用生成的数据数组进行操作。或者你甚至可以在模板中使用async pipe而不是在组件中订阅。

1
太棒了!感谢您的回复!它的设置方式将使tagData作为一个单独的数组进行编辑(这只是为了在打开组件时获取值),但这比我们想出来的方法要好得多(一堆子程序)。 - Mason Secky-Koebel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接