30
votes

I have a data structure like:

enter image description here

I want to edit the value of "test" key in "first" object. I followed the document on https://firebase.google.com/docs/firestore/manage-data/add-data

But it did not work for me.

The nodejs code:

var setAda = dbFirestore.collection('users').doc('alovelace').update({
        first : {
            test: "12345"
            }
});

The result in firestore: enter image description here

The "test2" key was gone. However, I only want to update the value of "test" and keep the "test2".

Any solution for this problem?

9

9 Answers

44
votes

According to the link you provided, it says this:

If your document contains nested objects, you can use "dot notation" to reference nested fields within the document when you call update():

Therefore you need to use dot notation to be able to update only one field without overwriting, so like this:

var setAda = dbFirestore.collection('users').doc('alovelace').update({
    "first.test": "12345"
});

then you will have:

 first
  test: "12345"
  test2: "abcd"
24
votes

Peter's solution's great, but it's not works with dynamic key. This code is far better:

var nestedkey = 'test';
var setAda = dbFirestore.collection('users').doc('alovelace').update({
    [`first.${nestedkey}`]: "12345"
});
7
votes

In case somebody is using TypeScript (like in Cloud functions for example) here is the code to update nested fields with dot notation.

var setAda = dbFirestore.collection('users').doc('alovelace').update({
    `first.${variableIfNedded}.test`: "12345"
});
7
votes

If you don't want an exception that occur if 'first' field doesn't exist, try using set with {merge: true} option instead of update.

var setAda = dbFirestore.collection('users').doc('alovelace').set({
        first : {
            test: "12345"
        }
}, {merge: true});
1
votes

Try this one: Does it work like that?

var setAda = dbFirestore.collection('users').doc('alovelace').update({
        "first.test" : "12345"
});
1
votes

For those who need something more generic and recursive, here is a function that updates a Foo Firestore document non destructively with a typescript Partial :

private objectToDotNotation(obj: Partial<Foo>, parent = [], keyValue = {}) {
    for (let key in obj) {
        let keyPath = [...parent, key];
        if (obj[key]!== null && typeof obj[key] === 'object')
            Object.assign(keyValue, this.objectToDotNotation(obj[key], keyPath, keyValue));
        else
            keyValue[keyPath.join('.')] = obj[key];
    }
    return keyValue;
}

public update(foo: Partial<Foo>) {
    dbFirestore.collection('foos').doc('fooId').update(
        this.objectToDotNotation(foo)
    )
}
0
votes

For Swift 4

let dictionary:[String:Any] = ["first.test" : "12345"]
let plansDocumentReference = Firestore.firestore().collection("users").document("alovelace")              
plansDocumentReference.updateData(dictionary) { err in
                if let err = err {
                    print("Error updating document: \(err)")

                }}

You can update multiple fields by adding records to the dictionary as well as build variable based sub-keys by replacing the fixed text in the dictionary with string variable.

This technique also works to replace entire single key sub-blocks in the document by using a dictionary as the value for any key

0
votes

I was looking for a version in typescript without the extra params, having type completion in mind... Ended up with the following:

TS Playground example

function objectToDotNotation(obj: any): any {

    return Object.keys(obj).reduce( (dnObj, key) => {
        const value = obj[key];

        if (value !== null && typeof value === 'object') {            
            const childObj = objectToDotNotation(value);

            Object.keys(childObj).forEach( childKey => {
                dnObj = {...dnObj, [`${key}.${childKey}`]: childObj[childKey] };
            });
        } else {            
            dnObj = {...dnObj, [key]: value };
        }

        return dnObj;
    }, {});
}
0
votes

Use the set function and passing merge: true in options to update deeply nested fields without overwriting other fields.

const firstore = firebase.firestore()
const ref = firestore.doc('users/alovelace').set({
  first : {
    test: "12345"
  }
}, { merge: true })

With this method, you can update a field even if it's buried 20 levels deep.