4

Please help me solve this, I would like to update the fields using dot notation, using set() but each time I run with the below implementation. I have the fields added to firestore as e.g studentInfo.0.course.0.courseId instead of updating the already existing ones.

Json sample as it sits in firestore

    "schoolId": "school123",
    "studentInfo": [
        {
            "studentId": "studentI23",
            "regDate": "2020-04-18",
            "course": [
                {
                    "courseId": "cs123",
                    "regDate": "2020-05-28",
                    "status": "COMPLETED"
                }
            ]
        
        }
    ],
    "registered":"yes"
}

Code logic

const query = firestore.collection('users').where('registered', '==', 'yes')
const students = await query.get()
 students.forEach(student => {
    firestore.doc(student.ref.path).set({
        'studentInfo.0.studentId': '345','studentInfo.0.course.0.courseId': '555'
      }, { merge: true })
 }) 

On the docs https://firebase.google.com/docs/firestore/manage-data/add-data#update_fields_in_nested_objects I can only find updating nested objects but not nested array objects.

SabetiG
  • 379
  • 2
  • 8

2 Answers2

6

It is indeed not possible to update a single element in an array using dot notation, or otherwise. To update an array you'll need to:

  1. Read the document
  2. Get the current value of the array from it
  3. Determine the new array contents
  4. Write the entire updated array back to the database.

The only alternative array operations are array-union and array-remove, which add and remove unique elements to/from the array - essentially treating it as a mathematical set. But since you are looking to update an existing element, these operations are of no use here.

Also see:

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thank you so much for this clear reply and helpful related links @Frank I was loosing my mind over this thinking there's something am missing. But clearly, updating a single element in an array is such a hassle. – SabetiG Oct 20 '20 at 17:35
0

There is no direct way to update the as stated in the article. You can either run a transaction to get the latest array value and then updating the array with the final array value. That would be as below:

await firestore.runTransaction((transaction: Transaction) => {
  const students: Array<Students> = firestore
    .collection("users")
    .where("registered", "==", "yes");
  students.forEach((student) => {
    const firebaseDoc = firestore.doc(student.ref.path);
    transaction.set(
      firebaseDoc,
      {
        "studentInfo.0.studentId": "345",
        "studentInfo.0.course.0.courseId": "555",
      },
      { merge: true }
    );
  });
});

Inside transaction I am getting the array first and then updating each values as per my need. This will make the whole operation atomic so the issues mentioned in the article will not come.

Alternatively, you can also model your firestore database as below

    "schoolId": "school123",
    "studentInfo": {
        "studentI23": {
            "studentId": "studentI23",
            "regDate": "2020-04-18",
            "course": [
                {
                    "courseId": "cs123",
                    "regDate": "2020-05-28",
                    "status": "COMPLETED"
                }
            ]
        
        }
    },
    "registered":"yes"
}

Above I have changed the array to map, since in map you can update the each field based on dot notation fields(doc), hence. you can achieve your end result. This solution will avoid any transaction query and will be faster

AndroidEngineX
  • 975
  • 1
  • 9
  • 22