简体   繁体   English

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

[英]Get firestore documents from array of document references

Currently, I am working on a pretty complicated set of queries to firestore.目前,我正在对 Firestore 进行一组非常复杂的查询。

I am trying, all at once, to populate an array full of references to other documents, and then read those documents and put the information in an array.我正在尝试一次填充一个充满对其他文档的引用的数组,然后读取这些文档并将信息放入一个数组中。

More specifically to this example, I have 4 references in one collection.更具体地说,对于这个示例,我在一个集合中有 4 个引用。 I want to get those references, and then using those references, query 4 documents and populate an array with the information.我想获取这些引用,然后使用这些引用,查询 4 个文档并用信息填充一个数组。

The order is as follows: do a query for all of the documents in the tags subcollection, which is handled by the function below:顺序如下:对tags子集合中的所有文档进行查询,由下面的 function 处理:

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 works fine with the following code performing the subscription:这适用于执行订阅的以下代码:

this.pageService
      .getTagsOnPage(this.workspaceId, this.currentPage.id)
      .subscribe((data) => {
        temp = data;
      });

data is as follows, via the console: data如下,通过控制台:

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

Now, to perform the next set of data reads is where my confusion begins.现在,执行下一组数据读取是我开始困惑的地方。

My initial approach was to try a for loop (for the length of this array), but this would involve iterating performing a number of nested subscriptions, which I do not think is possible in this sense.我最初的方法是尝试一个 for 循环(对于这个数组的长度),但这将涉及迭代执行许多嵌套订阅,我认为这在这个意义上是不可能的。

I am fairly new to rxjs, and have only used the map and switchMap operators.我对 rxjs 相当陌生,并且只使用了 map 和 switchMap 运算符。 In this case, I am imagining I would use something like mergeMap and/or flatMap, but frankly, I am not sure how to make this work in this case.在这种情况下,我想我会使用诸如mergeMap和/或flatMap,但坦率地说,我不确定如何在这种情况下进行这项工作。 Also, dealing with the for loop where I need to grab documents based on the array of documentReferences I get is also throwing me for a loop.此外,处理我需要根据我得到的 documentReferences 数组获取文档的 for 循环也让我陷入了循环。

This is my current implementation, which is all over the place;这是我目前的实现,到处都是; I hope the feel for what I am trying to do is there.我希望我正在尝试做的事情的感觉就在那里。 Basically, get the array of references via getTagsOnPage, wait for the observable to end, use switchMap to take the data array and loop over it;基本上,通过 getTagsOnPage 获取引用数组,等待 observable 结束,使用 switchMap 获取数据数组并循环遍历它; ideally, subscribe to each ref and add to tagData, and then return that:理想情况下,订阅每个 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;

Messy, I know, but I think once I know the right operators to use this will make a lot more sense.混乱,我知道,但我认为一旦我知道正确的运营商来使用它就会更有意义。

Also, atm this returns an empty array.此外, atm this 返回一个空数组。 There is an error for when I use switchMap that says the following:当我使用 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>'.

Thank you for any help!感谢您的任何帮助!

The reason for your error using switchMap is because you are not returning an Observable.使用switchMap出错的原因是您没有返回 Observable。

When using any of the " Higher Order Mapping Operators " (switchMap, concatMap, mergeMap, etc), the provided function must return an observable.当使用任何“ 高阶映射运算符”(switchMap、concatMap、mergeMap 等)时,提供的 function 必须返回一个 observable。 As the error states: "Type void is not assignable to type ObservableInput<any> ", you aren't returning anything (void).正如错误所述:“类型void不可分配给类型ObservableInput<any> ”,您没有返回任何内容(void)。

The next thing that isn't quite right is that within your loop, you reference ref.snapshotChanges().pipe() , but you never subscribe to it.下一件不太正确的事情是,在您的循环中,您引用ref.snapshotChanges().pipe() ,但您从未订阅它。 As you may know, observables are lazy and won't fire unless there is a subscriber.如您所知,可观察对象是惰性的,除非有订阅者,否则不会触发。

As long as you return an observable inside of switchMap() , it will automatically subscribe to it and emit its values.只要你在switchMap()中返回一个 observable,它就会自动订阅它并发出它的值。

Let's think about this a little bit differently;让我们换个角度想一想; instead of looping over the results of your first call, executing them, then pushing the values into an array.而不是循环第一次调用的结果,执行它们,然后将值推送到数组中。 We can instead take the results and turn them into an observable stream that emits all the results of their individual calls, and combines them into an array for you.我们可以取而代之,将结果转换为可观察的 stream,它会发出它们各自调用的所有结果,并将它们组合成一个数组供您使用。 But... with a subtle difference: I'm suggesting not to have a separate tagData array outside the stream, but to have your observable return the tagData form that you need as an Observable<TagData[]> .但是......有一个细微的区别:我建议不要在 stream 之外有一个单独的tagData数组,而是让你的 observable 返回你需要的tagData表单作为Observable<TagData[]>

I think something like this will work for you:我认为这样的事情对你有用:

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], [])
  })
);

Let's break that down!让我们打破它!

  1. We use from to create an observable from your array, that emits each "reference" one at a time.我们使用from从您的数组中创建一个 observable,它一次发出每个“引用”。

  2. mergeMap will subscribe to the observable we are creating and emit its emissions mergeMap将订阅我们正在创建的 observable 并发出它的发射

  3. map simply transforms the value into your desired shape map只是将值转换为您想要的形状

  4. scan accumulates your emissions for you and emits on each change. scan会为您累积排放量,并在每次更改时排放量。 if you don't want to emit until all calls come back, use reduce() instead如果您不想在所有调用返回之前发出,请改用reduce()

Now, you can simply do: tagData$.subscribe() and do what you want with your resulting array of data.现在,您可以简单地执行以下操作: tagData$.subscribe()并对生成的数据数组执行您想要的操作。 Or you could even use the async pipe in your template rather than subscribing in your component.或者你甚至可以在你的模板中使用异步 pipe而不是在你的组件中订阅。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM