skip to Main Content

So the document contains an array of objects, each object containing it’s own array. So how would I go about updating one of the elements in the array that’s inside the object which is inside another array. I’ve read some things with $. But I don’t understand completely how to use it to call a position. I know the position of the element. But I can’t just say $[] because the position is defined in a variable and not a string…

I’ve tried doing a simple

db.collection.findOne({...}, (err, data) => {...});

and then changing the arrays in the objects in the array in there with a simple:

data.arr[x].type[y] = z; data.save().catch(err => {console.log(err)});

But it doesn’t save the new values I set for for the element of the array.

Sample structure after proposed solution from @Tom Slabbaert:

Data.findOne({
    userID: 'CMA'
}, (err, doc) => {
    if(err) {console.log(err)}
    if(doc) {
        for(var i = 0; i<CMA.stockMarket.length; i++) {
            if(CMA.stockMarket[i].name == data.userID) {
                for(var z = 0; z<CMA.stockMarket[i].userStock.length; z++) {
                    if(z == company) {
                        var updateAmount = CMA.stockMarket[i].userStock[z]+args[1]
                        var updateKey = `stockMarket.${i}.userStock.${z}`
                        Data.updateOne({userID: 'CMA'}, {'$set': {[updateKey]: updateAmount}})
                    }
                }
            }
        }
    }
});

————————-EDIT————————-
So I tried changing some things around in the data base to see if that would fix the problem I was having. I modified the updated code that was provided by @Tom Slabbaert. But nothing seems to work for some reason :/ Here’s what I have so far, at this point I hope it’s just a syntax error somewhere. Cause this is really frustrating at this point. Note that I’m still using the for loops here to find if the info exists. And if not, push that info into the database. This might only be temporary until I find a better way / if there is a better way.

for(var i = 0; i<CMA.userStocks.length; i++) {
    if(CMA.userStocks[i].name == data.userID) {
        for(var z = 0; z<CMA.userStocks[i].shares.length; z++) {
            //console.log(CMA.userStocks[i].shares[z].companyName)
            if(CMA.userStocks[i].shares[z].companyName == args[0]) {
                var updateKey = `CMA.userStocks.$[elem1].shares.$[elem2].amount`

                Data.updateOne(
                    {userID: 'CMA'},
                    {
                        "$inc": {
                            [updateKey]: args[1]
                        }
                    },
                    {
                        arrayFilters: [
                            {
                                "elem1.name": data.userID,
                                "elem2.companyName": args[0]
                            }
                        ]
                    }
                )
                purchaseComplete(); return;
            }
        }
        CMA.userStocks[i].shares.push({companyName: args[0], amount: parseInt(args[1])})
        CMA.save().catch(err => {console.log(err)});
        purchaseComplete(); return;
    }
}
CMA.userStocks.push({name: data.userID, shares: [{companyName: args[0], amount: parseInt(args[1])}]});
CMA.save().catch(err => {console.log(err)});
purchaseComplete(); return;

The data I’m trying to find and change is structured like the following:
And what I’m trying to change in the end is the ‘amount’ (which is an integer)

_id: (Not relavent in this question)
userID: 'CMA'
stockMarket: [...] (Not relavent in this question)
userStocks: [
   Object: (position 0 in userStocks array)
      name: 'string' (equal to data.userID in the code)
      shares: [
          Object: (position 0 in shares array)
              companyName: 'string' (this is args[0] in the code)
              amount: integer
      ]
]

2

Answers


  1. Chosen as BEST ANSWER

    Well I found something that worked. Apparently it didn't save the db.collection.updateMany unless I made a .then() function on the end? I have no idea why, but it's the same with an aggregate I made. (It basically does the same as a Data.findOne and save it too, but it isn't limited by the parallel save error)
    Solution I found with aggregation:

    <collection field> = <new data for collection field>
    Data.aggregate([
        {
            $match: { //This is used to create a filter
                ['<insert field>']: <insert filter>
            }
        }, {
            $addFields: { //This is used to update existing data, or create a new field containing the data if the field isn't found
                ['<collection field>']: <new data for collection field>
            }
        }, {
            $merge: { //This is used to merge the new data / document with the rest of the collection. Thus having the same effect as a standard save
                into: {
                    db: '<insert database name>',
                    coll: '<insert collection name>'
                }
            }
        }
    ]).then(() => {
        //After it's done, do something here. Or do nothing at all it doesn't matter as long as the .then() statement remains. I found that not having this part will break the code and make it not save / work for some reason.
    }); return;
    

    Solution I found with db.collection.updateMany

    db.collection.updateMany(
        {<insert field>: filter}, {$set: {'<insert field>': <new data>}}
    ).then(() => {
        //This .then() statment in my case was needed for the updateMany function to work correctly. It wouldn't save data without it for some reason, it does not need to contain any actual info in this part. As long as it's here.
    });
    

    With this new info I could simply access and change the data that I was trying to before using the previous instructions provided by @Tom Slabbaert and my new method of actually making it save the changes made into the document.


  2. You can just prepare the "key" ahead of time. like so:

    const updateKey = `arr.${x}.type.${y}`
    db.collection.updateOne(
    {...},
    {
      "$set": {
        [updateKey]: z
      }
    })
    

    Mongo Playground

    Using Mongo’s positional operators ($ and $[]) are usually required when you don’t know the position in the array and want to use a condition to update the element.

    —— EDIT—–

    After given your sample code you just have a minor syntax error:

    var updateKey = `stockMarket.${i}.userStock.${z}`
    

    Should just be:

    var updateKey = `CMA.stockMarket.${i}.userStock.${z}`
    

    However After seeing your code I recommend you execute the following solution which uses a single update with arrayFilters, it just cleans up the code quite a bit:

    const updateKey = `CMA.stockMarket.$[elem1].userStock.${company}`;
    db.collection.update(
    {userID: 'CMA'},
    {
      "$inc": {
        [updateKey]: args[1]
      }
    },
    {
      arrayFilters: [
        {
          "elem1.name": data.userID
        }
      ]
    })
    

    Mongo Playground

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search