简体   繁体   中英

how to dynamically update the nested objects in mongodb

We have some arbitrarily nested configuration that we store in the mongodb.

  1. Let say we initially store the following object in the mongodb.

const value = { 'a': { 'b': 1 } }

collection.insertOne({userId, value})

now I want to modify the object in the database by adding

const addValue = { 'f': { 'c': 2 } }

and similarly with few more nested object.

const addValue1 = { 'a': { 'd': 1 }, 'f': { 'g': 1 } };

As we adding, these keys are very dynacmic and should able to update value in the db but not replace it, so expecting the end result stored should be

const result = collection.findOne({ userId });

console.log(result);

{ 'a': { 'b': 1, 'd': 1 }, 'f': { 'g': 1, 'c': 2 } }

And also overwrite if the udpate value is

const addValue2 = { 'a': { 'b' : { 'e': 1 } } }

expected result is

const result2 = { 'a': { 'b': { 'e': 1 } , 'd': 1 }, 'f': { 'g': 1, 'c': 2 } }

Similarly when deleting

    let testObject = new MongoDBStorageService('test', dbConnection as any, 'userTestSchema');

    testObject.load({ 'a': { 'b': 1 }});
    testObject.removeValue('a.b');

    const content = await testObject.contents;

 expect(content).toEqual({}); // but we see output as `{ a: {} }`

Remove method used

public async removeValue(key: string) {
        return await this.remove(JSON.parse(`{\"${key}\": "" }`));
    }
private remove(value) {
    return new Promise((resolve, reject) => {
        this.collection.updateOne({ user: this.userId }, {
            $unset: value,
        }, { upsert: false }, function (err, res) {
            if (err) {
                reject(err);
            } else {
                console.log('REUSLL AFTER REMOVE', res.);
                resolve({ id: true });
            }
        });
    });
}

The difficulty here is that you have to convert your objects into MongoDB's dot notation which can be used to build your update statements. You can do that by running below function:

 let flatten = (obj, prefix, result) => { result = result || {}; for(let key of Object.keys(obj)){ let keyExpr = prefix ? `${prefix}.${key}` : `${key}`; if(typeof obj[key] === "object"){ flatten(obj[key], keyExpr, result); } else { result[keyExpr] = obj[key]; } } return result; } const addValue = { 'f': { 'c': 2 } } let update1 = flatten(addValue) console.log(update1); const addValue1 = { 'a': { 'd': 1 }, 'f': { 'g': 1 } }; let update2 = flatten(addValue1); console.log(update2);

const value = { 'a': { 'b': 1 } }
const userId = 1;
db.col.insertOne({userId, ...value})

db.col.update({ userId: userId }, { $set: update1 });
db.col.update({ userId: userId }, { $set: update2 });

The reason why you can't run $set directly on your objects is that it will replace existing a nested object instead of merging it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM