简体   繁体   中英

How to update array inside object, inside array mongoDB

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
      ]
]

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.

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.

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