How to fix and prevent duplicate key error in mongodb - node.js

I've been working on a hobby project recently and I've encountered a problem I can't seem to figure out, even after scouring the internet for an answer. I'm using Node.js on c9.io with MongoDB. Anytime I try to create a new entry into the database, the first entry works and goes through fine, but then the second one causes an error.
E11000 duplicate key error collection: project.tasks index: username_1 dup key:
{ : null }'
My Schema:
var mongoose = require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");
var taskSchema = new mongoose.Schema({
task: String,
region: String,
cost: String,
when: String,
isAccepted: Boolean,
author: {
id:{
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
},
tasker: {
id : {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
}
});
taskSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("Task", taskSchema);
My Post Request:
app.post("/taskers/index/show", function(req, res){
var task = req.body.task;
var newTask = {
task: task.task,
region: task.region,
cost: task.cost,
when: task.when,
isAccepted: false,
author: req.user._id,
tasker: req.user._id
};
console.log("STSOTSOTSOTOOPP");
Task.create(newTask, function(err, newlyCreated){
if(err){
console.log(err);
} else {
console.log(newlyCreated);
res.redirect("/users/index");
}
});
});
If anyone knows what I'm doing wrong or can lead me to a solution, that would be amazing as I've been stuck on this for a while.

E11000 duplicate key error collection: project.tasks index: username_1 dup key: { : null }
This error is coming from mongo (not from mongoose). Removing indexes from your mongoose schema will not have any impact on the underlying collection so you'll now want to remove the unique index on username from your tasks collection.
This index was likely created by previous code that we no longer see (or perhaps by that taskSchema.plugin(passportLocalMongoose); -- that sounds suspiciously like the kind of thing that would want an index on username).
If you connect to mongo using the shell, you should be to run db.tasks.getIndexes() to see that unique username index, and then use the dropIndexCommand to remove the offending index.
See E11000 duplicate key error index in mongodb mongoose for more details about how mongoose & mongo interact.

Open MongoDB compass and connect same Database that you are using in your code,
open your Db and select your collection in MongoDB Compass
navigate to indexes section and remove unnecessary index
save

Related

MongoDB E11000 duplicate key error collection with no unique field in schema

I'm setting up a simple blog form with title, content, and json base64 image data to pass to a local MongoDB server. This is my post route:
router.post('/:userName/posts/new', isAuthenticated, async (req, res) => {
let parsedImage = req.body.file != null && req.body.file != '' ? JSON.parse(req.body.file).data : ''
const newPost = new Post({
title: req.body.title,
content: req.body.content,
imageJson: parsedImage
})
try {
await newPost.save()
res.redirect("/account")
} catch (e) {
console.log(e)
res.redirect("/home")
}
})
And this is my schema model:
const mongoose = require('mongoose');
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
content: {
type: String,
required: true,
},
imageJson: {
type: String
}
});
module.exports = mongoose.model('Post', postSchema)
no unique: true as you can see. The first time I tried running this, it ran perfectly, redirecting directly to the /account page, as intended in the try catch block. But, on a second attempt, using a different image, different content, and different title, it threw this error:
MongoError: E11000 duplicate key error collection: fullstacksus.posts index: userName_1 dup key: { userName: null }
I have no idea what's going on since userName is a database name used in my user account route, but not in my post-making route. Why is it showing up again? Thanks for your answers.
In your MongoDB there is a unique index on {userName: 1}. Since your schema does not have a userName field, it is missing, i.e. null, in every document, so the index rejects it.
Use the mongo shell to connect and drop the index.

Schema referencing stores the whole value instead of ObjectId alone

Lately, I've been going through a course named The Web Developer Course.
In which the final project is based on Camps.
In the project, the comment database and the campground database are referenced, that is, the ObjectIds of the comments which are posted in a campground is stored in an array. This is what to be happened actually.
But in my case, the exact scenario's changed..When I try to Add a new comment what actually happens is that the total object gets stored in the comments array instead of the ObjectId of the comment.
I've almost gone through Stackoverflow seeking solution for my problem but failed.
I just wanted the ObjectId to be stored in the comments array instead it stores the whole Object which brings me problem in updating and deleting a comment. When I delete or update a comment the operation does happen in the Comments database but doesn't reflect in the Campgrounds database. Please help me with this issue. If anyone's taking the same course, Please give me solutions if you've experienced something like this already. The Schema are as given below
Campground Schema:
var mongoose = require("mongoose");
var campgroundSchema = mongoose.Schema({
campGroundName: String,
campGroundImage: String,
description: String,
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
],
addedBy: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
module.exports = mongoose.model("Campground", campgroundSchema);
Comment Schema:
var mongoose = require("mongoose");
var commentSchema = mongoose.Schema({
text: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
module.exports = mongoose.model("Comment", commentSchema);
Post request for creating a comment:
router.post("/", middleware.isLoggedIn, function(req, res) {
Comment.create(req.body.comment, function(err, createdComment) {
if(err) {
console.log(err);
} else {
createdComment.author.id = req.user._id;
createdComment.author.username = req.user.username;
createdComment.save(); Campground.findById(req.params.id).populate("comments").exec(function(err, foundCampground){
foundCampground.comments.push(createdComment);
foundCampground.save();
req.flash("success" , "Comment created successfully");
res.redirect("/index/" + req.params.id);
});
}
});
});
The whole source code is below,
https://1drv.ms/u/s!AmISAco3PGaPhQl_Riu8nroCom5h
Please help me fix this issue!
you have this line:
foundCampground.comments.push(createdComment)
which is telling mongodb to store the whole comment in the array.
it should be this instead:
foundCampground.comments.push(createdComment._id)
which will only push the id property of the comment into the array.
The version seemed to have a bug in it.
The problem was fixed when I updated it. Fixed version is 5.0.3

Mongoose - inserting subdocuments

I have a user model, and a log model. The log model is a subdocument of user model. So in my user model I have:
var mongoose = require('mongoose');
var Log = require('../models/log');
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [
Log
]
});
Then in my 'Log' model I have:
var mongoose = require('mongoose');
var logSchema = new mongoose.Schema({
logComment: {
type: String,
},
});
module.exports = mongoose.model('Log', logSchema);
So upon creation of a 'user', the 'logsHeld' always begins empty. I want to know how to add subdocuments to this user model.
I've tried doing this POST method:
router.post('/createNewLog', function(req, res) {
var user = new User ({
logssHeld: [{
logComment: req.body.logComment
}]
});
user.save(function(err) {
if(err) {
req.flash('error', 'Log was not added due to error');
return res.redirect('/home');
} else {
req.flash('success', 'Log was successfully added!');
return res.redirect('/home');
}
});
});
But this doesn't work. It also includes a 'new User' line, which I don't think I need given this would be for an existing user.
You need to use the logSchema instead of the Log model as your subdocument schema in User model. You can access the schema as follows:
var mongoose = require('mongoose');
/* access the Log schema via its Model.schema property */
var LogSchema = require('../models/log').schema; // <-- access the schema with this
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [LogSchema]
});
Picking up from your comments in another answer where you are facing another issue
WriteError({"code":11000,"index":0,"errmsg":"E11000 duplicate key
error index: testDB.users.$email_1 dup key:
you are getting this because there's already a document in your users collection that has most probably a null value on the email field. Even though your schema does not explicitly specify an email field, you may have an existing old and unused unique index on users.email.
You can confirm this with
testDB.users.getIndexes()
If that is the case and manually remove the unwanted index with
testDB.users.dropIndex(<index_name_as_specified_above>)
and carry on with the POST to see if that has rectified the error, I bet my $0.02 that there is an old unused unique index in your users collection which is the main issue.
Try using logSchema which references only the subdocument schema, Log refers to the entire contents of ../models/log
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [
logSchema
]
});
Documentation: http://mongoosejs.com/docs/subdocs.html
Try push to insert item in array in mongoose
var user = new User;
user.logssHeld.push({
logComment: req.body.logComment
});
user.save(function(err, doc) {
//DO whatever you want
});
see the docs here

Not able to persist array of objects in mongo using mongoose

I'm trying to persist an array of objects in a document using mongoose. I have tried multiple times but it's not persisting array in document. It places an empty array in document.
Following is my Schema:
var ProfileSchema = new Schema({
name: String,
PagesData: [{
pageAccessToken: {type: String, get: decryptText, set: encryptText},
category: String,
name: String,
id: String,
perms: [String]
}]
});
module.exports = mongoose.model('Profile', ProfileSchema);
I'm trying to save a document with an array of objects using following query:
var newProfile = new Profile();
newProfile.name = "someName";
newProfile.PagesData = [ { pageAccessToken: 'someToken',
category: 'Bags/Luggage',
name: 'someBrandName',
id: '12345',
perms:
[ 'ADMINISTER',
'EDIT_PROFILE',
'CREATE_CONTENT' ] } ];
newProfile.save(function(err, result, numAffected){
if(err) {
console.log(err);
res.send(500, "Error");
}
console.log(result);
res.send(200, "Success");
});
I tried debugging the mongo commands using
require('mongoose').set('debug', true)
On Debug logs it shows, empty array during insert command execution.
Can anyone please tell me how can I store this array of object in my schema ?
Thanks,
Update:
It's been too long and I'm still not able to figure out the root cause of the problem. There is a long thread going on github for this.
https://github.com/Automattic/mongoose/issues/3249
I would like other experts to please take a look and suggest me some way by which I can solve the issue. I'm really stuck at this.
Update 2:
None of the solution worked for me so far, so I decided to modify the schema only to meet my requirements. This resulted in a different problem:
I want to create a map with a objectId as key and an array of string values as its value. The closest that I can get is:
var schema = new Schema({
map: [{myId: {type:mongoose.Schema.Types.ObjectId, ref: 'MyOtherCollection'}, values: [String]}]
});
But somehow this is not working for me. When I perform an update with {upsert: true}, it is not correctly populating the key: value in the map. In fact, I'm not even sure if I have declared the schema correctly.
Can anyone tell me if the schema is correct ? Also, How can I perform an update with {upsert: true} for this schema?
Also, if above is not correct and can;t be achieved then how can I model my requirement by some other way. My use case is I want to keep a list of values for a given objectId. I don't want any duplicates entries with same key, that's why picked map.
Please suggest if the approach is correct or should this be modelled some other way?
Thanks
I tried the exact code you have provided here and it's working for me. I am not sure what is causing the issue for you. Until and unless we get the same issue, it's very difficult to rectify it.
Here are few suggestions which you might try:
Create a simple schema and try storing the object, that way you can
figure it out if it has to do something with the schema.
You can try out your schema in a sample app to find if some
dependency is causing the problem.
Once you know where exactly the problem is, you would be able to figure out a solution too. I hope it helps.
I tested this and the insert works for me using the below:
(I had to remove the get: decryptText, set: encryptText)
var n = { name: "Testing for mongoose", PagesData : [{ pageAccessToken: 'someToken',
category: 'Bags/Luggage',
name: 'someBrandName',
id: '12345',
perms:
[ 'ADMINISTER',
'EDIT_PROFILE',
'CREATE_CONTENT' ] } ] }
Profile.create(n, function (err) {
if (!err) {
return 'records saved successfully';
}
else {
return error on save:' + err;
}
});
To create multiple pageDatas you can use it as an embedded collection instead of using arrays.
The Schema will be as follows:
var PagesDataSchema = new Scheme({
pageAccessToken: {type: String, get: decryptText, set: encryptText},
category: String,
name: String,
id: String,
perms: [String]
})
var ProfileSchema = new Schema({
name: String,
PagesData: [PagesDataSchema]
});
module.exports = mongoose.model('Profile', ProfileSchema);
Reference: http://mongoosejs.com/docs/subdocs.html
For Saving the document you can use like.
exports.save = function(req,res){
var test = new ProfileSchema; // new object for ProfileSchema domain.
test.name= req.body.name;
if(req.body.PagesData){
req.body.PagesData.forEach(function(page){ // For every element of pageData from client.
test.PagesData.push(page) // This pushes each and every pagedata given from the client into PagesData.
})
}
test.save(function (saveErr, saved) { // Saves the new document into db.
if (saveErr) {
console.log(saveErr)
return;
}
res.status(HttpStatus.OK).json(saved);
});
};
Hope this helps.
Have you tried
Profile.create({
name: "someName",
PagesData: [
{
pageAccessToken: 'someToken',
category: 'Bags/Luggage',
name: 'someBrandName',
id: '12345',
perms: [
'ADMINISTER',
'EDIT_PROFILE',
'CREATE_CONTENT'
]
}
]
}, function(err, profile) {
// do your stuff
})
?

Why Mongoose Populate Required?

Using mongoose populate:
http://mongoosejs.com/docs/populate.html
It seams that mongoose is forcing me to declare a ref value for populate when I first create the document but in my case i don't have the ref info yet. When I try to create a new document while providing an empty string I get to my developer field I get:
{"message":"Cast to ObjectId failed for value \"\" at path \"developer\"","name":"CastError","type":"ObjectId","value":"","path":"developer"}
Object that I'm saving through mongoose:
var Project = {
name: 'Coolproject',
status: 'pending',
developer: '',
type: 'basic',
};
Project.create(req.body, function(err, project) {
if(err) { return handleError(res, err); }
return
});
My Model:
var ProjectSchema = new Schema({
name: String,
status: {type:String, default:'pending'},
developer:{type: Schema.Types.ObjectId, ref: 'User'},
type:String
});
Basically I need to set it later, but it doesn't seam like this is possible. Currently my work around is populate it with a dummy user until later but this is less than desirable.
Thoughts?
Update
Realized that if i provide a object id like value (55132a418b3cde5546b01b37) it lets me save the document. Very odd. Guess it just figured it can find the document moves on. Wondering why this doesn't happen for a blank value.
The problem is explained in the error message. You cannot save an Empty String in the place of an ObjectId. The field is not listed as 'required', so there is no problem leaving it out and saving the document.
Code correction:
// you can save this
var Project = {
name: 'Coolproject',
status: 'pending',
type: 'basic',
};
You need to use the sparse index in model.
So, the valid model can have developer equal to nil
var ProjectSchema = new Schema({
name: String,
status: {type:String, default:'pending'},
developer:{type: Schema.Types.ObjectId, ref: 'User', sparse:true},
type:String
});
See
http://mongoosejs.com/docs/api.html#schematype_SchemaType-sparse and
http://docs.mongodb.org/manual/core/index-sparse/
for additional info

Resources