【问题标题】:Update individual map in cloud firestore document更新 Cloud Firestore 文档中的单个地图
【发布时间】:2019-10-23 16:43:52
【问题描述】:

最终更新 我从使用基于 andresmijares 以下答案的事务更改为使用 set()。

这现在允许我将数据写入数据库。

var gradeDocRef = db.collection("students").doc(studentId);
                            console.log(gradeDocRef);

                            var setWithMerge = gradeDocRef.set({
                                "UnitGrades": {
                                            [unitNo]: {
                                                "CG": CG,
                                                "PG": PG, 
                                                "TG": TG
                                                }
                                            }
                                }, { merge: true });

编辑 我根据下面 andresmijares 的评论更改了交易代码。

transaction.set(gradeDocRef, {merge: true}, {

然后得到这个错误?

传递给函数 Transaction.set() 的未知选项“UnitGrades”。可用选项:合并、合并字段


我有一个包含学生集合的云 Firestore 数据库。 每个学生集合都包含一个学生文档,其中包含如下图和子图

UnitGrades: 
    {
     IT1:
       {                                  
        CG: "F"
        PG: "F"                                          
        TG: "F"
        id: "IT1"
        name: "Fundamentals of IT"
        type: "Exam"
    }

我在地图 UnitGrades 中有 10 个单位 每个学生都有相同的单元组合

我想在 bootstrap 中根据 HTML 表单更新地图(表单正在运行,而且很长,所以不要放在这里)

即更改学生成绩

我使用了 firestore 事务更新文档并稍作调整以从 HTML 表单中获取数据。

let studentId = $(this).attr("data-student-id");
let unitNo = $(this).attr("data-unit");
let CG = $(this).attr("data-CG");
let PG = $(this).attr("data-PG");
let TG = $(this).attr("data-TG");

// Create a reference to the student doc.
var gradeDocRef = db.collection("students").doc(studentId);
console.log(gradeDocRef);
    return db.runTransaction(function(transaction) {
// This code may get re-run multiple times if there are conflicts.
    return transaction.get(gradeDocRef).then(function(gradeDoc) {

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

// update the grades using a transaction

   transaction.update(gradeDocRef, {

// in here is my error, I need to be able to select the map
// for the variable for UnitNo only and not wipe the other maps

    "UnitGrades": {

    [unitNo]: {

    "CG": CG,

    "PG": PG, 

    "TG": TG                                                }
});
});

}).then(function() {

console.log("Transaction successfully committed!");

}).catch(function(error) {

console.log("Transaction failed: ", error);
console.log(studentId);

});

我实现的代码更新了正确的单元映射,但随后擦除了 UnitGrades 的其余部分。我真正想要的是更新变量中标识的单位映射

UnitNo,然后保持其余单元不变。

例如目前,如果我更新 IT1,这会正确更新地图中的等级,但随后会从 UnitGrades 地图中擦除单位 IT2、IT3、IT12 等。我真的希望 IT2、IT3、IT12 等保持原样,并使用新值更新 IT1。例如“F”变为“P”

【问题讨论】:

  • 代替更新,使用 set 并传递 { merge: true } 作为第二个参数
  • @andresmijares 我已经尝试过了,编辑了上面的原始问题以显示我现在得到的错误?
  • 看来你有参数错误,让我写一个答案给你检查,我的意思是参考后的第二个参数^^对不起

标签: javascript firebase google-cloud-firestore


【解决方案1】:

以下应该可以解决问题:

  //....
  return db.runTransaction(function(transaction) {
    // This code may get re-run multiple times if there are conflicts.
    return transaction
      .get(gradeDocRef)
      .then(function(gradeDoc) {
        if (!gradeDoc.exists) {
          throw 'Document does not exist!';
        }

        // update the grades using a transaction


        transaction.update(
          gradeDocRef,
          'UnitGrades.' + unitNo,
          {
            CG: CG,

            PG: PG,

            TG: TG
          }
          // in here is my error, I need to be able to select the map
          // for the variable for UnitNo only and not wipe the other maps
        );
      })
      .then(function() {
        console.log('Transaction successfully committed!');
      })
      .catch(function(error) {
        console.log('Transaction failed: ', error);
        console.log(studentId);
      });

通过做

transaction.update(gradeDocRef, {
    "UnitGrades": { ... }
});

您将 整个 UnitGrades 字段替换为新地图,因此您会删除现有地图和子地图的值。

您需要做的只是替换特定的“子图”。为此,您需要使用dot notation,正如documentation 中对update() 方法的解释:“字段可以包含点以引用文档中的嵌套字段。”

请注意,调用update() 方法有两种不同的方式:

update(documentRef: DocumentReference, data: UpdateData): Transaction

update(documentRef: DocumentReference, field: string | FieldPath, value: any, ...moreFieldsAndValues: any[]): Transaction

在这种情况下,我们使用第二种方式,并使用'UnitGrades.' + unitNo(点符号)定义嵌套“子图”的路径。


HTML 测试器页面

如果您想测试建议的解决方案,只需在本地将以下代码保存为 HTML 文件,然后在 a/Adapted Firebase 配置并 b/ 创建一个 ID 为 1 的 Firestore 文档后在浏览器中打开它students 集合。然后修改unitNo的值,在浏览器中刷新页面,就可以在DB中看到更新了。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>

    <script src="https://www.gstatic.com/firebasejs/6.1.1/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/6.1.1/firebase-firestore.js"></script>
  </head>

  <body>
    <script>
      // Initialize Firebase
      var config = {
        apiKey: 'xxxxxx',
        authDomain: 'xxxxxx',
        databaseURL: 'xxxxxx',
        projectId: 'xxxxxx',
        appId: 'xxxxxx'
      };

      firebase.initializeApp(config);

      var db = firebase.firestore();

      let studentId = '1';
      let unitNo = 'IT1';
      let CG = 'F';
      let PG = 'F';
      let TG = 'F';

      // Create a reference to the student doc.
      var gradeDocRef = db.collection('students').doc(studentId);
      console.log(gradeDocRef);
      db.runTransaction(function(transaction) {
        // This code may get re-run multiple times if there are conflicts.
        return transaction
          .get(gradeDocRef)
          .then(function(gradeDoc) {
            if (!gradeDoc.exists) {
              throw 'Document does not exist!';
            }

            transaction.update(
              gradeDocRef,
              'UnitGrades.' + unitNo,
              {
                CG: CG,

                PG: PG,

                TG: TG
              }

            );
          })
          .then(function() {
            console.log('Transaction successfully committed!');
          })
          .catch(function(error) {
            console.log('Transaction failed: ', error);
            console.log(studentId);
          });
      });
    </script>
  </body>
</html>

【讨论】:

  • 如果您还解释了为什么这样做会很好,以及为什么原始代码不起作用。
  • 不幸的是,这不起作用。它在控制台中抛出一个错误 - Unexpected token +
  • @user1693026 其实我已经彻底测试过了,应该可以正常工作了。
  • 我已经仔细检查并测试了代码以使用您的答案,但即使我现在再次获得更新,也会遇到使用合并时确定的相同问题。每次我在新文档上重新运行代码时,它都会更新所有以前更新的值。出现的错误是----> firestore.googleapis.com/v1/projects/myctec-cfbs/databases/(default)/documents:commit:1 POST firestore.googleapis.com/v1/projects/myctec-cfbs/数据库/… 400
  • @user1693026 我刚刚在我的答案中添加了一个网页代码,您可以使用它来测试所提出的解决方案是否真的有效。
【解决方案2】:

更改这些行:

transaction.update(gradeDocRef, {
    "UnitGrades": {
    [unitNo]: {
       "CG": CG,
       "PG": PG, 
       "TG": TG                                                }
});

为此

transaction.set(gradeDocRef, {
    `UnitGrades.${unitNo}`: {
       "CG": CG,
       "PG": PG, 
       "TG": TG 
}, { merge: true });

据我所知,它是这样工作的:

假设您的文档如下所示:

 {
   "fantasticsFours": {
     "thing": { ... },
     "susan": { ... },
     "mister": { ... }
   }
 }

我们需要添加{"humanTorch" :{...}}

带集合+合并

db.collection('heroes').doc(`xxxXXXxxx`).set({
  "fantasticsFours": {
    "humanTorch":{ ... }
  }
}, {merge:true})

将产生以下数据:

 {
   "fantasticsFours": {
     "thing": { ... },
     "susan": { ... },
     "mister": { ... },
     "humanTorch":{ ... }
   }
 }

有更新

db.collection('heroes').doc(`xxxXXXxxx`).update({
  "fantasticsFours": {
    "humanTorch":{ ... }
  }
})

将产生以下数据:

 {
   "fantasticsFours": {
     "humanTorch":{ ... }
   }
 }

更多here

【讨论】:

  • 我现在收到一个错误,它似乎更新了地图中所有以前更新的成绩 - firestore.googleapis.com/v1/projects/myctec-cfbs/databases/(default)/documents:commit: 1 个帖子firestore.googleapis.com/v1/projects/myctec-cfbs/databases/… 400
  • 所以它现在将更新所有已编辑等级的单元。而不仅仅是单位没有被看。
  • 是不是因为每次将gradeDocRef 添加到而不是在每次加载表单时重新开始?文档更新后有没有办法清除它?
  • 我只是更好地阅读了您的代码,您为什么在这里使用事务?为什么不直接使用 set 呢?看看这个firebase.google.com/docs/firestore/manage-data/transactions,感觉它不适合你的用例
  • 我已更新代码以直接使用 set,但与使用您的方法进行交易时出现的错误相同。我已经编辑了原始帖子以解释现在的问题。
猜你喜欢
  • 2018-09-21
  • 2021-01-24
  • 1970-01-01
  • 2018-05-08
  • 1970-01-01
  • 2018-11-28
  • 2021-07-31
  • 2019-07-15
  • 1970-01-01
相关资源
最近更新 更多