0

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.

appel
  • 517
  • 2
  • 7
  • 19
  • When you say `turning it into an array` do you mean `tracks:[]` ? Because that should work as your update statement seems correct. – cillierscharl Jul 03 '14 at 10:54
  • tracks is and object in the database, not array: "tracks": {}, – Ben Jul 03 '14 at 11:09
  • Yeah, if I do $push: { tracks: [trackId] } then it still doesn't add it to the array. @Ben: I'm not sure what you mean, the call I do to save new documents is as follows: db.db_name.save({id: partyId, tracks: {}}, function(err, saved) {...}); – appel Jul 03 '14 at 11:18
  • should be db.db_name.save({id: partyId, tracks: []}, function(err, saved) {...}); Note that tracks equals square brackets [] instead of curly brackets {}. Or if you don't have anything for tracks initially, you can just omit tracks when saving it db.db_name.save({id: partyId}, function(err, saved) {...});, then when you push, it'll create the tracks array – Ben Jul 03 '14 at 12:34
  • 1
    Just noticed that the push in your code is an array { $push: { tracks: [trackId] } }, should be element { $push: { tracks: trackId } } – Ben Jul 03 '14 at 12:40

3 Answers3

1

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.

SymbolixAU
  • 25,502
  • 4
  • 67
  • 139
appel
  • 517
  • 2
  • 7
  • 19
0

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

SudoGetBeer
  • 128
  • 1
  • 6
  • This is what I thought too so I tried it using: db.db_name.save({id: partyId, tracks: []}, function(err, saved) {...}); as initial document creation. I updated the code above, but it still doesn't add the value to the tacks array. – appel Jul 03 '14 at 12:04
  • Ah got it, thanks to your first sentence I went to check if it actually found any docs. Thanks :) – appel Jul 03 '14 at 12:07
  • Not tested it, but from the documentation ( http://docs.mongodb.org/manual/reference/operator/update/push/ ) $push doesn't want an array. So change { $push: { tracks: [trackId] } } to { $push: { tracks: trackId } } EDIT: Or it doesnt find any Documents ;) – SudoGetBeer Jul 03 '14 at 12:08
0

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
 }
agm1984
  • 15,500
  • 6
  • 89
  • 113