递归调用Firestore [英] Recursive calls to Firestore

查看:83
本文介绍了递归调用Firestore的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Firebase Firestore,其中以组件"作为根集合.集合中的每个文档(组件")可以具有一个称为子项"的数组,该数组中的每个项目都是另一个组件的"id",本质上是一对多的关系.由于每个孩子也是一个组成部分,因此它也可能有自己的孩子,依此类推.

I have a Firebase Firestore with "Components" as a root collection. Each document (a "Component") in the collection may have an array called "children", with each item in the array being an "id" of another component, essentially, a one-to-many relationship. Since every child is also a component it may also have its own children and so forth.

1_Parent (document)
│ name: 'Parent'
│ id: '1_Parent'
└─children (array)
   ├─1.1_Child_one
   └─1.2_Child_two

第一个孩子

1.1_Child_one (document)
     name: 'Child One'
     id: '1.1_Child_one'

第二个孩子

1.2_Child_two (document)
│ name: 'Child Two'
│ id: '1.2_Child_two'
└─children (array)
   ├─1.2.1_Grandchild_one
   └─1.2.2_Grandchild_two

第一个孙子

1.2.1_Grandchild_one (document)
     name: 'Grandchild One'
     id: '1.2.1_Grandchild_one'

第二代孙子

1.2.2_Grandchild_two (document)
 name: 'Grandchild Two'
 id: '1.2.2_Grandchild_two'

在我的代码中,我想为每个组件创建一个对象,如果它具有子数组,则该数组中的每个id都将替换为从Firestore检索的完全成熟的对象.

In my code, I want to create an object for each component and if it has a children array then each of the id in the array is replaced by a fully fledged object retrieved from Firestore.

输出对象树应如下图所示

The output object tree should look like this

1_Parent
│ name: 'Parent'
│ id: '1_Parent'
└─children
   ├─1.1_Child_one
   │    name: 'Child One'
   │    id: '1.1_Child_one'
   └─1.2_Child_two
     │  name: 'Child Two'
     │  id: '1.2_Child_two'
     └─children
        ├─1.2.1_grandhild_one
        │   name: 'Grandchild One'
        │   id: '1.2.1_grandhild_one'
        └─1.2.2_grandhild_two
            name: 'Grandchild Two'
            id: '1.2.2_grandhild_two'

JSON输出对象应如下所示

The output object as JSON should look like this

{
  "name": "Parent",
  "id": "1_Parent",
  "children": [
    {
      "name": "Child One",
      "id": "1.1_Child_one"
    },
    {
      "name": "Child Two",
      "id": "1.2_Child_two",
      "children": [
        {
          "name": "Grandchild One",
          "id": "1.2.1_Grandchild_one"
        },
        {
          "name": "Grandchild Two",
          "id": "1.2.2_Grandchild_two"
        }
      ]
    }
  ]
}

很明显,我们需要在这里进行递归,但是我完全不知道如何使用RxJS创建递归函数.我会答应一些提示或示例代码以允许这样做.

It is obvious, we need recursion here, but I am at complete loss about how to create a recursive function using RxJS. I would appreciate some tips or example code for allowing to do so.

请注意,我正在Angular项目中使用它,并且正在使用AngularFire来访问Firebase-Firestore.

Note, I am using this in an Angular project and I am using AngularFire to access Firebase-Firestore.

推荐答案

使用expand运算符可以最好地解决RxJS中的递归问题.您为其提供了一个投影函数,该函数返回一个Observable,在收到通知时,将使用发出的值再次调用该投影函数.只要您的内部Observable不发出EMPTYcomplete,它就会执行此操作.

Recursion in RxJS is best tackled with the use of the expand operator. You provide it with a projection function that returns an Observable, that, on notification, calls the projection function again with the emitted value. It does this for as long as your inner Observable is not emitting EMPTY or complete.

这样做的同时,每个通知也都转发给expand的订阅者,这与传统的递归不同,在传统的递归中,您只能在最后获得结果.

While it does that, every notification is also forwarded to the subscribers of expand, unlike with a traditional recursion where you'll only get the result at the very end.

摘自expand上的官方文档:

递归地将每个源值投影到一个Observable中,该值将合并到输出Observable中.

Recursively projects each source value to an Observable which is merged in the output Observable.

让我们看看您的示例.如果没有RxJS,则如果我们有一个同步数据源为我们提供了节点的每个子节点(我们将其称为getChildById),则该函数可能如下所示:

Let's look at your example. Without RxJS, if we had a synchronous datasource that gave us each child of a node (let's call it getChildById), the function could look like this:

function createFamilyTree(node) {
    node.children = node.childrenIds.map(childId => 
        createFamilyTree(
            getChildById(childId)
        )
    );
    return node;
}

现在,我们将使用expand运算符将其转换为RxJS:

Now, we'll translate it to RxJS with the use of the expand operator:

parentDataSource$.pipe(
    map(parent => ({node: parent})),

    expand(({node}) => // expand is called for every node recursively 
                          (i.e. the parent and each child, then their children and so on)

        !node ? EMPTY : // if there is no node, end the recursion

        from(node.childrenIds) // we'll convert the array of children 
                                  ids to an observable stream

            .pipe(
                mergeMap(childId => getChildById(childId) // and get the child for 
                                                              the given id

                    .pipe(
                        map(child => ({node: child, parent: node}))) // map it, so we have 
                                                                       the reference to the 
                                                                       parent later on
                )
            )
    ),

    // we'll get a bunch of notifications, but only want the "root", 
       that's why we use reduce:

    reduce((acc, {node, parent}) =>
        !parent ?
            node : // if we have no parent, that's the root node and we return it
            parent.children.push(node) && acc // otherwise, we'll add children
    )
);

在RxJS官方文档页面上详细说明了所使用的运算符: from 扩展

The used operators are explained in detail on the official RxJS documentation page: from, expand, reduce

编辑:可以在此处找到上述代码的经过测试的干净版本: https://stackblitz.com/edit/rxjs-vzxhqf?devtoolsheight=60

EDIT: A clean and tested version of the above code can be found here: https://stackblitz.com/edit/rxjs-vzxhqf?devtoolsheight=60

这篇关于递归调用Firestore的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆