-2

我实际上是在一个 Javascript 项目中使用 Firebase,并试图获取集合中所有元素的嵌套子元素的信息。

由于我需要递归触发数据库,​​因此我正在尝试创建一个自调用递归方法,该方法将映射实际的数据库模式并提取所需的数据。

为了这篇文章的目的,我创建了一个假方法来展示我的实际逻辑。也许有人可以帮助我,这是一个永无止境的循环,无限触发最后一项。它从不存储第一个结果

function fakeWait(toReturn) {
  return new Promise((resolve) => {
    setTimeout(function(){ resolve(toReturn); }, 8000);
  });
}

function callMe(params = null) {
  return new Promise((resolve, reject) => {
   console.log('Promise called', {...params});

    const promises = [];

    if (params === null) {
      promises.push(fakeWait(
        {
          id1:{title:'Title1'},
          id2:{title:'Title2'},
          id3:{title:'Title3'},
          id4:{title:'Title4'},
        }).then(results => {
          params = {};
          params.formations = results;
          resolve(callMe(params));
        }));
    }

    else {
      if (!params.hasOwnProperty('formationId') && 
          params.hasOwnProperty('formations')) {
        Object.keys(params.formations).forEach(formationId => {
          params.formationId = formationId;
          promises.push(resolve(callMe(params)));
        });
            }

      else if (params.hasOwnProperty('formationId') && 
               params.hasOwnProperty('formations') && 
               !params.formations[params.formationId].hasOwnProperty.modules) 
      {
        promises.push(fakeWait({
          id1:{title:'Title1.1'},
          id2:{title:'Title1.2'},
          id3:{title:'Title1.3'},
          id4:{title:'Title1.4'},
        }).then(result => {
          params.formations[params.formationId].modules = result;
          resolve(callMe(params));
        }))
            }
    }

    Promise.all(promises).then(() => { console.log('Resolved.'); resolve(params); }).catch(()=> reject('oops'));
  });
}

callMe().then(results =>console.log(results)).catch(msg => console.log(msg));

您还可以在此处查看和尝试 stackblitz 上的代码:https ://stackblitz.com/edit/js-wuvp9z

Firebase 数据结构:

Formations - Collection
- Formation - Document
-- Modules - Collection
--- Module - Document
---- Chapters - Collection
----- Chapter - Document
------ Screens - Collection
------- Screen - Document
4

1 回答 1

1

这是 Firestore 事务的代码,它将编写一个新的 Screen 文档并nbrScreens分别更新 Formation、Module 和 Chapter 父文档中的计数器。

它是一个完整的 HTML 页面。执行以下操作:

1/ 在 HTML 中调整您的 Firebase 配置值。

2/ 在 Firestore 中创建具有所需 ID 的父集合和文档。

3/ 为每个文档(Formation、Module 和 Chapter)添加一个类型为 number 的字段,名称为nbrScreens0

4/ 在 HTML 页面中,将 的值screenId和函数调用screenData的参数调整为setNewScreen()您想要的。

5/ 在浏览器中打开页面(例如本地),该函数将被调用并执行事务

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="https://www.gstatic.com/firebasejs/5.0.4/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/5.0.4/firebase-firestore.js"></script>
</head>

<body>

<script>

    var config = {
        apiKey: "...",
        authDomain: "...",
        databaseURL: "...",
        projectId: "..."
    };

    firebase.initializeApp(config);

    var firestoredb = firebase.firestore();


    function setNewScreen(formationId, moduleId, chapterId, screenId, screenData) {

        var formationDocRef = firestoredb.collection("Formations").doc(formationId);
        var moduleDocRef = formationDocRef.collection("Modules").doc(moduleId);
        var chapterDocRef = moduleDocRef.collection("Chapters").doc(chapterId);
        var screenDocRef = chapterDocRef.collection("Screens").doc(screenId);

        return firestoredb.runTransaction(function (transaction) {

            var newChaptersNbrScreens;
            var newModulesNbrScreens;
            var newFormationsNbrScreens;

            return transaction.get(chapterDocRef)
                .then(function (sfDoc) {

                    if (!sfDoc.exists) {
                        throw "Document Chapter does not exist!";
                    }

                    newChaptersNbrScreens = sfDoc.data().nbrScreens + 1;
                    return transaction.get(moduleDocRef);

                })
                .then(function (sfDoc) {

                    if (!sfDoc.exists) {
                        throw "Document Module does not exist!";
                    }

                    newModulesNbrScreens = sfDoc.data().nbrScreens + 1;
                    return transaction.get(formationDocRef);

                })
                .then(function (sfDoc) {

                    if (!sfDoc.exists) {
                        throw "Document Formation does not exist!";
                    }

                    newFormationsNbrScreens = sfDoc.data().nbrScreens + 1;
                    return transaction
                        .set(formationDocRef, {nbrScreens: newFormationsNbrScreens}, { merge: true })
                        .set(moduleDocRef, {nbrScreens: newModulesNbrScreens}, { merge: true })
                        .set(chapterDocRef, {nbrScreens: newChaptersNbrScreens}, { merge: true })
                        .set(screenDocRef, screenData)

                });

        });

    }


    //Calling the function
    //The formation, module and chapter docs must be created before calling it!!

    var screenId = 's1';
    var screenData = {title: 'screenTitle_s1', content: 'foo'};

    setNewScreen("f1", "m1", "c1", screenId, screenData)  //Screen will be saved under Formation f1, Module m1 and Chapter c1
        .then(function () {
            console.log("Transaction successfully committed!");
        })
        .catch(function (error) {
            console.log("Transaction failed: ", error);
        });


</script>


</body>
</html>

请注意,您可以调整代码以创建 Formation、Module 和 Chapter。事实上,这不能在事务中完成,因为所有读取都应在写入之前执行

你可以像(伪代码):

FormationDocumentReference.set({}) // <- returns a promise
.then(function() {
    ModuleDocumentReference.set({}) // <- returns a promise
})
.then(function() {
    ChapterDocumentReference.set({}) // <- returns a promise
})
.then(function() {
    setNewScreen(id_f, id_m1, id_c1, screenId, screenData)  // <- returns a promise
.then(function () {
    console.log("Transaction successfully committed!");
})
.catch(function (error) {
    console.log("Transaction failed: ", error);
});

最后,关于上面的事务代码,有几点值得注意:

1/transaction.get()返回一个包含非 null 的承诺firebase.firestore.DocumentSnapshot,如此所述。因此,您必须将它们与then().

2/ 另一方面,transaction.set()返回“This Transaction instance”,见这里。因此,您应该将它们一个接一个地链接起来(例如return transaction.set().set()...

于 2018-05-30T20:44:44.543 回答