This question already has answers here:
E11000 duplicate key error index in mongodb mongoose
(22 answers)
Closed 4 years ago.
I am trying to create a blog. I have a user schema, a blog schema, and a comment schema.
When I register a user and create a blog, it works (and saves fine to the database). When I create another user and that user tries to write a blog, I get returned a large error message:
BulkWriteError: E11000 duplicate key error collection: blog.blogs index: username_1 dup key: { : null }
The problem is - there is no key in any of my schema's called username_1. here are my schema's:
var UserSchema = new mongoose.Schema({
firstname: String,
lastname: String,
username: {
type: String,
unique: true
},
email: String,
createdDate: {
type: Date,
default: Date.now()
},
blogs: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Blog"
}
]
});
Blog schema
var BlogSchema = new mongoose.Schema({
title: String,
text: String,
date: {
type: Date,
default: Date.now()
},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
],
author: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
});
The post route is this in case you want to know:
// create a new blog object
var newBlog = new Blog(
{
title : req.body.title,
text : req.body.text,
author: foundUser._id
}
);
// create the new blog
Blog.create(newBlog, function(err, createdBlog) {
if(err) {
console.log(err);
} else {
// push the new blog into the blogs array
foundUser.blogs.push(createdBlog);
// save to db
foundUser.save();
}
});
Actually, I think there is a unique key in your schema.
username: {
type: String,
unique: true
},
Maybe you can try to go though the whole thing with different usernames?
Related
I have a user model schema, a work model schema, and a critique model schema. The relationship between these schema's is a user can submit many works (like blog posts), and can comment/review (which we call critiques) other people's posts (works).
So when a user submits a critique (think of it like a review), this is my post route. I find the work by the id, then create a new critique model object, and pass that to the .create() mongoose function. All goes seemingly well until I hit the foundWork.critiques.push(createdCritique) line. the console log errors out saying:
BulkWriteError: E11000 duplicate key error collection: zapper.critiques index: username_1 dup key: { : null }
Obviously, it is saying that there are two username keys in the objects and they're conflicting with each other, but I'm not familiar enough with this to find the root of the issue and fix it in the mongoose models. The models are below. If anyone could help, that'd be greatly appreciated.
// post route for getting the review
router.post('/:id', isLoggedIn, function(req, res) {
Work.findById(req.params.id, function(err, foundWork) {
if (err) {
console.log(err);
} else {
// create a new critique
var newCritique = new Critique ({
reviewerName: {
id: req.user._id,
username: req.user.username
},
work: {
id: foundWork._id,
title: foundWork.title
},
critique : req.body.critique,
date: Date.now(),
rating: 0
});
// save new critique to db
Critique.create(newCritique, function(err, createdCritique) {
if (err) {
console.log(err)
} else {
console.log("Created critique is ");
console.log(createdCritique);
// push the new critique into array of critiques of the work
foundWork.critiques.push(createdCritique);
// save to db
foundWork.save();
}
});
}
});
User model:
var mongoose = require('mongoose');
var passportLocalMongoose = require('passport-local-mongoose');
var UserSchema = new mongoose.Schema({
firstname: String,
lastname: String,
username: String,
password: String,
email: String,
zip: String,
bio: {
type: String,
default: ''
},
influences: {
type: String,
default: ''
},
favBooks: {
type: String,
default: ''
},
notWriting: {
type: String,
default: ''
},
favHero: {
type: String,
default: ''
},
favVillain: {
type: String,
default: ''
},
works: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Work'
}
],
critiques: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Critique'
}
],
friends: [
{
friendId: String,
friendName : String,
friendPic: String
}
],
friendRequests: [
{
sendingFriendId: String,
sendingFriendName : String,
sendingFriendPic: String
}
],
createdDate: {
type: Date,
default: Date.now
},
lastLogin: {
type: Date,
default: Date.now
}
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
Work model:
var mongoose = require('mongoose');
var WorkSchema = new mongoose.Schema({
title: String,
genre: String,
workType: String,
length: Number,
ageRange: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
manuscriptText: String,
critiques: [
{
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Critique"
}
}
],
ratingNumber: [Number],
ratingSum: {
type: Number,
default: 0
},
date: {
type: Date,
default: Date.now
},
isPublic: {
type: Boolean,
default: true
}
});
module.exports = mongoose.model("Work", WorkSchema);
Critique model:
var mongoose = require('mongoose');
var passportLocalMongoose = require('passport-local-mongoose');
var CritiqueSchema = new mongoose.Schema({
reviewerName: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
work: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Work"
},
title: String
},
critique: String,
date: {
type: Date,
default: Date.now
},
rating: [Number]
});
CritiqueSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("Critique", CritiqueSchema);
When you create a unique index in MongoDB, the default behavior is that it will index null values also.
This means if you have a document in your collection with a username of null, you can not add another one with a username of null.
What you need is a sparse index which only indexes actual values (and ignores documents with null for that field).
Check this link It shows how to create a sparse index vs "normal" one in mongoose (index: true, vs spare: true). Most of the time you would want sparse indexes.
Here is my schema:
/** Schemas */
var profile = Schema({
EmailAddress: String,
FirstName: String,
LastName: String,
BusinessName: String
});
var convSchema = Schema({
name: String,
users: [{
type: Schema.Types.ObjectId,
ref: 'Profiles'
}],
conversationType: {
type: String,
enum: ['single', 'group'],
default: 'single'
},
created: {
type: Date,
default: Date.now
},
lastUpdated: {
type: Date,
default: Date.now
}
});
/** Models */
db.Profiles = mongoose.model('Profiles', profile);
db.Conversations = mongoose.model('ChatConversations', convSchema);
module.exports = db;
Then I try to populate Users using following code (http://mongoosejs.com/docs/populate.html):
db.Conversations.find(query).populate('users').exec(function (err, records) {
console.log(records);
});
This is returning records but users array as a blank array [].
I also tried the other way around (http://mongoosejs.com/docs/api.html#model_Model.populate):
db.Conversations.find(query, function (err, records) {
db.Conversations.populate(records, {path: "users", select: "BusinessName"}, function (err, records) {
console.log(records);
});
});
Results are same. When I checked references into profile collection records are there.
Any idea what wrong here?
I got it working by renaming model (the 3rd arguement):
mongoose.model( "Profiles", profile, "Profiles" );
The issue was Mongoose was searching for profiles collection but its there as Profiles in database. So I renamed it to Profiles to match the exact name.
Phewww! Thanks to me.
I have architectural question about how to design my meanjs controller and routes for mongoose subdocuments.
my model looks as following:
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
/**
* Customerrpc Schema
*/
var CustomerrcpSchema = new Schema({
company: {
type: String,
enum: ['Option1', 'Option2'],
required: 'Please fill company name'
},
rcp: {
type: String,
required: 'Please fill rcp'
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
/**
* Customer Schema
*/
var CustomerSchema = new Schema({
name: {
type: String,
default: '',
required: 'Please fill Customer name',
trim: true
},
description: {
type: String,
default: '',
//required: 'Please fill Customer description',
trim: true
},
url: {
type: String,
default: '',
//required: 'Please fill Customer url',
trim: true
},
rcp: [CustomerrcpSchema],
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Customer', CustomerSchema);
mongoose.model('Customerrcp', CustomerrcpSchema);
I tried it out by adding on the server controller the following code during the create methode:
exports.create = function(req, res) {
var customer = new Customer(req.body);
customer.user = req.user;
var rcp = new Customerrcp({
company: 'Option1',
rcp: 'dumm',
user: req.user
});
customer.rcp = rcp;
customer.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(customer);
}
});
};
This works perfectly fine. Now my question is what is the best procedure to create / modify / remove a subdocument from the maindocument?
I thought of always work with the main document 'Customer' but this brings several issues with it that i dont like, like saving always the hole document. Since i do have a uniq _id for each subdocument i guess there must be a better way.
What i would like to have is a controller only for the subdocument with the create / save / remove statement for it. Is this even possible?
As far as i understand it:
to create a new subdocument i need the following: _id of the main document for the mongoose query. So i need a service which would handover the _id of the maindocument to the controller of the selected subdocument. I was able to do this.
But im insecure if this is the proper way.
Any Ideas?
Cheers,
Michael
This question already has answers here:
Querying after populate in Mongoose
(6 answers)
Closed 6 years ago.
Mongo is driving me crazy. I have these schemas:
var userSchema = new Schema({
username: {
type: String,
require: true,
unique: true
},
password: {
type: String,
require: true,
}
});
var User = mongoose.model('user', userSchema);
var deviceSchema = new Schema({
name: {
type: String,
require: true,
unique: true
},
type: String,
lastvalue: {
type: Schema.ObjectId,
ref: 'event'
},
owner: {
type: Schema.ObjectId,
ref: 'user',
require: true
},
apiKey: {
type: String,
unique: true,
default: function() {
return crypto.randomBytes(64).toString('hex');
}
}
});
var Device = mongoose.model('device', deviceSchema);
and I want to retrieve all devices of a user. So:
Device.find({'owner.username': req.params.username})
But it returns an empty array!
I've also tried:
Device.find({'owner.username': req.params.username}, {'owner':1})
Device.find({'owner.username': req.params.username}, {'owner.$':1)
Without any luck...
I've searched the whole internet but I can't find anything useful!
You need to get the associated user _id first then query the Device model on the owner field using the _id from the first user query. The following shows this approach:
User.findOne({ "username" : req.params.username }),
.exec(function(err, user) {
if (err) throw err;
Device
.find({"owner": user._id})
.populate('owner', 'username') // only return the username
.exec(function(err, devices) {
if (err) throw err;
console.log(JSON.stringify(devices, undefined, 4));
});
}
);
can someone please help me with population of this schema? I need to populate array of Staff by their userId.
var PlaceSchema = new Schema ({
name: { type: String, required: true, trim: true },
permalink: { type: String },
country: { type: String, required: true },
...long story :D...
staff: [staffSchema],
admins: [adminSchema],
masterPlace:{ type: Boolean },
images: []
});
var staffSchema = new Schema ({
userId: { type: Schema.Types.ObjectId, ref: 'Account' },
role: { type: Number }
});
var adminSchema = new Schema ({
userId: { type: Schema.Types.ObjectId, ref: 'Account'}
})
var Places = mongoose.model('Places', PlaceSchema);
I tried to use this query, but without success.
Places.findOne({'_id' : placeId}).populate('staff.userId').exec(function(err, doc){
console.log(doc);
});
Polpulation is intended as a method for "pulling in" information from the related models in the collection. So rather than specifying a related field "directly", instead reference the related fields so the document appears to have all of those sub-documents embedded in the response:
Places.findOne({'_id' : placeId}).populate('staff','_id')
.exec(function(err, doc){
console.log(doc);
});
The second argument just returns the field that you want. So it "filters" the response.
There is more information on populate in the documentation.