Reputation: 537
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
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
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
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