mongoose - remove element in array using $pull - javascript

I have a node object that looks like this:
{
"_id":"58b336e105ac8eec70aef159",
"name":"my node",
"ip":"192.168.1.1",
"__v":0,
"configuration":{
"links":[
{
"linkName":"athena_fw_listen_tcp_5555",
"_id":"58b336e105ac8eec70aef15d",
"local":true
},
{
"linkName":"athena_fw_listen_udp_5554",
"_id":"58b336e105ac8eec70aef15c",
"local":true
}
]
}
}
I am sending a delete request to my express server that looks like this:
DELETE http://localhost:9000/api/nodes/58b336e105ac8eec70aef159/links/58b336e105ac8eec70aef15d
I followed instructions in $pull mongodb documentation and I also tried this
But it does not seem to work, as I keep receiving: 500 (Internal Server Error)
This is how the code on my express side looks like:
exports.destroyLink = function(req, res) {
Node.findById(req.params.id, function(err, node) {
if (err) { return handleError(res, err); }
if (!node) { return res.status(404).send('Not Found'); }
console.log("Node", JSON.stringify(node))
console.log("Params", req.params)
node
.update({ '_id': req.params.id }, { $pull: { 'configuration': { 'links': { '_id': req.params.linkId } } } }, false, true)
.then(err => {
if (err) { return handleError(res, err); }
return res.status(204).send('No Content');
});
})
};
The express router contains this:
router.delete('/:id/links/:linkId', controller.destroyLink);
So I am expecting id and linkId as params, I use id (_id: req.params.id) to target a specific node and linkId (_id: req.params.linkId) to target a specific link, but it doesn't work!
Need help resolving the issue, I don't know what I am missing here!

Hi all and thank you for your help. I finally got it to work!!!
After almost 3 hours of troubleshooting :'( :'( this is the solution that used:
exports.destroyLink = function(req, res) {
Node.findByIdAndUpdate(
req.params.id, { $pull: { "configuration.links": { _id: req.params.linkId } } }, { safe: true, upsert: true },
function(err, node) {
if (err) { return handleError(res, err); }
return res.status(200).json(node.configuration.links);
});
};
Instead of findById then update the node, I did both using findByIdAndUpdate. It' working perfectly now!!!
I still don't find an explanation for the other version though. But anyways glad it's working this way.

I had a similar issue getting a query like yours working. I think your query is not finding the id you're looking for. If you want to find an object in mongo by '_id' it needs to be passed in as an ObjectId. Try modifying things so it looks like this:
const mongo = require('mongodb');
var oId = new mongo.ObjectID(req.params.id);
update({ '_id': oId }, { $pull: { 'configuration': { 'links': { '_id': req.params.linkId } } } }, false, true)

I never got this working using the mongodb $pull operator. An alternative is to find the document then update the array using the mongoose pull method. The pull method can take an id string as its single argument and knows how to handle it. For this example using promises the code would look something like:
exports.destroyLink = function(req, res) {
Node.findById(req.params.id)
.then(node => {
node.configuration.links.pull(req.params.linkId)
return node.save()
.then(node => res.send(node.configuration.links))
}
The mongoose docs for pull

I got the .update mongoose function to work by changing to this:
{ $pull: { 'configuration': { 'links': { '_id': ''+req.params.linkId+'' } } } }
Once you concatenate that, it'll pull it from the array.

For my problem, this worked
await FormSchema.updateOne({formName: name}, {$pull: {fields: {fieldName: fieldName}}}).exec();
and here is what my schema looks like
{
"fields": [
{
"contentType": "Text",
"fieldName": "First Name",
"textType": "short",
"isUnique": false
},
{
"contentType": "Boolean",
"fieldName": "Contact Me"
}
],
"_id": "61f71efd14cafb5a50ba365f",
"formName": "Contact",
"__v": 4
}

Related

Mongo findById() only works sometimes even when passed a valid ID

I am having a strange issue querying a Mongo DB collection. I am using findById() to get a single item that works sometimes and not others.
I have checked the id being passed to the server route and in all cases, they match perfectly with the targeted document in the collection.
Here is the basic code:
router.get("/:postId", async (req, res) => {
console.log('id : ', req.params.postId)
console.log('type: ', typeof(req.params.postId)) // id is a string
try {
const post = await Post.findById(req.params.postId).exec();
console.log('post :', post) // sometimes null
res.json(post);
} catch (err) {
res.json({ message: err });
}
});
In the above route, only certain posts will be found while others come back null. This happens regardless of whether the id passed is correct and the document exists with the exact id.
If anyone has any ideas about what could be going wrong here I'd much appreciate the help!
EDIT
I have done some more debugging and think it is something to do with the Schema for the Post model.
For example, this object will be found:
{
"tags": ["foo"],
"_id": "8394839483fhg020834903",
"title": "bar",
"content": "baz",
"isPrivate": true,
}
But this one will not because of the missing isPrivate property.
{
"tags": [],
"_id": "5e0fdc631ef5c46b285a4734",
"title": "New post",
"content": "Some content here",
}
I have tested this across multiple queries and it appears to the root of the problem.
I have tried adding
isPrivate: {
required: false
}
To the Schema but it doesn't seem to solve the issue.
Here is the full Schema
const postSchema = mongoose.Schema({
title: {
type: String,
required: true
},
content: {
type: String,
required: true
},
tags: [{ type: String }],
date: {
type: Date,
default: Date.now
},
isPrivate: {
type: Boolean
required: false
}
});
I'm not a Mongo/Mongoose expert, so any guidance would be much appreciated.
If post id match with any record it return data, otherwise it will return null. You should handle the exception
router.get("/:postId", async (req, res) => {
try {
const post = await Post.findById(req.params.postId).exec();
if(post) {
return res.json(post);
}
res.json({ message:'No Post found' });
} catch (err) {
res.json({ message: err });
}
});
You can manually check is record exists against a post id. You can use MongoDB Compass for gui browse the record
I believe the issue might be with your _id as per mongo standard _id should be a String is of 12 bytes or a string of 24 hex characters.
We can check if the _id is valid using mongoose.isValidObjectId()
I did run this check on your objects that you posted and indeed 1 is invalid while other is valid
const mongoose = require('mongoose');
console.log(`is '8394839483fhg020834903' valid - ${mongoose.isValidObjectId('8394839483fhg020834903')}`);
console.log(`is '5e0fdc631ef5c46b285a4734' valid - ${mongoose.isValidObjectId('5e0fdc631ef5c46b285a4734')}`);
It gives me
You will have to check what is modifying your ID's in the code, you can upload your schema to get a better understanding as well.

Unable to remove item from array using Mongoose / UpdateOne

I have a Node.js application in which I am trying to remove an object from an array when an API endpoint is hit. I so far have been unable to get it to update/remove the object. Currently, the below query returns with no error but upon checking into my DB I am still seeing it. Below is my query and basic response (I will be adding more but that is outside the scope of this question). I have also included a sample of my data model.
In the below data model I am trying to remove the whole object from the foo array as it is no longer needed.
Code
const ID = req.params.id
await FooBar.updateOne({foo: {$elemMatch: {v_code: ID}}}, { $pull: {v_code: ID}}, (err) => {
if(err) return res.json({success: false, err})
return res.json({success: true, id: ID})
})
Data model
{
bar: [
{
foo: [
{
v_code: <>
_id: <>
}
]
}
]
}
I'm sure this has been asked for in other questions but none specific to my data model. I've tried piecing together multiple SO posts and that is how I got the $elemmatch and the $pull portions of my query and so far I've had zero luck
give the following command a try:
db.collection.updateOne(
{
"bar.foo.v_code": ID
},
{
$pull: { bar: { foo: { $elemMatch: { v_code: ID } } } }
}
)
https://mongoplayground.net/p/iqJki-mnHSJ

How to write find and update query in express.js?

I am using REST API(express.js and mongodb) and trying to update my document but it's not working. I don't know what is the error but can someone help me out to move forward? I have added my route and controller
Routes:
app.route('/articleupdation')
.post(article.updatearticle);
Controller:
exports.updatearticle = function(req, res) {
Article.findOne({
Username: 'xx',
Email: 'xx#gmail.com',
Info: 'Deactivate',
}, function(err, article) {
if (!err && article) {
article.Info = 'Active';
article.save(function(err) {
if (err) {
console.log('not working');
} else {
console.log('working');
}
});
} else {
console.log('Condtion not matched ');
console.log(err);
}
});
};
Data stored like this
{
"_id": {
"$oid": "5799995943d643600fabd6b7"
},
"Username": "xx",
"Email": "xx#gmail.com",
"Info": "Deactivate",
"Description": "aajdjdjddjdkjddjdjdhdj",
}
Here is what I am trying to achieve; if Username, Email, Info are matched I need to update article.Info = 'Active'; but this is not working, can someone help me out please?
From the looks of it, your query is not matching any documents in the collection hence the statement branch which does the update is not being reached, just the else statement as the returned article is null. You can test this by running the raw query in mongo shell on the underlying collection i.e.
db.articles.findOne({
"Username": "xx",
"Email": "xx#gmail.com",
"Info": "Deactivate"
})
and see if that returns any matching document. If not, check the Info field from the document returned in this query
db.articles.findOne({
"Username": "xx",
"Email": "xx#gmail.com"
})
The best way to go about this within an atomic update that does not require two requests to the server (i.e. one to query the document and the other to write the changes to the server) is to use the findOneAndUpdate api. This will issue a mongodb findAndModify update command which modifies and returns a single document. By default, the returned document does not include the modifications made on the update. To return the document with the modifications made on the update, use the new option.
Thus your refactored code could follow this pattern:
exports.updatearticle = function(req, res) {
Article.findOneAndUpdate(
{ "Username": req.body.username, "Email": req.body.email, "Info": "Deactivate" },
{ "$set": { "Info": "Active" } },
{ "new": true },
function (err, doc) {
if (err) { // err: any errors that occurred
console.log(err);
} else { // doc: the document before updates are applied if `new: false`
console.log(doc); // , the document returned after updates if `new true`
console.log(doc.Info);
}
}
);
};

Mongoose update previously fetched collection

I would like to update a collection. Docs seem unclear on this.
I am wondering how to achieve the following:
Order.find({ _id: { $in: ids }}).exec(function(err, items, count) {
// Following gives error - same with save()
items.update({ status: 'processed'}, function(err, docs) {
});
});
I know how to batch save like this:
Model.update({ _id: id }, { $set: { size: 'large' }}, { multi: true }, callback);
But that requires setting my query again.
I've also tried:
Order.collection.update(items...
But that throws a max call stack error.
In mongoose, model.find(callback), return an Array of Document via callback. You can call save on a Document but not on an Array. So you can use for loop or forEach on the Array.
Order
.find({ _id: { $in: ids}})
.exec(function(err, items, count) {
items.forEach(function (it) {
it.save(function () {
console.log('you have saved ', it)
});
})
});

MongoError,err:E11000 duplicate key error

I have a MongoDb schema like this
var User = new Schema({
"UserName": { type: String, required: true },
"Email": { type: String, required: true, unique: true },
"UserType": { type: String },
"Password": { type: String }
});
I am trying to create a new user
This is done in NodeJs using mongoose ODM
And this is the code for creating:
controller.createUser = function (req, res) {
var user = new models.User({
"UserName": req.body.UserName.toLowerCase(),
"Email": req.body.Email.toLowerCase(),
"UserType": req.body.UserType.toLowerCase()
});
models.User.findOne({ 'Email': user.Email }, function (err, olduser) {
if (!err) {
if (olduser) {
res.send({ 'statusCode': 409, 'statusText': 'Email Already Exists' });
}
else if (!olduser) {
user.setPassword(req.body.Password);
user.save(function (err, done) {
if (!err) {
console.log(user);
res.send({ 'statusCode': 201, 'statusText': 'CREATED' });
}
else {
res.send({ 'Status code': 500, 'statusText': 'Internal Server Error' });
}
});
}
}
else {
res.send({ 'statusCode': 500, 'statusText': 'ERROR' });
}
});
};
The for creating new user,I am giving attributes and values as follows:
{
"UserName": "ann",
"Email": "ann#ann.com",
"UserType": "normaluser",
"Password":"123456"
}
And I am getting error like this:
{"Status code":500,"statusText":"Internal Server Error","Error":{"name":"MongoError","err":"E11000 duplicate key error index: medinfo.users.$UserName_1 dup key: { : \"ann\" }","code":11000,"n":0,"connectionId":54,"ok":1}}
I understand that this error is because UserName is duplicated ,but I haven't set UserName with unique constraint.Whenever I add a new row,I need only email to be unique,UserName can be repeated.How to achieve this??
#ManseUK Is probably right, that looks like UserName is a 'key' - in this case an index. The _id attribute is the "primary" index that is created by default, but mongodb allows you to have multiple of these.
Start a mongo console and run medinfo.users.getIndexes()? Something must have added an index on 'UserName'.
required: true wouldn't do that, but you might have played with other settings previously and the index hasn't been removed?
There should be an index that is blocking.
You can try the db.collection.dropIndex() method
medinfo.users.dropIndexes()
I got the similar issue on my project. I tried to clear out all the documents and the dup issue still keep popping up. Until I dropped this collection and re-start my node service, it just worked.
What I had realized is that my data-structures were changing -- this is where versioning comes in handy.
You may need to get a mongoose-version module, do a thing.remove({}, ...) or even drop the collection: drop database with mongoose
I use RoboMongo for an admin tool (and I highly recommend it!) so I just went in and right-clicked/dropped collection from the console.
If anyone knows how to easily version and/or drop a collection from within the code, feel free to post a comment below as it surely helps this thread ( and I :) ).

Categories