92

following the official documentation of firestore :

{
    name: "Frank",
    favorites: { food: "Pizza", color: "Blue", subject: "recess" },
    age: 12
}

// To update favorite color:
db.collection("users").doc("frank").update({
    "favorites.color": "Red"
})

I would like to use a dynamic key instead of color.

db.collection("users").doc("frank").update({
    "favorites[" + KEY + "].color": true
});

this is of course not possible and will throw an error.

I've been trying to do this :

db.collection("users").doc("frank").update({
    favorites: {
        [key]: {
            color": true
        }
    }
});

It is actually updating with the right key but unfortunately, it is overwriting the other keys (they are being deleted).

J. Doe
  • 12,159
  • 9
  • 60
  • 114
Brieuc
  • 3,994
  • 9
  • 37
  • 70

7 Answers7

112

I found the solution inspired by a firebase solution (replacing "/" by ".").

var usersUpdate = {};
usersUpdate[`favorites.${key}.color`] = true;

db.collection("users").doc("frank").update(usersUpdate);
Brieuc
  • 3,994
  • 9
  • 37
  • 70
  • 9
    Related doc -> https://firebase.google.com/docs/firestore/manage-data/add-data?authuser=0#update_fields_in_nested_objects – Kushagra Gour Jan 05 '18 at 06:04
  • 2
    I'm wondering if that actually works. I would expect it to overwrite the current value of `usersUpdate` with the new one with a single value, namely `{favorites {KEY: {color: true}}}`.If what you say is correct, then there would be no way to actually update `usersUpdate` to a completely new value. –  Apr 23 '18 at 03:31
  • 4
    more briefly, @Georg Hackenberg points out this solution: `db.collection('users').doc('frank').update({ [\`favorites.${key}.color\`] = true })` – Nth.gol Jun 17 '18 at 14:30
  • 1
    Why is it invalid? You can use backticks in es6. This solution still work, it is really similar to the answer below. https://developers.google.com/web/updates/2015/01/ES6-Template-Strings – Brieuc Sep 08 '18 at 09:27
  • 3
    This solution is wrong as this will create a new field usersUpdate of type map with child favorites.${key}.color : true - if key=1234 then usersUpdate field will look like ```usersUpdate { favorites.1234.color : true } ``` – sachin rathod Jan 29 '20 at 18:54
  • I don't understand why you're saying it's wrong. The is the intended purpose is to have a key such as "favorites.1234.color", at least it was the way to do it at that time to achieve something similar than Firebase. – Brieuc Jan 30 '20 at 08:19
  • 1
    For me it worked. What is weird is that it highlight the entire map even I'm updating only one field of only one value of map – Leonardo Rick Sep 04 '20 at 21:19
73

This solution worked for me:

db.collection('users').doc('frank').update({
  [`favorites.${key}.color`]: true
});
  • 3
    Correct solution. – Fernando Rojo Aug 25 '19 at 17:37
  • 1
    db.collection('users').doc('frank').update({ [` favorites.${key}.color `]: true }) – coolcool1994 Dec 31 '19 at 09:16
  • 3
    This solution is correct. Also alternate solution : ```db.collection('users').doc('frank').update({ [`favorites.${key}.color `]: true })``` and also be careful enough to avoid blank spaces inside [``] section i.e you would end up doing wrong if you include blank space ```db.collection('users').doc('frank').update({ [` favorites.${key}.color `]: true }``` – sachin rathod Jan 29 '20 at 18:46
  • doesn't make any nested stuff though :/ just update – Zack Heisenberg Jun 09 '20 at 11:18
  • 10
    Note for lurkers: This works for update, but for set it saves as a single prop with dot in it's name, not as a nested property. – Sebastijan Dumančić Sep 14 '20 at 12:58
32

Just a note about a potential pitfall: After discovering that you can update nested fields using a dot syntax, I tried using set() the same way, since I needed it to work whether the object already exists or not:

var updateObj = {['some.nested.property']: 9000};
docRef.set(updateOb, {merge: true});

Unfortunately, this doesn't work – it sets a property whose key is some.nested.property instead. Inconsistent, but okay.

Fortunately, it appears that set(updateObj, {merge: true}) does a deep merge, so if you construct your update object as a fully nested object, your nested objects will also be properly merged:

// create the object
db.doc('testCollection/doc').set({p1: {['p2']: {p3: true}}}, {merge: true})
// update the existing object
db.doc('testCollection/doc').set({p1: {['p2']: {p4: true}}}, {merge: true})

// result is now this:
{ p1: { p2: { p4: true, p3: true } } }
undefined
  • 6,208
  • 3
  • 49
  • 59
3

You can update specific fields of variable(ly) named nested objects like below.

ref.set({
    name: "Frank",
    favorites: { food: "Pizza", quantity: 2 }
});

//now the relevant update code
var name = "favorites";
var qty = 111;
var update = {};
update[name+".quantity"] = qty;
ref.update(update);

https://jsbin.com/hihifedizu/edit?js,console

Ronnie Royston
  • 16,778
  • 6
  • 77
  • 91
0

I felt this is the simplest solutions of all the above :)

db.collection("users").doc("frank").update({
    [`favorites.${KEY}.color`]: true
});
bad_coder
  • 11,289
  • 20
  • 44
  • 72
deepak n
  • 71
  • 2
  • 4
0

you can use

var auxKey = "history." + someVar;
var dataToUpdate = {
   [auxKey]: "new data generated"
};
db.collection("users").doc("frank").update(dataToUpdate);
Fenkan
  • 1
  • 1
0

2022 Update with Webversion 9 (modular):

const docRef = doc(db, 'users', frank); 

var usersUpdate = {};
usersUpdate[`favorites.${key}.color`] = true;

updateDoc(docRef, userUpdate);
Stefan
  • 123
  • 1
  • 10