Reputation: 691
In the development of my fullstack aplication (which is a facebook clone) i've found this error in my server, and i don't know how to solve it.
I'm using node.js, express, mongodb, mongoose and typescript, and, in the development of the route for liking publications is where this error took place (as well in the liking comments route).
Error
VersionError: No matching document found for id "60bf5b73de309f1a30fe88a2" version 10 modifiedPaths "likes"
at generateVersionError (C:\Users\diego cifuentes\Desktop\Portafolio\Fullstack Projects\Facebook - MERN ( with next.js )\backend\node_modules\mongoose\lib\model.js:432:10)
at model.Model.save (C:\Users\diego cifuentes\Desktop\Portafolio\Fullstack Projects\Facebook - MERN ( with next.js )\backend\node_modules\mongoose\lib\model.js:488:28)
at C:\Users\diego cifuentes\Desktop\Portafolio\Fullstack Projects\Facebook - MERN ( with next.js )\backend\src\controllers\publication.ts:166:18
at Generator.next (<anonymous>)
at fulfilled (C:\Users\diego cifuentes\Desktop\Portafolio\Fullstack Projects\Facebook - MERN ( with next.js )\backend\src\controllers\publication.ts:5:58)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:93:5) {
version: 10,
modifiedPaths: [ 'likes' ]
}
VersionError: No matching document found for id "60bf5b73de309f1a30fe88a2" version 10 modifiedPaths "likes"
at generateVersionError (C:\Users\diego cifuentes\Desktop\Portafolio\Fullstack Projects\Facebook - MERN ( with next.js )\backend\node_modules\mongoose\lib\model.js:432:10)
at model.Model.save (C:\Users\diego cifuentes\Desktop\Portafolio\Fullstack Projects\Facebook - MERN ( with next.js )\backend\node_modules\mongoose\lib\model.js:488:28)
at C:\Users\diego cifuentes\Desktop\Portafolio\Fullstack Projects\Facebook - MERN ( with next.js )\backend\src\controllers\publication.ts:166:18
at Generator.next (<anonymous>)
at fulfilled (C:\Users\diego cifuentes\Desktop\Portafolio\Fullstack Projects\Facebook - MERN ( with next.js )\backend\src\controllers\publication.ts:5:58)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:93:5) {
version: 10,
modifiedPaths: [ 'likes' ]
}
This only happens when the user in the client side tries to click like button too fast multiple times, so, because of that, the problem doesn't give the action enough time to interact with the server.
I've looked for a solution, and i've seen that people mention .update() instead of .save in this cases, but, i can't really understand specifycally what .update() means and why it might be a solution for this problem.
To give you a litle bit more of context, this is how the logic looks like for the liking pubs route
// Like Publication
export const likePublication = async (req: Request, res: Response) => {
const { publicationId } = req.params;
const { identifier } = req.body;
// Check id's
if (!mongoose.Types.ObjectId.isValid(identifier!))
return res.status(400).json({ Message: "identifier not valid" });
if (!mongoose.Types.ObjectId.isValid(publicationId!))
return res.status(400).json({ Message: "identifier not valid" });
// Find pub
const thePub: Ipub = await Publication.findById(publicationId);
// Find user
const theUser: Iauth = await User.findById(identifier);
try {
// Check if user already liked, if not, like the pub
if (thePub.likes!.find(f => f.identifier === theUser.id) === undefined) {
thePub.likes!.push({ identifier: theUser.id! });
}
// Check if user already liked, if user liked, cut like
else if (thePub.likes!.find(f => f.identifier === theUser.id)) {
thePub.likes = thePub.likes!.filter(
filt => filt.identifier !== theUser.id!
);
}
// Save
await thePub.save();
return res.json(thePub);
} catch (err) {
console.log(err);
return res.status(500).json({ Error: "the API failed" });
}
};
What can i do ? thanks !
Upvotes: 1
Views: 353
Reputation: 1227
MongoDB will auto-increment the version number of a document when you save it.
Before the save action, it will compare the version number of the incoming document and the existing document in the collection. If the incoming document version is lower than the version in the collection it will throw the "No matching document found" error. When a user clicks the like button very fast await Publication.findById(publicationId);
might not return the latest document since the previous save action might not be completed. One way of handling this issue is to set a debounce time in calling likePublication
route from the frontend. Otherwise, you have to catch this error and try to like again.
// Like Publication
const maxRetry = 3;
async function likePublication(publicationId:ObjectId, identifier:ObjectId, retryCount = 0) {
if(retryCount > maxRetry) {
throw new Error("Max retry reached");
}
// Find pub
const thePub: Ipub = await Publication.findById(publicationId);
// Find user
const theUser: Iauth = await User.findById(identifier);
try {
// Check if user already liked, if not, like the pub
if (thePub.likes!.find(f => f.identifier === theUser.id) === undefined) {
thePub.likes!.push({ identifier: theUser.id! });
}
// Check if user already liked, if user liked, cut like
else if (thePub.likes!.find(f => f.identifier === theUser.id)) {
thePub.likes = thePub.likes!.filter(
filt => filt.identifier !== theUser.id!
);
}
// Save
return thePub.save();
} catch (err) {
if(err.message.indexOf("No matching document found") > -1) {
return likePublication(publicationId, identifier, retryCount+1);
}
throw err;
}
}
export const likePublicationHandler = async (req: Request, res: Response) => {
const { publicationId } = req.params;
const { identifier } = req.body;
// Check id's
if (!mongoose.Types.ObjectId.isValid(identifier!))
return res.status(400).json({ Message: "identifier not valid" });
if (!mongoose.Types.ObjectId.isValid(publicationId!))
return res.status(400).json({ Message: "identifier not valid" });
try {
const thePub = await likePublication(publicationId, identifier);
return res.json(thePub);
} catch (err) {
console.log(err);
return res.status(500).json({ Error: "the API failed" });
}
};
Upvotes: 1
Reputation: 5411
Since you control the full-stack, in the frontend part, you can use BlockUI to block the screen when a request is being sent to the server. This will prevent users from doing multiple requests when a request is treated and has not been finished yet.
Upvotes: 1