Mongoose - No matching document found (ForEach) - javascript

I'm new to NodeJS and Mongoose and this might be a duplicate question, so don't reply back negatively please. I tried finding a solution but failed to fix this error.
Basically, I get this error when I try to update the database values. The first update I perform works perfectly but from the second one onward, I get this error. The values update but it updates several fields at once giving this error.
Here's my code: (and I have added my github link to the project):
User.js (model)
local: {
...,
education: [{
id: String,
degree: String,
institute: String,
dates: String,
description: String
}],
...
}
router.js
app.put('/put_education/:id', function(req, res) {
var row_id = req.params.id; //get education id from table row
//get the current logged in user
User.findById(req.user._id, function (err, doc) {
if (err) {
console.log('no entry found');
}
//match the table row id with the id in users education model
doc.local.education.forEach(function (education, index) {
console.log(education.id + " " + row_id);
//if rows match, replace the database document with values from client
if(education.id === row_id){
doc.local.education[index] = req.body;
doc.save(function (err) {
if (err) {
console.log(err);
} else {
res.send("Success");
}
});
}
});
});
});
I added a console.log to see the loop operates, the image below shows its fine for the first iteration but acts weirdly for the next ones:
I was thinking of breaking the loop after the id's match but the foreach loop doesnt have a break function, I changed it to a normal for loop but it still gives me the same error. So I dont think breaking the loop is an answer..
Edit: Added image of my website to show what happens after updating (duplicates rows)
Github: https://github.coventry.ac.uk/salmanfazal01/304CEM-Back-End

if break out of an iteration is your issue, then why not use a simple for loop with break statement.
let eduArray = doc.local.education;
for(i=0;i<eduArray.length;i++) {
if(eduArray[i]["id"] == row_id) {
// do your logic
break;
}
}

becuase you are updating the education first time with no problem, then the problem appears afterwards, I suspect that you update the education wrongly, look into this line of code you wrote:
doc.local.education[index] = req.body;
here I see you are assigning whatever inside the request body to education, but have you checked what data is actually inside the req.body?
try to log the req.body to see what you actually assigning to the education.

Related

Why can't I access the elements of this object in Javascript?

I'm working on a simple hobby project after finishing a Udemy course on web development, and I'm having some issues with a simple middleware I've written.
The function in question goes as follows:
const User = require('../models/user.js');
const middleware = {
checkIfAdmin: function checkIfAdmin(req, res, next) {
if (req.isAuthenticated()) {
User.find({"username": "jemorgan"}, (err, foundUser) => {
console.log(foundUser._id);
if (err) {
req.flash("error", "Something went wrong");
} else if (foundUser._id === req.user._id) {
console.log("Going next");
next();
} else {
console.log("going back");
req.flash("error", "You don't have permission to do that.");
res.redirect("back");
}
});
}
}
}
module.exports = middleware;
When I ran the code without the console.log, evaluating the final else branch regardless of whether or not I was logged in as the correct user. I threw in the console.log to try to troubleshoot, and found that user._id was undefined. Strangely, when I remove the .id and simply call console.log(user), I get the following:
[
{
_id: 5ef5c1adfc196d47107679ce,
username: 'jemorgan',
...
}
]
I feel like there must be something simple here that I'm missing. My troubleshooting leads me to believe that the User.find() function is locating the correct document in the database, and that foundUser is a reference to the correct object. Yet when I try to access elements of that object, I'm being told they're undefined, even though I can clearly see that the object does have those elements.
To keep this brief, I'm not showing the whole project here, but if anyone wants context, the project is hosted on my github.
Thanks for any tips that you might habe.
It seems you are querying through Mongoose using .find and not .findOne, it's easy to confuse the two but the first one will always return an array of documents that matched the query while the second will return the first object it finds according to the query. When querying emails/ids etc. you should use findOne.
In your example :
let user=[
{
_id: 5ef5c1adfc196d47107679ce,
username: 'jemorgan',
...
}
]
since user is an array so you can't access user._id , Instead you should user[0]?._id

Model.findOneAndRemove (MongoDB & Mongoose) specificity?

I've been trying to build my first solo CRUD app (without following a tutorial) and came across something a little weird which I can't wrap my head around. Apologies if it's rather obvious, but I couldn't find anything on here about it.
I added my delete route for one of my models and used the following code:
// delete list
router.delete("/lists/:id", function(req, res){
// find list by ID and remove it
List.findOneAndRemove(req.params.id, function(err, deletedList){
if(err) {
console.log("ERROR DELETING LIST");
console.log(err);
res.redirect("/lists");
} else {
// if list is removed successfully, remove each item
console.log(deletedList);
Item.remove({_id: {$in: deletedList.items}}, function(err, deletedItems){
if(err) {
console.log("ERROR DELETING ITEMS");
console.log(err);
} else {
console.log("SUCCESSFULLY DELETED LIST & ALL ITEMS");
res.redirect("/lists");
}
});
}
});
});
Now, it deletes things from the database, however it doesn't delete the right thing. I have a button on my web page which submits a form. The form's action is generated based on the page content where I insert the ID of the model into the action using EJS. When I submit the form to delete the model and it's referenced items, it ends up deleting the first document in the database, even though (to my understanding), I've specified that only documents which match the provided ID should be deleted.
I did fix it, by replacing
List.findOneAndRemove(req.params.id, function(err, deletedList){
if(err) {
console.log("ERROR DELETING LIST");
console.log(err);
res.redirect("/lists");
} else {
with
List.findOneAndRemove({_id: {$in: req.params.id}}, function(err, deletedList){
if(err) {
console.log("ERROR DELETING LIST");
console.log(err);
res.redirect("/lists");
} else {
though I'm not completely sure on why this works and req.params.id doesn't. I was just hoping for some clarification in case I come across something similar in the future.
I believe it is deleting the first item in your array because you are using "FindOneAndRemove" - as is suggested in the name, this will limit the match to a single document. And in your first version of the code:
List.findOneAndRemove(req.params.id, function(err, deletedList){
You aren't specifying your _id field, so i believe it is matching everything, and is only removing a single document due to the method you are using. Where as in the second one, you are
List.findOneAndRemove({_id: {$in: req.params.id}}, function(err, deletedList){
Here's a possible reason I can point. When you pass req.params.id to findOneAndRemove, since JS is not a strongly typed language and you are not specifically typing it, Mongoose isn't sure about what to delete.
The first parameter to findOneAndRemove is the filter object and Mongoose is not able to find the right filtering since the data type that is passed here is a string.
I think then Mongoose consider an empty filter and as usual, empty filter deletes the first item.
One try you can do is to explicitly type the _id, say like
var id = new mongoose.mongo.ObjectID(req.params.id)
and pass this id to the findOneAndRemove() function.
Hope this helps.

Pushing String into database utilizing nodeJS and mongoDB

I am new to JS and I am utilizing the MEAN stack to create a place where students can add classes to their user profile. I already have a session store in my database, so "req.user" will return the currently logged in user information and specifically "req.user.id" will return the currently logged in user's id. Also, I have figured out how to search a course in my database from my application. Ultimately, my goal is that when the user makes the post request to search in the database, I also want those "values" to be pushed into the classes "key". I have provided two options, both of which do not add the respective strings to the database. Thank you for any help!
Portion of Search.JS Option #1
router.post('/', ensureAuthenticated, function (req,res,next) {
var query = {course: req.body.coursename};
db.collection('courses').find(query).toArray()
.then(db.collection('DefaultUser').update({_id: req.user.id}, {$push: {classes: req.body.coursename}}));
res.render('search', {title: 'search'})
});
Portion of Search.JS Option #2
router.post('/', ensureAuthenticated, function(req,res,next) {
var query = {course: req.body.coursename};
db.collection('courses').find(query).toArray((err, results) => {
if (err) {
throw err;
} else {
db.collection('DefaultUser').updateOne({_id: '5c17161d3347e79410ff29ba'}, {
$push: {
classes: req.body.coursename
}
})
console.log(results)
res.render('search', {courses: results, title: 'search'})
}
})
});
Some tips may help:
req.body will hold nothing if you forget to use app.use(express.urlencoded()) to parse the posted data.
You may use ObjectId('<string>') (in option #2) for finding and updating queries, not just the string, because mongodb stores _id as ObjectId type by default.
You may use $addToSet instead of $push modifier to add a unique element to an array, or you may get one student subscribed with two more same class, if he repeatedly submit the form.
In your code, you find in the courses collection first and then update, but since you did nothing with the find result, it is not necessary (empty result does not throw any error). Checking the data is valid is good practice, if you would like to do so, in both options, you need to use findOne instead of find to make sure the course is existed, and .then(course => {/* check if the course is null and then throws an error */} ).
I don't have the full code of your project so I can only guess there may be the problems listed above, wish you good luck!

Stuck on Node + MongoDB asynchronous query issue

A little preface: I am very new to working with Node so please bear with my ignorance
I am trying to pass some info from an array in Node.js, and check whether it exists in a MongoDB document. I am still struggling to wrap my head around Node and how to work with databases asynchronously.
I have the following code
for (i in articleTitle) {
console.log(articleTitle[i]);
// Use connect method to connect to the Server
MongoClient.connect(mongoUrl, function(err, db) {
if (err) throw err; // Throw error
var query = { title: articleTitle[i] }; // Query Parameter
// Perform Query
db.collection(mongoCollection).find(query).toArray(function(err, result) {
if (err) throw err; // Throw error
if (result == '') {
console.log('No results found for title:', articleTitle[i]);
} else {
console.log('Found an entry');
}
db.close(); // Close connection
});
});
}
In the above code, I have an array of strings called articleTitle (for example: ['Title1', 'Title2', 'Title3']), I then run through each of those titles in the array (using the for() loop) to check if each title exists in the database.
The output I get is as follows:
> Title1
> Title2
> Title3
> No results found for title: Title 3
> No results found for title: Title 3
> No results found for title: Title 3
As evident above it seems to be checking for the last object in the array three times. I have also tried to implement the async package but struggled with that as well.
Any help would be appreciated.
The issue you have is scope of the variable i in the callback function.
Use for (let i in articleTitle) instead.
This creates a new variable i for every iteration and scope is restricted to that iteration.
The answers to this question JavaScript closure inside loops – simple practical example explain in detail about why this happens and about scope and closure in JavaScript. The above question is an exact duplicate of this question.

Update 2 different collections simultaneously

I have two models defined like this:
var OrganizationSchema = new mongoose.Schema({
users: [mongoose.Schema.Types.ObjectId]
});
and
var UserSchema = new mongoose.Schema({
organizations: [mongoose.Schema.types.ObjectId]
});
When a user want to join an organization I need to update both the organization collection and the user collection. I want to know what is the best way to achieve this ? Is it worth considering the case when one update request fail ? I'm currently doing something like this (where organization is a collection instance of the model Organization):
User.findByIdAndUpdate(req.userSession.userId, { $push: { organizations: organization.id } }, function (err){
if (err)
{
// server error
console.log(err);
}
else
{
organization.users.push(req.userSession.userId);
organization.save(function (err){
if (err)
{
// server error we need to cancel
User.findByIdAndUpdate(req.userSession.userId, { $pull: { organizations: organization.id } }, function (err){
if (err)
{
// we got a problem one collection updated and not the other one !!
console.log(err);
}
});
}
else
{
// success
}
});
}
});
The problem is: if my second update method fail I will end up with one collection updated and not the other ? Is there a way to make sure they are both updated ?
Well firstly, I would stay clear of that design. I would either reference or embed user in organisations and the other way around, not both of them at same time, so I wouldn't have problems like this(which happens every-time you duplicate data).
MongoDB doesn't have support for simultaneous updates, or transactions. So you are left to manage this in your code.
So yes, if the second update fails, then as you wrote your code you have to rollback, and if the rollback fails, you have to retry till it succeeds(though with exponential backoff probably). Keep in mind that might intefer with other requests(another user tries to save the same thing simultaneously). To handle that you have to give a unique to each entry in the array.

Categories

Resources