appel
appel

Reputation: 537

Pushing new value to array in MongoDB document with NodeJS

I have a MongoDB collection with documents that look as follows:

{
    "id": 51584,
    "tracks": [],
    "_id": {
        "$oid": "ab5a7... some id ...cc81da0"
    }
}

I want to push a single track into the array, so I try the following NodeJS code:

function addTrack(post,callback){
    var partyId = post['partyId'], trackId = post['trackId'];
    // I checked here that partyId and trackId are valid vars.
    db.db_name.update({id: partyId}, { $push: { tracks: [trackId] } }, function(err, added) {
      if( err || !added ) {
        console.log("Track not added.");
        callback(null,added);
      }
      else {
        console.log("Track added to party with id: "+partyId);
        callback(null,added);
        }
    });
}

This returns successfully with the callback that the track was added. However, when I inspect the database manually it is not updated and the array tracks is still empty. I've tried a lot of different things for the tracks element to be pushed (ie. turning it into an array etc.) but no luck so far.

PS: Perhaps I should note that I'm using MongoLab to host the database.

Any help would be most welcome.

Upvotes: 0

Views: 6847

Answers (3)

appel
appel

Reputation: 537

I found my problem, in the addTrack update({id: partyId},.. method partyId was not a string so it didn't find any docs to push to. Thanks to SudoGetBeer for leading me to the solution.

Upvotes: 1

agm1984
agm1984

Reputation: 17132

Here's how I'm doing it:

 // This code occurs inside an async function called editArticle()
 const addedTags = ['one', 'two', 'etc']

 // ADD NEW TAGS IN MONGO DB
 try {
     const updateTags = addedTags.reduce((all, tag) => {
         all.push(articles.updateOne({ slug: article.slug }, { $push: { tags: tag } }))
         return all
     }, [])
     await Promise.all(updateTags)
 } catch (e) {
     log('error', 'addTags', e)
     throw 'addTags'
 }

addTags is the Array of tags, and we need to push them into Mongo DB one at a time so that the document we are pushing into looks like this after:

 {
     tags: ["existingTag1", "existingTag2", "one", "two", "etc"]
 }

If you push an array like in the original question above, it would look like this:

 {
     tags: ["existingTag1", "existingTag2", ["one", "two", "etc"]]
 }

So, tags[2] would be ["one", "two", "etc"], not what you want.

I have shown .reduce() which is an accumulator, which is a fancy, immutable way of doing this:

 let updateTags = []
 addedTags.forEach((tag) => {
     updateTags.push(articles.updateOne({ slug: article.slug }, { $push: { tags: tag } }))
 })

At this point, updateTags contains an array of functions, so calling Promise.all(updateTags) will run them all and detonate if any of them fail. Since we are using Mongo DB Native Driver, you will have to clean up if any errors occur, so you will probably want to track the pre-write state before calling Promise.all() (ie: What were the tags before?)

In the catch block, or catch block of the upper scope, you can "fire restore previous state" logic (rollback) and/or retry.

Something like:

 // Upper scope
 catch (e) {
     if (e === 'addTags') rollback(previousState)
     throw 'Problem occurred adding tags, please restart your computer.'
     // This can now bubble up to your front-end client
 }

Upvotes: 0

SudoGetBeer
SudoGetBeer

Reputation: 128

If your posted document is correct(get via find() i.e.):

tracks is a subdocument or embedded document ( http://docs.mongodb.org/manual/tutorial/query-documents/#embedded-documents )

The difference is simple: {} = Document, [] = Array

So if you want to use $push you need to update the tracks field to be an array

Upvotes: 0

Related Questions