Pull objects from array in embedded MongoDB document - node.js

I've been unable to figure out a way to to remove objects from arrays in embedded mongoDB documents.
My schema holds a user and a list of flower objects. It looks like this:
const UserSchema = new Schema({
name : {
type : String,
required : true
},
flowers : [
{
name : {
type : String,
required : true
},
water_freq : {
type : Number,
required : true
}
}
]
});
I've managed to add flower objects like so:
router.post('/:id/flowers/add', (req, res) => {
const { name, water_freq } = req.body;
User.findByIdAndUpdate(
req.params.id,
{
$push : {
flowers : { name, water_freq }
}
},
{ new: true }
).then((user) => res.json(user));
});
I want to delete flowers from users by their id but I'm unable to get it to work.
The code below is my non-working attempt at it.
router.delete('/:id/flowers/delete/:flowerid', (req, res) => {
User.findByIdAndUpdate(req.params.id, {
$pull : {
flowers : { _id: req.params.flowerid }
}
}).then((user) => res.json(user));
});
I would greatly appreciate if someone can help me get this right.

One possible cause is, in your query {$pul: xxxx}, MongoDB is expecting a BSON type object id that was auto generated for each flower entry, while you are giving a string. So you may want to convert it to the proper type before doing the query:
try:
router.delete('/:id/flowers/delete/:flowerid', (req, res) => {
User.findByIdAndUpdate(req.params.id, {
$pull : {
flowers : { _id: ObjectId(req.params.flowerid) }
}
}).then((user) => res.json(user)); });
To see more about the objectId

Thanks for the replies!
I did some more testing with postman and found out that in fact the code I posted in my question did work after all. What set me off was that the response with the user document still displayed the flower I had just deleted, which made me think it didn't work.
I still have no idea why that is, or if there's a way to get a response with the updated user. But the deletion seems to work as intended.

Related

unable to find a record in my db although it is present in my db

I'm using findOne() method to search through my db using the following code
app.get("/articles/:articleId", (req, res) => {
Article.findOne({ _id: req.params.articleId}, (err, foundArticle) => {
if(foundArticle) {
res.send(foundArticle);
} else {
res.send("No article found");
}
});
});
using the following URL in postman
http://localhost:3000/articles/626e950e4fb74295f5139b78
and passing the object id for the following record in the DB
{
"_id": "626e950e4fb74295f5139b78",
"title": "Albert Einstein",
"content": "Did you know that sleep is good for your brain? Einstein sure did, he slept for 10 hours a day!",
"__v": 0
}
but My find one code returns no article found, even though the article is present
collection modal
const articleSchema = {
title: String,
content: String
};
const Article = mongoose.model("Article", articleSchema);
after trying different solution I finally got to solve the issue by
adding a _id: string in the schema solves the issue
const articleSchema = {
_id: String,
title: String,
content: String
};
rest of the code will remain the same

MERN - update specific string in an array's object

I am using mongoose to connect my backend (Express) server to database. I want to do normal CRUD operations - but I am able to do it only for direct data in object, but I need to be able to access also array data.
Example of my model:
const LeewaySchema = new mongoose.Schema({
name: {
type: String,
},
shirt: [
{
name: String,
image: String,
},
],
With the following code I am able to update only name of the object, but I need to be able to update also name in shirt array
Here is working approach when changing name of object:
app.put('/update', async (req, res) => {
const updateName = req.body.updateName;
const id = req.body.id;
console.log(updateName, id);
try {
await ClosetModel.findById(id, (error, closetToUpdate) => {
closetToUpdate.name = updateName;
closetToUpdate.save();
});
} catch (err) {
console.log(err);
}
res.send('success');
});
And I tried the same with shirt array, just specifying the correct path
app.put('/update-shirt', async (req, res) => {
const updateShirtName = req.body.updateShirtName;
const id = req.body.id;
try {
await ClosetModel.findById(id, (error, closetToUpdate) => {
closetToUpdate.shirt.name = updateShirtName; // different path here
closetToUpdate.save();
});
} catch (err) {
console.log(err);
}
res.send('success');
});
The server crashes and /update-shirt conflicts with /update path
I am using the same route and frontend for READ
useEffect(() => {
axios
.get('http://localhost:8000/read')
.then((response) => {
setListOfClosets(response.data);
})
.catch(() => {
console.log('error');
});
}, []);
And update name function calling with button onClick:
const updateCloset = (id) => {
const updateName = prompt('Enter new data');
axios
.put('http://localhost:8000/update', {
updateName: updateName,
id: id,
})
.then(() => {
setListOfClosets(
listOfClosets.map((val) => {
return val._id === id
? {
_id: id,
name: updateName,
email: val.email,
}
: val;
})
);
});
};
I don't really know how to do update for shirt's name, I tried to copy paste and just change path and url of course, but it did not work.
The question doesn't actually describe what specific transformation (update) you are attempting to apply to the document. Without knowing what you are attempting to do, there is no way for us to help advise on how to do it.
Say, for example, that the document of interest looks like this:
{
_id: 1,
shirt: [
{ name: "first shirt", image: "path to first shirt" },
{ name: "second shirt", image: "path to second shirt" },
{ name: "third shirt", image: "path to third shirt" }
]
}
Also let's say that the application hits the /update-shirt endpoint with an id of 1 and a updateShirtName of "updated shirt name". Which entry in the array is that string supposed to be applied to? Similarly, how would that information be passed to the server for it to construct the appropriate update.
It is absolutely possible to update documents in an array, here is some documentation about that specifically. But the actual structure of the command depends on the logic that you are attempting to provide from the application itself.
The only other thing that comes to mind here is that the motivation for the schema described in the question seems a little unclear. Why is the shirt field defined as an array here? Perhaps it should instead just be an embedded document. If so then the mechanics of updating the field in the subdocument are more straightforward and none of the aforementioned concerns about updating arrays remain relevant.
just make an update api where you just have to pass the id and and pass the shirt in the findByIdAndUpdate query and hit the postman by passing the below code.
shirt: [
{
name: "jhrh",
image: String,
},
],

Updating nested array mongodb

I think there are multiple ways to do this, and that has me a little confused as to why I can't get it to work.
I have a schema and I would like to update Notes within it but I can't seem to do it. Additionally, if I want to return the notes how would I go about doing it?
schema :
{
_id : 1234
email : me#me.com
pass : password
stock : [
{
Ticker : TSLA
Market : Nasdaq
Notes : [
"Buy at 700",
"Sell at 1000"
]
},
{
Ticker : AAPL
Market : Nasdaq
Notes : [
"Buy at 110",
"Sell at 140"
]
},
]
}
Each user has a list of stocks, and each stock has a list of notes.
Here is what I have tried in order to add a note to the list.
router.post(`/notes/add/:email/:pass/:stock/:note`, (req, res) => {
var email = req.params.email
var pass = req.params.pass
var note = req.params.note
var tempStock = req.params.stock
userModel.findOne({email: email} , (err, documents)=>{
if (err){
res.send(err);
}
else if (documents === null){
res.send('user not found');
}else if (bcrypt.compareSync(pass , documents.pass)){
userModel.findOneAndUpdate({email : email , "stock.Ticker" : tempStock}, {$push : {Notes : note}} ,(documents , err)=>{
if(err){
res.send(err);
}else {
res.send(documents.stock);
}
})
}
})
})
Thanks :)
Currently, you are pushing the new note into a newly created Notes property inside the model instead of into the Notes of the concrete stock. I am not completely aware of the mongoose semantics but you need something like this:
userModel.findOneAndUpdate({ email: email, "stock.Ticker": tempStock }, { $push: { "stock.$.Notes": note } }, (documents, err) => {
$ gives you a reference to the currently matched element from the stock array.
For the second part, I am not sure what you mean by
Additionally, if I want to return the notes how would I go about doing it?
They should be returned by default if you're not doing any projection excluding them.
Also, as per the docs(and general practise), the callback for the findOneAndUpdate has a signature of
(error, doc) => { }
instead of
(documents, err) => { }
so you should handle that.

How to delete an object from an array in mongodb?

MongoDB collection/doc :
{
_id:something,
name:something,
todos: [
{key:1234},
{key:5678}
]
}
I want to delete the object with key:5678 using mongoose query. I did something like this but It's not deleting the object at all and returning the User with unchanged todos array.
Node Route:
router.post('/:action', async (req, res) => {
try {
if (req.params.action == "delete") {
const pullTodo = { $pull: { todos: { key: 5678 } } }
const todo = await User.findOneAndUpdate({ _id:req.body.id} },pullTodo)
if (todo) {
res.json({ msg: "Todo Deleted", data: todo });
}
}
} catch (err) {
console.log(err)
}
})
I have allso tried findByIdAndUpdate(),update() methods but none of them deleting the object from the array. Getting User as a result without deleting the object from the array.
It is working, but you forgot give an configuration to the function call of Model.findByIdAndUpdate..
const todo = await User.findOneAndUpdate({ _id:req.body.id} },pullTodo, {new: true});
// if {new: true} is enabled, then it will give the latest & updated document from the
// result of the query. By default it gives the previous document.
Do some, research first. This isn't a type of question that should be asked. It's already been answered several times in stackoverflow.
Try using Model.findOneAndRemove() instead. It also makes only one call to the database.
Example: User.findOneAndRemove({'todos':{'$elemMatch':{key}});
can you please re-visit your JSON like below and see if this design works for you.
> db.test6.find()
{ "_id" : "mallik", "name" : "mallik-name", "todos1" : { "key1" : [ 1234, 5678 ] } }
> db.test6.update({},{$pull:{"todos1.key1":5678}},{multi:true});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test6.find()
{ "_id" : "mallik", "name" : "mallik-name", "todos1" : { "key1" : [ 1234 ] } }
>
I was adding a key property to every "Todo" using "mongoose.Types.ObjectId()" and I was querying with id string like : "5f439....." which was the problem. So I used:
1st Step: MongoId = require('mongodb').ObjectID;
2nd Step: const key = MongoId (**<actual id here>**);

Find specific data in MongoDB via NodeJS

I need to find specific data in my MongoDB for the rest API but it's returning null.
app.get('/api/v1/champions/:name', (req, res) => {
db.find({"champions": req.params.name}, (err, champion) => {
res.json(err)
})
})
Here is my MongoDB Schema:
champions: {
champ_name_1: {
dmg: Number,
cost: Number
},
champ_name_2: {
....
}
}
Since you are checking to see if a key exists in the champions object you'll need to write the query differently.
If your data was formatted like this then your query would work. (champions is a String)
{
"champions": "champ_name_1",
"dmg": 123,
"cost": 123
}
To check if a key exists in an object in mongo, use a query like this.
const champKey = 'champions.' + req.params.name;
db.find({ [champKey]: { $exists: true } });
https://docs.mongodb.com/manual/reference/operator/query/exists/
you can use mongoose package
here
and simply use the mongoose's find() methond to find particular data as providing to it.
For Example: find({_id: MY_ID_HERE});

Resources