Hi fellow developers!
I've got this error showing up in my console when I try to save two identical documents in a collection in MongoDB that has nothing to do with the index shown in the error.
Here's the error: E11000 duplicate key error collection: Bohemian.orders index: user.email_1 dup key: { user.email: null }
Now this makes no sense, because I'm trying to save an Order document in a separate collection, which has nothing to do with the user router I had set up previously.
Here is the schema and model code:
const mongoose = require('mongoose');
const orderSchema = new mongoose.Schema({
amountToPay: Number,
});
const Order = mongoose.model("Order", orderSchema);
module.exports.Order = Order;
As shown here, I am only trying to save the amount to be payed into the database in a separate collection.
Here is the router file:
const express = require('express');
const router = express.Router();
const { Order } = require('../models/Order');
router.get("/", async (req, res, next) => {
try {
const orders = await Order.find();
if (orders.length === 0) return res.status(404).send("There are currently no orders");
res.send(orders);
} catch (ex) {
console.error(ex);
next();
}
});
router.post("/", async (req, res, next) => {
try {
const order = new Order({
amountToPay: req.body.amountToPay
});
await order.save();
res.send(order);
} catch (ex) {
console.error(ex);
next();
}
});
module.exports = router;
As you can see there is nothing relative to the error that I'm getting and I have no clue why I'm getting a duplicate user.email = null key , when I haven't made any reference to the User model or router.
Here is the POST call I'm making from POSTMAN to test:
Pretty straight forward, nothing extreme, nothing tangled, right? Well the first ever POST call saves the document in the Database, but from then on I keep getting the same error. The only thing I can take from that is that when I save the first document, Mongo looks for the user.email property when I'm creating the new instance of Order and when it doesnt find it, it creates it with a value of null and then the next document would naturally be a duplicate, hence the error. But I'm confused, because this model and router should not absolutely nothing to do with the user ones.
Here is the error:
So please if anyone can help me understand why MongoDB is screwing with me or where I'm making a mistake, I would really appreciate it.
I found out what the problems was, thanks to Molda:
At some point I had created these indexes, which I'm still unsure when and how, but I did. Which essentially lead to this error, when I tried to save the second document.
A simple quick console log of the indexes in the collection showed that I had that index in there.
const indexes = await Order.collection.getIndexes();
console.log(indexes);
Then I removed them using this method:
await Order.collection.dropIndexes("user.email_1");
And everything worked flawlessly from there.
I hope this helps anyone in this situation in the future and thanks Molda! :)
Related
I am making a backend api with node/express and am running into an error. I've tried many different approaches as well as looked at many different questions on here, but none seem to fix my problem. As far as I can tell my routes are structured properly, but they simply don't work. Perhaps I'm missing something super obvious, but I'm at a loss.
Bellow is a simplified version of my index.js where I start my node server:
const movies = require("./routes/movies");
app.use("/api/movies/", movies);
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Listening on port ${port}...`));
And then in my movies.js I have several endpoints:
// DOES WORK - returns movies with the specifed object
router.get("/genre/:id", async (req, res) => {
const movies = await Movie.find({ genres: req.params.id });
if (!movies)
return res
.status(400)
.send(`No movies with genre of id ${req.params.id} could be found.`);
res.send(movies);
});
// DOESN'T WORK - returns main html page
router.get("/tmdbid/:id", async (req, res) => {
const movie = await Movie.findOne({
tmdbID: req.params.id,
});
if (!movie)
return res
.status(400)
.send(`No movies with tmdbID of ${req.params.id} could be found.`);
if (res.status(404)) res.send("Route not found");
res.send(movie);
});
// DOESN'T WORK - returns Cannot GET /api/movies/title/{title}
router.get("/title/:title", async (req, res) => {
const movie = await Movie.find({
title: { $regex: new RegExp(req.params.title, "i")
},});
if (!movie)
return res
.status(400)
.send(`The movie with name of ${req.params.title} could not be found.`);
res.send(movie);
});
I'm not sure what needs to be done here. All of these share similar syntax, but each result in a different response. None of them log any errors to the terminal either. I know for sure general refactoring should be done, but as far as the problem at hand goes, I can't wrap my head around why the last two aren't working. Lastly, I have several other endpoints in the this file, all of which are working fine. Just these two have issues.
Add a console.log statement to check whether the endpoint is hit or not. The code logic in your code sample above sounds right. However, I would try formatting the code batter.
if (!movie){
return res.status(400).send(`No movies with genre of id ${req.params.id} could be found.`);
}
Also, make sure you do not forget to restart your node js server.
What are those return statements doing there? and you could improve your if statements as well like such ->
try {
const movie = await Movie.findOne({
tmdbID: req.params.id,
});
if (!movie) {
res
.status(400)
.send(`No movie(s) could be found.`);
} else {
res
.status(200)
.send(movie);
}
} catch(err) {
res.send(500);
}
I eventually found my answer and it was simply a mistake I had in the url when calling the endpoint, not in the endpoint's code itself. Rather than calling to localhost:5000, I was calling to localhost:3000, which is probably why it was returning the index.html of my react app.
So, my code works as is. Perhaps not the best structure ever, but as long as it works, I'm happy.
Still not sure why the /title/:title endpoint wasn't working and then suddenly was. That remains a mystery.
This code works like it should work, but after fifth GET request it does what it should do on the backend(stores the data in db) but it's not logging anything on the server and no changes on frontend(reactjs)
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const User = require('./login').User;
mongoose.connect('mongodb://localhost:27017/animationsdb');
router.get('/', async(req, res) => {
await User.findOne({ username: req.query.username }, (err, result) => {
if (result) {
// when user goes to his profile we send him the list of animations he liked
// list is stored in array at db, field likedAnimations
res.send({ animationList: result.likedAnimations });
console.log("Lajkovane animacije:", result.likedAnimations);
} else {
console.log("no result found");
res.sendStatus(404)
}
});
});
router.put('/', async(req, res) => {
console.log("username:", req.body.username);
console.log("link:", req.body.link);
// if animation is already liked, then dislike it
// if it's not liked, then store it in db
const user = await User.findOne({ username: req.body.username });
if (user.likedAnimations.indexOf(req.body.link) === -1) {
user.likedAnimations.push(req.body.link);
} else {
user.likedAnimations = arrayRemove(user.likedAnimations, user.likedAnimations[user.likedAnimations.indexOf(req.body.link)]);
}
user.save();
});
function arrayRemove(arr, value) {
return arr.filter((item) => {
return item != value;
});
}
module.exports = router;
For first five requests I get this output:
Liked animations: ["/animations/animated-button.html"]
GET /animation-list/?username=marko 200 5.152 ms - 54
Liked animations: ["/animations/animated-button.html"]
GET /animation-list/?username=marko 304 3.915 ms - -
After that I don't get any output on server console and no changes on front end untill I refresh the page, even though db operations still work and data is saved.
It appears you have a couple issues going on. First, this request handler is not properly coded to handle errors and thus it leaves requests as pending and does not send a response and the connection will stay as pending until the client eventually times it out. Second, you likely have some sort of database concurrency usage error that is the root issue here. Third, you're not using await properly with your database. You either use await or you pass a callback to your database, not both. You need to fix all three of these.
To address the first and third issues:
router.get('/', async(req, res) => {
try {
let result = await User.findOne({ username: req.query.username };
if (result) {
console.log("Liked animations:", result.likedAnimations);
res.send({ animationList: result.likedAnimations });
} else {
console.log("no database result found");
res.sendStatus(404);
}
} catch(e) {
console.log(e);
res.sendStatus(500);
}
});
For the second issue, the particular database error you mention appears to be some sort of concurrency/locking issue internal to the database and is triggered by the sequence of database operations your code executes. You can read more about that error in the discussion here. Since the code you show us only shows a single read operation, we would need to see a much larger context of relevant code including the code related to this operation that writes to the database in order to be able to offer any ideas on how to fix the root cause of this issue.
We can't see the whole flow here, but you need to use atomic update operations in your database. Your PUT handler you show is an immediate race condition. In multi-client databases, you don't get a value, modify it and then write it back. That's an opportunity for a race condition because someone else could modify the value while you're sitting their holding it. When you then modify your held value, you overwrite the change that the other client just made. That's a race condition. Instead, you use an atomic operation that updates the operation directly in one database call or you use transactions to make a multi-step operation into a safe operation.
I'd suggest you read this article on atomic operations in mongodb. And, probably you want to use something like .findAndModify() so you can find and change an item in the database in one atomic operation. If you search for "atomic operations in mongodb", there are many other articles on the topic.
I'm building an API that I intend to use with my react native application. The problem which I'm facing right now is that I when I try to hit this particular route /api/auth/signup in Postman I get the Could not get any response error message.
This is the route:
//create user token
router.post(
"/signup",
[check("username").isEmail(), check("password").isLength({ min: 6 })],
async (req, res) => {
//validate input field on the backend
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
const { username, password, firstName, lastName } = req.body;
try {
//search DB if there is an existing user
let user = await User.findOne({ username });
if (user) {
return res.status(400).json({ msg: "User already exists" });
}
const salt = await bcrypt.genSalt(10);
res.status(200).json({ data: salt });
} catch (error) {}
}
);
module.exports = router;
The strange thing is that if I remove the User.findOne function I get a response. I don't know why this keeps happening, as I had built a similar application following the same pattern without a problem.
NOTE: In the main app.js I have the app.use(express.json({extended:true}), I've also successfully linked the routes in the main file too. Any help would be greatly appreciated!
I think you have a typo in your app.use... statement which you've given in your comment as,
app.use(express.json({extented:true})
which needs to be corrected as, ('d' should come in place of 't')
app.use(express.json({extended:true})
Hope this helps!
So it turns out when I was requiring mongoose in one of my models I required it with an uppercase letter const mongoose = require('Mongoose') which isn't valid, therefore the findOne method won't run because there's no model to take it from. Strange that visual studio code didn't complain about the false import. Anyways, thanks to anyone willing to help me out :) !
I've been trying to figure out why my HTTP Put has not been working after I use it once. When I click a button, I push the current user's id into an array like so:
$scope.currentUser = {
'eventsAttending' = [];
}
$scope.attending = function(event, id){
if($cookieStore.get('token')){
$scope.currentUser = Auth.getCurrentUser();
}
$scope.currentUser.eventsAttending.push(event._id);
$http.put('/api/users/' + $scope.currentUser._id, $scope.currentUser)
.success(function(data){
console.log("Success. User " + $scope.currentUser.name);
});
}
And my HTTP Put function is like so:
var express = require('express');
var controller = require('./user.controller');
var config = require('../../config/environment');
var auth = require('../../auth/auth.service');
router.get('/:id', controller.getEvents);
var router = express.Router();
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
User.findById(req.params.id, function (err, user) {
if (err) { return res.send(500, err); }
if(!user) { return res.send(404); }
var updated = _.merge(user, req.body);
updated.markModified('eventsAttending');
updated.save(function (err) {
if (err) { return res.send(500, err); }
return res.json(200, user);
});
});
};
In my HTML page I have multiple events I can attend, and each event has the button called Attend where I call $scope.attending and the function is called and the HTTP Put occurs. All of this works for the first event I choose to attend. However, when I click the Attend button for another event, I get an error that says:
{"message":"No matching document found.","name":"VersionError"}
And I have no idea why. The error occurs when I try to do updated.save() in the mongoose call and I get res.send(500, err)
I tried to look at http://aaronheckmann.blogspot.com/2012/06/mongoose-v3-part-1-versioning.html to solve the issue as I did some googling but I keep getting an error that says:
Undefined type at `versionKey`
Did you try nesting Schemas? You can only nest using refs or arrays.
Upon adding into my schema:
var UserSchema = new Schema({
versionKey: 'myVersionKey',
...
I also tried to change the .save() function into .update() as someone suggested it online but that seemed to give me even more errors. Any ideas how to fix this? That would be much appreciated!
I think the issue you may be experiencing (as was the case when I was getting this error from a similar action) is that after the first update, the '__v' VersionKey property on the newly updated document has changed, but you might not be updating that property on the object you have in the browser. So when you go to update it again, you're sending the old '__v' VersionKey, (even though you updated the 'eventsAttending' property) and that document conflicts the newer VersionKey. This would assume that the Auth.getCurrentUser(); function returns the whole document object from mongo.
What I did to fix this was simply add delete entity.__v; to delete the old VersionKey from the document before sending it with the request. Better yet, I'd recommend updating the properties your API sends back when returning documents so this issue doesn't happen in the first place.
I have a database on MongoLab. It has several collections. All but one work. One collection is called "selectopts". It has two documents. I can clearly see these two documents.
In my express code I have...
var db = require('mongojs');
db.connect('mongodb://xxxx:xxxx#ds053xxx.mongolab.com:53xxx/rednecks',['selectopts']);
exports.selects = function (req, res) {
db.selectopts.find(function (err, s) {
if (err) return;
res.json(s);
});
};
It always errors at db.selectopts.find..., TypeError: Cannot call method 'find' of undefined. This exact same stupid simple code works fine for four other collections. Why is just this one collection not coming back from MongoLab?
I'm so completely stumped.
EDIT...
Tried db.collection('selectopts').find(... and got this error...
EDIT again...
Here are the two docs in the selectopts collection on MongoLab. Do you see some problem with the docs?...
EDIT x 3...
This is the correct/working mongo connection setup code...
var mongojs = require('mongojs');
var db = mongojs.connect(
'xxx:xxx#ds053xxx.mongolab.com:53xxx/rednecks',
);
See the main difference? (SMFH) :-/
Sometimes when I post at SO I don't receive the exact actual answer from any one reply, but somehow you guys always steer me toward the answer. In this case, it was programmer stupidity. My code at the top of the route js file is wrong. You can see it wrong at the top of this post. I edited and added the correct syntax, which magically got everything working.
To eliminate this type of repetition/syntax/non-DRY bungling, I moved the mongo connection lines into a separate database.js file and require it at the top of the route files. Genius huh?!? :-D
"Thank you" x 100 for all your replies! You always get me back on track :-)
Connect using the following style:
var MongoClient = require('mongodb').MongoClient;
var db;
MongoClient.connect(
'mongodb://xxxx:xxxx#ds053xxx.mongolab.com:53xxx/rednecks',
{auto_reconnect: true},
function(e, database)
{
db = database;
});
exports.selects = function (req, res) {
db.selectopts.find(function (err, s) {
if (err) return;
res.json(s);
});
};