Pushing data from React in array in MongoDB - reactjs

I want to push a string in an array in a MongoDB document using React/NodeJS/MongoDB,
Here's my code in React
async function toggleLike() {
try {
const dataUser = await axios.post(
`http://localhost:5000/user/${props.auth.user.id}/add/moviesLiked/${props.match.params.id}`);
console.log("user ", dataUser);
forceUpdate();
} catch (error) {
console.log(error);
}
Here's my code in NodeJS
router.post("/user/:user/add/moviesLiked/:movie", function(req, res) {
console.log("in api function add");
mongo.connect(
url,
{
useNewUrlParser: true,
useUnifiedTopology: true
},
(err, client) => {
if (err) {
console.error(err);
return;
}
const db = client.db("ofilms-demo");
const collection = db.collection("users");
collection.update(
{ _id: req.params.user },
{ $addToSet: { moviesLiked: req.params.movie } }
);
console.log("req params user ", req.params.user);
console.log("req params movie ", req.params.movie);
client.close();
}
);
});
Here's the model of an user in Mongoose
const UserSchema = new Schema({
moviesLiked: Array,
moviesDisliked: Array,
});
All my console.log show the right thing, but I still don't have the data pushed in the array,
Can somebody help me ? Thank you,

collection.update is asynchronous, so you need to wait for it to finish executing before closing your connection to Mongo and returning a response to the client.
You can wait for the update operation to complete by either passing a call back to the update method or using the async/await javascript feature.
Passing a call back function:
router.post("/user/:user/add/moviesLiked/:movie", function (req, res) {
mongo.connect(
url,
{
useNewUrlParser: true,
useUnifiedTopology: true
},
(err, client) => {
if (err) {
console.error(err);
return;
}
const db = client.db("ofilms-demo");
const collection = db.collection("users");
collection.update(
{ _id: req.params.user },
{ $addToSet: { moviesLiked: req.params.movie } },
function (error, result) { // The callback function
if (error) {
// Handle the error and send a respone to the user
} else {
// Make use of the result and send a response to the user
}
client.close();
}
);
}
);
});
Using async/await:
// Add the async keyword before declaring the function
router.post("/user/:user/add/moviesLiked/:movie", async function (req, res) {
mongo.connect(
url,
{
useNewUrlParser: true,
useUnifiedTopology: true
},
(err, client) => {
if (err) {
console.error(err);
return;
}
const db = client.db("ofilms-demo");
const collection = db.collection("users");
try {
// Add the await keyword before the update call
await collection.update(
{ _id: req.params.user },
{ $addToSet: { moviesLiked: req.params.movie } },
);
// Send response to your client
} catch (err) {
// Handle any possible error
}
client.close();
console.log("req params user ", req.params.user);
console.log("req params movie ", req.params.movie);
}
);
});

After DB i/o operation is done you should send back the response to your client something like this:
use try-catch to get the error message without crashing the whole node server.
Don't forget to send back the response to client otherwise, the client-side will keep waiting for server response until it's timeout reached
Node.js
router.post("/user/:user/add/moviesLiked/:movie", async (req, res) =>{
console.log("in api function add");
mongo.connect(
url,
{
useNewUrlParser: true,
useUnifiedTopology: true
},
(err, client) => {
if (err) {
console.error(err);
res.status(500).send({"message":"error occured", err})
return;
}
try{
const db = client.db("ofilms-demo");
const collection = db.collection("users");
const response = await collection.update(
{ _id: req.params.user },
{ $addToSet: { moviesLiked: req.params.movie } }
);
console.log("req params user ", req.params.user);
console.log("req params movie ", req.params.movie);
//send back the response
res.status(200).send({response, "message":"your profile is successfully updated."})
client.close();
}catch(err){
//check what is the error in your Nodejs console (Not browser console)
console.log(err)
//send back response
res.status(500).send({"message":"error occured", err})
}
);
}
});
MongoDB is itself schema-less. you don't have to provide schema. if you want to provide your own schema I'd recommend using mongoose. & mongoose arrays

Related

Mongoose/Mongodb getting .deleteOne is not a function

When I click to delete a post, my console is saying TypeError: post.user.posts.deleteOne is not a function. It giving me this error after deleting.
const post = await Post.findByIdAndDelete(id).populate('user'); This code I am deleting the post from Post Schema
await post.user.posts.deleteOne(post)This code is to delete the post from the User Schema. I populated user and assigned it to post and then delete the user's post from this code, but I'm getting the error here.
Below is the controller code
export const deletePost = async (req, res) => {
const { id } = req.params;
try {
const post = await Post.findByIdAndDelete(id).populate('user');
await post.user.posts.deleteOne(post)
if (!post) {
return res.status(500).json({ message: "Unable To Delete" })
}
res.status(200).json({ message: "Deleted Successfully" })
} catch (error) {
console.log(error);
}
}
Client side delete request
const handleDeleteTrue = async () => {
try {
const { data } = await api.delete(`/post/${id}`)
console.log(data)
window.location.reload();
} catch (error) {
console.log(error.response.data.message);
}
};
User model schema
import mongoose from 'mongoose';
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
username: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true,
unqie: true
},
password: {
type: String,
required: true,
minlength: 6
},
posts: [{ type: mongoose.Types.ObjectId, ref: "Post", required: true }]
});
export default mongoose.model('User', userSchema);
Im able to delete the post from the post model schema, but in this pic, which shows the user model schema, that same post that was deleted is not deleted here. This is the problem Im trying to solve.
What I can seem to understand in your function below is that you're trying to delete a single post and also checking if post exists first
export const deletePost = async (req, res) => {
const { id } = req.params;
try {
const post = await Post.findByIdAndDelete(id).populate('user');
await post.user.posts.deleteOne(post)
if (!post) {
return res.status(500).json({ message: "Unable To Delete" })
}
res.status(200).json({ message: "Deleted Successfully" })
catch (error) {
console.log(error);
}
}
I'd suggest you try this
export const deletePost = async (req, res) => {
const { id } = req.params;
try {
//check if document exists in mongoDB collection
if (!mongoose.Types.ObjectId.isValid(id)) {
return res.status(500).json({ message: "Unable To Delete" }) }
await Post.deleteOne(id)
res.status(200).json({ message: "Deleted Successfully" })
catch (error) {
console.log(error);
}
}
I found out the answer. My user model schema for post was an array so I had to use $pull to delete it.
This is the code that worked for me
await post.user.posts.pull(post)
await post.user.save()
You can't use findOneAndDelete on populate to delete one object from an array. it doesn't work that way. Use This Instead.
const result = await User.findOneAndUpdate(
{ _id: Id },
{ $pull: { post:PostId } },
{ new: true }
);
You can find More on Pull and Push Operations on BlogLink

MongoDB pushing objects to array within object

I am trying to push a new object to an array with objects inside another object (see DB Schema here: MongoDB set-up). Trying to push to notes within subtopics[2] object within the topic.
This is the code but it ends up deleting the specified subtopic id:
router.patch('/:topicId/:subtopicId/note', async (req, res) => {
try {
const subtopicNote = await Note.update(
{_id: req.params.topicId},
{subtopics :
{ _id: req.params.subtopicId,
$push: {
notes:
{note: req.body.note}
}
}
}
);
res.json(subtopicNote);
} catch (err) {
res.json({ messsage: err})
}
})
This worked for me :)
router.patch('/:topicId/:subtopicId/note', async (req, res) => {
try {
const subtopicNote = await Note.updateOne(
{_id: req.params.topicId, subtopics : {"$elemMatch": {"_id":req.params.subtopicId}}},
{ $push : {
'subtopics.$.notes': {note: req.body.note}
}
}
)
res.json(subtopicNote);
} catch (err) {
res.json({ message: err})
}
})

How to Properly Send to Socket Room?

The 'message' socket is not working sending to the frontend. I think the problem is originating from my server code, specifically this block:
socket.to(data.roomId).emit('message', {
from: data.from,
body: data.body,
timestamp: data.timestamp
});
When I change the above to socket.emit('message'), the message is received and rendered on the front end. However, because it's not to the specific roomId, there is no instant chat functionality. I know data.roomId is the proper roomId using a console.log in the proper scope.
Server API
io.sockets.on('connection', function (socket) {
//meant to join the socket to the roomId so that messages can be emmitted to the roomId
socket.on('join', function (data) {
socket.join(data.roomid, () => {
console.log(data.roomid);
});
});
//my Conversation schema holds Message Schemas
socket.on('connected', function (data) {
//loads all messages already created
const filter = { roomId: data.roomid };
(async () => {
console.log('searching for Schema');
let conversation = await Conversation.findOne(filter)
.populate('messages')
.exec(function (err, message) {
if (message) {
const array = message.messages;
console.log(array);
socket.emit('send', { arra: array }); //sends previous conversation
} else {
console.log('Schema not found');
}
});
})();
});
socket.on('server:message', (data) => {
const filter = { roomId: data.roomId };
const message = new Message({
from: data.from,
body: data.body,
timestamp: data.timestamp
});
(async () => {
console.log('searching for Schema');
let conversation = await Conversation.findOneAndUpdate(filter, {
$push: { messages: message }
});
if (conversation == null) {
console.log('Schema being created');
(await Conversation.create(filter)).populate('messages');
message.save(function (err) {
if (err) console.log('an error has occured saving the message');
// saved!
});
await Conversation.findOneAndUpdate(filter, {
$push: { messages: message }
});
let updatedConversation = await Conversation.findOne(filter);
} else {
console.log('Schema found');
let updatedConversation = await Conversation.findOne(filter);
message.save(function (err) {
if (err) return handleError(err);
// saved!
});
}
})();
//this socket is not working
socket.to(data.roomId).emit('message', {
from: data.from,
body: data.body,
timestamp: data.timestamp
});
});
});
Frontend
//this socket is not receiving anything
ioClient.on('message', (msg) => {
console.log(msg); //this is not printing anything
if (isMount) {
setMessages((previousMessages) => [
...previousMessages,
toChatMessage(msg)
]);
}
});
When you broadcast to a room from a given socket using
io.on('connection', function(socket){
socket.to('some room').emit('some event');
});
Every sockets in the room excluding the sender will get the event.
In your case, you need to use io.to('some room').emit('some event');
Check out this Socket.IO Documentation

Json; how can I return my res array of MongoDB Models to Router's .get, as json? (Node js)

I can successfully connect, query & find all of the files that I want from my database, according to my model.
But I can't actually return them as JSON yet... so my question is, how can I return this following result (console.log of res):
Into JSON for my get request?
My model with most vars snipped for brevity:
const fileSchema = new mongoose.Schema({
fileName: { type: String },
versionData: [
{
versionID: { type: Number, required: false
}
]
});
This is my main code where I am trying to return my res as JSON so that my Get request can be properly processed and display the results. I've tried returning JSON.stringify(res), but that didn't seem to work.
// Controller code
findAll = function() {
File.find({}, function(err, res) {
if (err) console.log(err);
else {
// Res is populated successfully
console.log(res);
return res.json(); // JSON.stringify(res)
}
});
};
Router:
// Return all results
router.get("/findAll", ctrlFile.findAll);
Thank you
Hey so I got this working by changing:
findAll = function() {
File.find({}, function(err, res) {
if (err) console.log(err);
else {
return res.json();
}
});
};
To:
findAll = (req, res) => {
File.find({}, function(err, result) {
if (err) console.log(err);
else {
return res.json({ result });
}
});
};

When I update a record in MERN stack it deletes the object

When I try to update a record, it deletes the objects instead of updating them. I know my routes are correct since it's working in Postman.
Routes:
router.route('/update/:id').post(function (req, res) {
Bet.findById({ _id: req.params.id }, function (err, bets) {
if (!bets)
res.status(404).send("Data not found.");
else {
bets.result = req.body.result;
bets.profit = req.body.profit;
bets.save().then(bets => {
res.json('Update complete.');
})
.catch(err => {
res.status(400).send("Unable to update.");
});
}
});
});
App.js
handleSelection = async (value, id, event, key) => {
const obj = {
result: "Win",
profit: profits,
}
axios.post("http://localhost:5000/bet/update/" + key.id, obj)
.then(res => console.log(res.data));
}
Example of a json;
{
"_id": "5ddfe84f07a48861b4c0c153",
"name": "asd",
"profit": "-10",
"result": "Loss"
}
When my code runs, it becomes like this:
{
"_id": "5ddfe84f07a48861b4c0c153",
"name": "asd"
}
Actually it is not deleting your document, but overwriting existing fields with empty values. And the reason is most probably that you don't send result and profit values in POST request body.
So I advise you to console.log req.body inside your route to see if result and values are there:
By the way you can shorten your code using findByIdAndUpdate method like this:
router.route('/update/:id').post(async (req, res) => {
console.log("req.body: ", req.body);
try {
const { result, profit } = req.body;
const response = await Bet.findByIdAndUpdate(req.params.id, {result,profit}, { new: true });
if (!response) return res.status(400).send("Data not found");
res.json("Update complete");
} catch (err) {
res.status(500).send("Something went wrong");
}
})
To send a POST request, you can use postman, with this body:
And in your node console.log will log this:
After you see it works with postman, you can add the content-type headers to axios.post and try if it works. Also be sure the console.log("obj: ", obj) logs your object with the data you want.
console.log("obj: ", obj);
axios.post("http://localhost:5000/bet/update/" + key.id, obj,
{
headers: { "content-type": "application/json" }
})

Resources