Gerardlamo
Gerardlamo

Reputation: 1723

Firestore update multiple properties in object field of a document

So based on the firestore documentation on object fields:

// Create an initial document to update.
var frankDocRef = db.collection("users").doc("frank");
frankDocRef.set({
    name: "Frank",
    favorites: { food: "Pizza", color: "Blue", subject: "recess" },
    age: 12
});

// To update age and favorite color:
db.collection("users").doc("frank").update({
    "age": 13,
    "favorites.color": "Red"
})
.then(function() {
    console.log("Document successfully updated!");
});

How can I update multiple properties of the favorites field eg.

db.collection("users").doc("frank").update({
    "age": 13,
    "favorites.color": "Red",
    "favorites.food": "Burger"
})

This FAILS with a msg in the console:

Unhandled error Error: Argument "dataOrField" is not a valid UpdateMap. Field "favorites" was specified multiple times.

I have also tried with the merge: true option eg.

db.collection("users").doc("frank").update({
    "age": 13,
    "favorites": {
        "color": "Red",
        "food": "Burger"
    }
}, {merge: true})

But this overwrites the entire favorites field, meaning 'subject' property is gone.

Any ideas?

Here is my actual code:

"cardId" comes from body.cardId on a POST request

  return db.runTransaction(t => {
    let user
    let hasDefault = false
    return t.get(userRef).then(userDoc => {
      if (!userDoc.exists) throw new HttpsError('not-found', 'user-not-found')

      user = userDoc.data()
      if (!user.cards || !user.cards[cardId]) throw new HttpsError('not-found', 'card-not-found')

      return updateCustomer(user.cid, { defaultCard: cardId })
    }).then(customer => {
      const prevDefaultId = Object.keys(user.cards).find(cardId => user.cards[cardId] && user.cards[cardId].default)

      let userUpdateData = {}
      if (prevDefaultId && prevDefaultId !== cardId) {
        userUpdateData[`cards.${prevDefaultId}.default`] = false
      }

      userUpdateData[`cards.${cardId}.default`] = true

      return t.update(userRef, userUpdateData, {merge: true})
    })
  })

I have worked around it by updating twice:

  return db.runTransaction(t => {
    let user
    let hasDefault = false
    return t.get(userRef).then(userDoc => {
      if (!userDoc.exists) throw new HttpsError('not-found', 'user-not-found')

      user = userDoc.data()
      if (!user.cards || !user.cards[cardId]) throw new HttpsError('not-found', 'card-not-found')

      return updateCustomer(user.cid, { defaultCard: cardId })
    }).then(customer => {
      const prevDefaultId = Object.keys(user.cards).find(cardId => user.cards[cardId] && user.cards[cardId].default)

      if (prevDefaultId && prevDefaultId !== cardId) {
        t.update(userRef, {
          [`cards.${prevDefaultId}.default`]: false
        }, {merge: true})
      }

      return t.update(userRef, {
        [`cards.${cardId}.default`]: true
      }, {merge: true})
    })
  })

Upvotes: 3

Views: 2927

Answers (1)

Code on the Rocks
Code on the Rocks

Reputation: 17854

In order to update multiple fields in a Firestore document you need to use the field, value, field, value... format:

db.collection("users").doc("frank").update({
    "age", 13, // field, value
    "color, "red" // field, value
})

If you want to update a nested field, you have a couple options.

  1. Use the dot notation like you mentioned:

    db.collection("users").doc("frank").update({
      "age", 13,
      "favorites.color", "Red",
      "favorites.food", "Burger"
     })
    
  2. Make your "value" a map:

    db.collection("users").doc("frank").update({
     "age", 13,
     "favorites", mapOf(
         "color" to "Red",
         "food" to "Burger"
    })
    

Upvotes: 3

Related Questions