Update individual map in cloud firestore document

The following should do the trick:

  //....
  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);
      });

By doing

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

you are replacing the entire UnitGrades field by a new map, therefore you erase the existing map and submaps values.

What you need to do is to only replace a specific "submap". For that you need to use the dot notation, as explained in the documentation for the update() method: "Fields can contain dots to reference nested fields within the document."

Note that there are two different ways to call the update() method:

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

or

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

In this case we use the second way and we define the path of the nested "submap" with 'UnitGrades.' + unitNo (dot notation).


HTML Tester page

If you want to test the proposed solution, just save locally the following code as an HTML file and open it in a browser after you have a/Adapted the Firebase config and b/ created a Firestore document with id 1 under the students collection. Then change the value of unitNo, refresh the page in the browser and you will see the updates in the 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>

change these lines:

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

for this

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

It works like this as far as I know:

assuming you doc looks like this:

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

we need to add {"humanTorch" :{...}}

With set + merge

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

will result in this data:

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

with Update

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

will result in this data:

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

More here