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