MongoDB Upsert add to array - node.js

I'm trying to insert/update an array of strings in a mongodb document using some typescript code running in NodeJS.
The following code is typescript but I guess JS developers will get it w/o any problems:
export function addEvents(entityId: string,
events: string[] ,
callback: () => void) {
db.collection('events', function(error, eventCollection) {
if(error) {
console.error(error); return;
}
eventCollection.update({ _id: entityId }, { "$pushAll ":
{ events: events }},
function(error, result) {
if(error) {
console.error(error); return;
}
callback();
});
});
}
the document have the following structure:
{
_id : string
events : ["array","of","strings"]
}
I simply want to append an array strings at the end of the existing array for a specific _id.
I don't quite get if I should use update,save, $push ,$pushall etc.
Can someone explain?

If I understood correctly the problem is that pushAll does nothing or update returns error? Maybe copy-paste mistake in your example but I think you have typo here.
{ "$pushAll ": { events: events }}
It should be
{ $pushAll: { events: events }}

Your combination of update and $pushAll looks like the best choice for what you're doing here -- it's for appending an array to an existing array. $push is for adding an element to an array. save would involve getting the existing events array, appending to it, then saving the document.
The extra space in "$pushAll " needs to be removed. It may have quotes: "$pushAll".

Found the problem, I needed to pass "{ upsert = true }" as a third argument to the update function.

To achieve 'upsert' semantics in this case, you'd need to use $addToSet. If you have an array of values to add, you'd need to throw in the $each modifier. From mongo shell:
db.events.update(
{ _id: entityId },
{ $addToSet: { $each: events } }
)

Related

I want to insert documents using upsert in mongoose

I am fetching data from an external api and I want to insert multiple records if they are not already there . I am creating an array of objects like this :
data=[
{
match_id: 212167781,
player1Id: 129753806,
player2Id: 129753811,
player1Details: '5f070c8f79f62c00042b0cd8',
player2Details: '5f3267fe6a0f891b3604b999'
},
{
match_id: 212167782,
player1Id: 129753799,
player2Id: null,
player1Details: '5f070ade79f62c00042b0cd6'
}
]
Now for instance if the second record is not already present then I want it to be inserted and I have tried upsert but nothing happens . Please help .
You should be able to do this using a simple loop and Model.findOneAndUpdate in combination with the upsert option:
async function upsertMany(data) {
for(const entry of data) {
await YourModel.findOneAndUpdate(entry, entry, { upsert: true });
}
}
// call this with your data-array
await upsertMany(data);

Push Array Items to Array type Column in mongoDb

This is a Controller in which I'm trying to catch multiple candidates id(ObjectId) and try to store it in the database in the array Candidates. But data is not getting pushed in Candidates column of Array type.
routes.post('/Job/:id',checkAuthenticated,function(req,res){
var candidates=req.body.candidate;
console.log(candidates);
Job.update({_id:req.params.id},{$push:{Appliedby : req.user.username}},{$push:{Candidates:{$each:
candidates}}}
});
Console screens output
[ '5eb257119f2b2f0b4883558b', '5eb2ae1cff3ae7106019ad7e' ] //candidates
you have to do all the update operations ($set, $push, $pull, ...) in one object, and this object should be the second argument passed to the update method after the filter object
{$push:{Appliedby : req.user.username}},{$push:{Candidates:{$each: candidates}}
this will update the Appliedby array only, as the third object in update is reserved for the options (like upsert, new, ....)
you have to do something like that
{ $push: { Appliedby: req.user.username, Candidates: { $each: candidates } } }
then the whole query should be something like that
routes.post('/Job/:id', checkAuthenticated, function (req, res) {
var candidates = req.body.candidate;
console.log(candidates);
Job.update(
{ _id: req.params.id }, // filter part
{ $push: { Appliedby: req.user.username, Candidates: { $each: candidates } } } // update part in one object
)
});
this could do the trick I guess, hope it helps

I want to update sub document in array

I now use mongooses to pull and pull subdocuments to the array, and now I want to change the contents of the detail field of that subdocument with the _id of the subdocument.
{
subDocument: [{
_id: ObjectId('123'),
detail: 'I want update this part'
}]
}
I tried to use the $set method as shown below but it did not work as expected.
Model.findByIdAndUpdate(uid, { $Set: {subDocument: {_id: _id}}});
Looking at the for statement as shown below is likely to have a bad effect on performance. So I want to avoid this method.
const data = findById(uid);
for(...) {
if(data.subDocument[i]._id==_id) {
data.subDocument[i].detail = detail
}
}
Can you tell me some mongodb queries that I can implement?
And, Is it not better to use the 'for(;;)' statement shown above than to search using mongodb's query?
This should work:
Model.findOneAndUpdate({"subdocument._id": uid},
{
$set: {
"subdocument.$.detail ": "detail here"
}
},
).exec(function(err, doc) {
//code
});
To find subdocument by id, I am using something like this :
var subDocument = data.subDocument.id(subDocumentId);
if (subDocument) {
// Do some stuff
}
else {
// No subDocument found
}
Hope it helps.

Replace character in MongoDB collection without saving changes.

I have a MongoDB collection of companies and models in following format:
[{company:'Any company', models:['Model1','model2','model3']},
...
{company:'Any-company', models:['model83','Model-abc','MODEL43']}]
As you can see some of company or model names are upper case or written with dash symbol.
I want to save this collection into variable, and I need it to be Lower Case and without dashes.
I did it with just mongoose find and for loop like so:
mongoose.model('Company', companySchema).find({}, function(err, docs) {
if (err) {
console.log(err);
} else {
var companyLowerCase = docs;
for(var i=0;i<companyLowerCase.length;i++) {
companyLowerCase[i].company = companyLowerCase[i].company.replace(/-/g, " ").toLowerCase();
for(var j=0;j<companyLowerCase[i].models.length;j++) {
companyLowerCase[i].models[j] = companyLowerCase[i].models[j].replace(/-/g, " ").toLowerCase();
}
}
}
});
But I'm wondering if it is possible to achieve with MongoDB. I found that you can lower case you values like so:
db.inventory.aggregate([{
$project:
{
item: { $toLower: "$item" },
description: { $toLower: "$description" }
}
}]);
But can you replace "-" on " " with MongoDB? If you can, then what request should I send to the MongoDB to achieve my goal?
NOTE: Collection in database should not change. Modification must be only save in variable.
I don't think you can since MongoDB doesn't have a replace operator for $project

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.
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.
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
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
}

Resources