NodeJS with mongoose: E11000 duplicate key error collection - node.js

This is the story: new to NodeJS > trying to seed a mongoDB database using mongoose > need help.
I have two collections: Users (or players) and Teams.
Seed goal: to create some Users and then create some Teams, populated with some of those Users.
Here is my User schema:
var mongoose = require('mongoose');
var passportLocalMongoose = require('passport-local-mongoose');
var UserSchema = new mongoose.Schema({
name: String,
username: String,
password: String
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
Here is my Team schema:
var mongoose = require('mongoose');
var passportLocalMongoose = require('passport-local-mongoose');
var TeamSchema = new mongoose.Schema({
name: String,
users: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
]
});
TeamSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("Team", TeamSchema);
And this is the seeding attempt:
var mongoose = require('mongoose');
function seed(collectionName, data, schema){
schema.remove({}, function(err){
if(err){
console.log(collectionName + ".remove: " + err);
} else {
console.log("All " + collectionName + " collection removed");
data.forEach(function(dt){
schema.create(dt, function(err, newData){
if(err){
console.log(collectionName + ".create: " + err);
} else {
console.log(collectionName + " added: " + newData.name);
}
});
});
}
});
}
function seedDB(){
var userData = require('./data/userData'),
userSchema = require('../../models/user');
seed("User", userData, userSchema);
var teamData = require('./data/teamData'),
teamSchema = require('../../models/team');
seed("Team", teamData, teamSchema);
};
module.exports = seedDB;
Yes, I tried to encapsulate the seed function, so I could use it in every collection I would possibly want to seed (in fact, I believe this is the problem).
userData:
module.exports = [
{
name: "Name1",
username: "nickname1",
password: "pass"
},
{
name: "Name2",
username: "nickname2",
password: "pass"
}
];
teamData:
module.exports = [
{
name: "Team1",
users: [
{ "_id" : "594497aa0a403b183ce7e485", "name" : "Name1", "username" : "nickname1", "password" : "pass", "__v" : 0 },
{ "_id" : "594497aa0a403b183ce7e486", "name" : "Name2", "username" : "nickname2", "password" : "pass", "__v" : 0 }
]
},
{
name: "Team 2",
users: [
{ "_id" : "594497aa0a403b183ce7e485", "name" : "Name1", "username" : "nickname1", "password" : "pass", "__v" : 0 }
]
}
];
When I run seed(), I get this error message:
Team.create: WriteError({"code":11000,"index":0,"errmsg":"E11000 duplicate key error collection: pastinha2.teams index: username_1 dup key: { : null }","op":{"name":"Team 2","_id":"5944afd68157e62190ec1919","users":["594497aa0a403b183ce7e485"],"__v":0}})
As far as I could understand, the problem is the order things are being executed, since the query calls are asynchronous. Not sure if this is really the issue, but if it is, how can I prevent it from happening?
Sorry for the long post, but I found several threads with similar problems, and I'm still really stuck here. Could use some help.
Thanks in advance.

you should use Seedgoose instead. It supports smart references. In this case, it won't cause conflict error for you.

Related

Why is MongoDB creating indexes for fields that don't exist within that schema?

TL;DR: I am having trouble with my mongo database seemingly creating indexes for my boards collection from fields from my users collection which is causing E11000 errors when I try to create a new board.
I am building a kanban board (like Jira) and have board, task and user collections (entity relationship diagram : https://imgur.com/a/Nu6Eg91). All of the collections work fine following a .dropIndexes(). However, when I have been working with tasks via the UI on one board, when I try to create another board I get this E11000 error:
{ MongoError: insertDocument :: caused by :: 11000 E11000 duplicate key error index: kanban.boards.$username_1 dup key: { : null }
at Function.create (/home/ubuntu/workspace/kanban v1.0/node_modules/mongodb-core/lib/error.js:43:12)
at toError (/home/ubuntu/workspace/kanban v1.0/node_modules/mongodb/lib/utils.js:149:22)
at coll.s.topology.insert (/home/ubuntu/workspace/kanban v1.0/node_modules/mongodb/lib/operations/collection_ops.js:859:39)
at /home/ubuntu/workspace/kanban v1.0/node_modules/mongodb-core/lib/connection/pool.js:532:18
at _combinedTickCallback (internal/process/next_tick.js:73:7)
at process._tickCallback (internal/process/next_tick.js:104:9)
driver: true,
name: 'MongoError',
index: 0,
code: 11000,
errmsg: 'insertDocument :: caused by :: 11000 E11000 duplicate key error index: kanban.boards.$username_1 dup key: { : null }' }
This appears to be because of a null value in the username field in the board schema but there is no such field in this model as can be seen below:
// Board Schema
// Config
var mongoose = require("mongoose"),
passportLocalMongoose = require("passport-local-mongoose");
// Create Board Schema
var BoardSchema = new mongoose.Schema({
user: {type: mongoose.Schema.Types.ObjectId, ref: "User", unique: true, sparse: true},
todo: [{type: mongoose.Schema.Types.ObjectId, ref: "Task"}],
inProgress: [{type: mongoose.Schema.Types.ObjectId, ref: "Task"}],
testing: [{type: mongoose.Schema.Types.ObjectId, ref: "Task"}],
completed: [{type: mongoose.Schema.Types.ObjectId, ref: "Task"}]
});
// Validation
BoardSchema.plugin(passportLocalMongoose);
// Export Model
module.exports = mongoose.model("Board", BoardSchema);
Here is an example board in the db:
{ "_id" : ObjectId("5c65767c8977670a2366ffe5"),
"todo" : [ ObjectId("5c657a1e451bc80ac315ef35"), ObjectId("5c657a4a451bc80ac315ef37"), ObjectId("5c657a76451bc80ac315ef39") ],
"inProgress" : [ ObjectId("5c6579e1451bc80ac315ef31"), ObjectId("5c657a03451bc80ac315ef33"), ObjectId("5c657a3a451bc80ac315ef36"), ObjectId("5c657a8a451bc80ac315ef3a") ],
"testing" : [ ObjectId("5c657a9a451bc80ac315ef3b") ],
"completed" : [ ObjectId("5c657a60451bc80ac315ef38"), ObjectId("5c65bb8f6f5b731aec27b4a7"), ObjectId("5c65bba06f5b731aec27b4a8"), ObjectId("5c657a11451bc80ac315ef34") ],
"user" : ObjectId("5c65767b8977670a2366ffe4"),
"__v" : 51 }
For some reason, I think a username index has been created by the database for the boards schema. The indexes for the boards collection are below:
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "kanban.boards"
},
{
"v" : 1,
"key" : {
"user" : 1
},
"name" : "user_1",
"ns" : "kanban.boards",
"background" : true
},
{
"v" : 1,
"unique" : true,
"key" : {
"username" : 1
},
"name" : "username_1",
"ns" : "kanban.boards",
"background" : true
}
]
For completeness, here is the user schema:
// User Schema
// Config
var mongoose = require("mongoose"),
uniqueValidator = require("mongoose-unique-validator"),
passportLocalMongoose = require("passport-local-mongoose");
// Create User Schema
var UserSchema = new mongoose.Schema({
username: {type: String, required: true, unique: true},
password: String
});
// Validation and Hash/Salt PW
UserSchema.plugin(uniqueValidator, {message: "A user is already registered with that {PATH}."});
UserSchema.plugin(passportLocalMongoose);
// Export Model
module.exports = mongoose.model("User", UserSchema);
The code causing the error is in the signup route, part of which is shown below:
// create new board for user
var newBoard = {
user: user._id,
todo: [],
inProgress: [],
testing: [],
review: [],
completed: [],
};
Board.create(newBoard, function(err, board) {
if(err) {
console.log(err);
req.flash("error", "Uh oh! Something went wrong.");
return res.redirect("/");
}
// authenticate user
passport.authenticate("local")(req, res, function() {
req.flash("success", "Welcome to your Kanban board, " + user.username + ".");
return res.redirect("/board");
});
});
I don't understand why this index is being created (or indeed if this is what is causing the E11000 errors but I'm pretty sure it is). Apologies for the spam of code, I am quite inexperienced with using mongo and so don't know what is relevant and what is not. I have built mongo databases with multiple collections before but not with a link collection as I have here so I can't work out what is going wrong. Please let me know if I have missed anything that is useful or important. Thanks.
I'm not 100% sure if this is the issue, but you should remove the unique: true and sparse: true from the BoardSchema.user parameter. Since you declare those in the UserSchema, you shouldn't also need them in the BoardSchema.
EDIT: You might also have to manually delete the index after completing this first step.
The problem was that I was plugging passport-local-mongoose into the board schema when that should only be on the user schema.

How to populate documents with unlimited nested levels using mongoose

I'm designing a web application that manages organizational structure for parent and child companies. There are two types of companies: 1- Main company, 2 -Subsidiary company.The company can belong only to one company but can have a few child companies. My mongoose Schema looks like this:
var companySchema = new mongoose.Schema({
companyName: {
type: String,
required: true
},
estimatedAnnualEarnings: {
type: Number,
required: true
},
companyChildren: [{type: mongoose.Schema.Types.ObjectId, ref: 'Company'}],
companyType: {type: String, enum: ['Main', 'Subsidiary']}
})
module.exports = mongoose.model('Company', companySchema);
I store all my companies in one collection and each company has an array with references to its child companies. Then I want to display all companies as a tree(on client side). I want query all Main companies that populates their children and children populate their children and so on,with unlimited nesting level. How can I do that? Or maybe you know better approach. Also I need ability to view,add,edit,delete any company.
Now I have this:
router.get('/companies', function(req, res) {
Company.find({companyType: 'Main'}).populate({path: 'companyChildren'}).exec(function(err, list) {
if(err) {
console.log(err);
} else {
res.send(list);
}
})
});
But it populates only one nested level.
I appreciate any help
You can do this in latest Mongoose releases. No plugins required:
const async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
const uri = 'mongodb://localhost/test',
options = { use: MongoClient };
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
function autoPopulateSubs(next) {
this.populate('subs');
next();
}
const companySchema = new Schema({
name: String,
subs: [{ type: Schema.Types.ObjectId, ref: 'Company' }]
});
companySchema
.pre('findOne', autoPopulateSubs)
.pre('find', autoPopulateSubs);
const Company = mongoose.model('Company', companySchema);
function log(data) {
console.log(JSON.stringify(data, undefined, 2))
}
async.series(
[
(callback) => mongoose.connect(uri,options,callback),
(callback) =>
async.each(mongoose.models,(model,callback) =>
model.remove({},callback),callback),
(callback) =>
async.waterfall(
[5,4,3,2,1].map( name =>
( name === 5 ) ?
(callback) => Company.create({ name },callback) :
(child,callback) =>
Company.create({ name, subs: [child] },callback)
),
callback
),
(callback) =>
Company.findOne({ name: 1 })
.exec((err,company) => {
if (err) callback(err);
log(company);
callback();
})
],
(err) => {
if (err) throw err;
mongoose.disconnect();
}
)
Or a more modern Promise version with async/await:
const mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.set('debug',true);
mongoose.Promise = global.Promise;
const uri = 'mongodb://localhost/test',
options = { useMongoClient: true };
const companySchema = new Schema({
name: String,
subs: [{ type: Schema.Types.ObjectId, ref: 'Company' }]
});
function autoPopulateSubs(next) {
this.populate('subs');
next();
}
companySchema
.pre('findOne', autoPopulateSubs)
.pre('find', autoPopulateSubs);
const Company = mongoose.model('Company', companySchema);
function log(data) {
console.log(JSON.stringify(data, undefined, 2))
}
(async function() {
try {
const conn = await mongoose.connect(uri,options);
// Clean data
await Promise.all(
Object.keys(conn.models).map(m => conn.models[m].remove({}))
);
// Create data
await [5,4,3,2,1].reduce((acc,name) =>
(name === 5) ? acc.then( () => Company.create({ name }) )
: acc.then( child => Company.create({ name, subs: [child] }) ),
Promise.resolve()
);
// Fetch and populate
let company = await Company.findOne({ name: 1 });
log(company);
} catch(e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})()
Produces:
{
"_id": "595f7a773b80d3114d236a8b",
"name": "1",
"__v": 0,
"subs": [
{
"_id": "595f7a773b80d3114d236a8a",
"name": "2",
"__v": 0,
"subs": [
{
"_id": "595f7a773b80d3114d236a89",
"name": "3",
"__v": 0,
"subs": [
{
"_id": "595f7a773b80d3114d236a88",
"name": "4",
"__v": 0,
"subs": [
{
"_id": "595f7a773b80d3114d236a87",
"name": "5",
"__v": 0,
"subs": []
}
]
}
]
}
]
}
]
}
Note that the async parts are not actually required at all and are just here for setting up the data for demonstration. It's the .pre() hooks that allow this to actually happen as we "chain" each .populate() which actually calls either .find() or .findOne() under the hood to another .populate() call.
So this:
function autoPopulateSubs(next) {
this.populate('subs');
next();
}
Is the part being invoked that is actually doing the work.
All done with "middleware hooks".
Data State
To make it clear, this is the data in the collection which is set up. It's just references pointing to each subsidiary in plain flat documents:
{
"_id" : ObjectId("595f7a773b80d3114d236a87"),
"name" : "5",
"subs" : [ ],
"__v" : 0
}
{
"_id" : ObjectId("595f7a773b80d3114d236a88"),
"name" : "4",
"subs" : [
ObjectId("595f7a773b80d3114d236a87")
],
"__v" : 0
}
{
"_id" : ObjectId("595f7a773b80d3114d236a89"),
"name" : "3",
"subs" : [
ObjectId("595f7a773b80d3114d236a88")
],
"__v" : 0
}
{
"_id" : ObjectId("595f7a773b80d3114d236a8a"),
"name" : "2",
"subs" : [
ObjectId("595f7a773b80d3114d236a89")
],
"__v" : 0
}
{
"_id" : ObjectId("595f7a773b80d3114d236a8b"),
"name" : "1",
"subs" : [
ObjectId("595f7a773b80d3114d236a8a")
],
"__v" : 0
}
I think a simpler approach would be to track the parent since that is unique instead of tracking an array of children which could get messy. There is a nifty module called mongoose-tree built just for this:
var tree = require('mongoose-tree');
var CompanySchema = new mongoose.Schema({
companyName: {
type: String,
required: true
},
estimatedAnnualEarnings: {
type: Number,
required: true
},
companyType: {type: String, enum: ['Main', 'Subsidiary']}
})
CompanySchema.plugin(tree);
module.exports = mongoose.model('Company', CompanySchema);
Set some test data:
var comp1 = new CompanySchema({name:'Company 1'});
var comp2 = new CompanySchema({name:'Company 2'});
var comp3 = new CompanySchema({name:'Company 3'});
comp3.parent = comp2;
comp2.parent = comp1;
comp1.save(function() {
comp2.save(function() {
comp3.save();
});
});
Then use mongoose-tree to build a function that can get either the ancestors or children:
router.get('/company/:name/:action', function(req, res) {
var name = req.params.name;
var action = req.params.action;
Company.find({name: name}, function(err, comp){
//typical error handling omitted for brevity
if (action == 'ancestors'){
comp.getAncestors(function(err, companies) {
// companies is an array
res.send(companies);
});
}else if (action == 'children'){
comp.getChildren(function(err, companies) {
res.send(companies);
});
}
});
});

Populating in array of object ids

My schema:-
var playlistSchema = new Schema({
name : {type:String,require:true},
videos : {type:[mongoose.Schema.Types.ObjectId],ref: 'Video'},
},{collection:'playlist'})
var Playlist = mongoose.model('Playlist',playlistSchema);
I have some data in the database as exapmple:-
{
"_id" : ObjectId("58d373ce66fe2d0898e724fc"),
"name" : "playlist1",
"videos" : [
ObjectId("58d2189762a1b8117401e3e2"),
ObjectId("58d217e491089a1164a2441f"),
ObjectId("58d2191062a1b8117401e3e4"),
ObjectId("58d217e491089a1164a24421")
],
"__v" : 0
}
And the schema of Video is :-
var videoSchema = new Schema({
name : {type:String,required:true},
createdAt : {type:String,default:new Date()},
isDisabled : {type:Boolean,default:false},
album : {type: mongoose.Schema.Types.ObjectId, ref: 'Album'}
},{collection:'video'})
var Video = mongoose.model('Video',videoSchema);
Now in order to get the name of the all the videos in playlist i am trying the code:-
var playlistModel = mongoose.model('Playlist');
let searchParam = {};
searchParam._id = req.params.pid;
playlistModel.findOne(searchParam)
.populate('[videos]')
.exec(function(err,found){
if(err)
throw err;
else{
console.log(found.videos[0].name);
}
})
But here i am getting the undefined result.I am not getting where i am wrong plzz anyone help me to short out this problem.
got the answer:-
just change the schema
var playlistSchema = new Schema({
name : {type:String,require:true},
videos : [{type:mongoose.Schema.Types.ObjectId,ref: 'Video'}],
},{collection:'playlist'})
var Playlist = mongoose.model('Playlist',playlistSchema);
and just use
.populate('videos')
instead of
.populate('[videos]')
const app = await this.appModel.findOne({ slug })
.populate({
path: 'category',
populate: {
path: 'subCategories',
model: 'Category',
select: { name: 1, slug: 1, _id: 0 }
},
});

MongoDB updating embedded document isn't working

I'm trying to update embedded document, but it is not working. This is what documents look like:
{
"_id" : ObjectId("577c71735d35de6371388efc"),
"category" : "A",
"title" : "Test",
"content" : "Test",
"tags" : "test",
"comments" : [
{
"_id" : ObjectId("57811681010bd12923eda0ca"),
"author" : "creator",
"email" : "creator#example.com",
"text" : "helloworld!"
},
{
"_id" : ObjectId("57811b17b667676126bde94e"),
"author" : "creator",
"email" : "creator#example.com",
"text" : "helloworld2!"
}
],
"createdAt" : ...,
"updatedAt" : ...
}
you can see the comments field is embedded document that contains comments. I want to update specific comment, so I made query like this(node.js):
db.update('posts', {
_id: new ObjectID(postId), // ID of the post
comments: {
$elemMatch: {
_id: new ObjectId(commentId)
}
}
}, {
$set: {
"comments.$.author": newComment.author,
"comments.$.email": newComment.email,
"comments.$.text": newComment.text,
"comments.$.updatedAt": new Date()
}
}) ...
when I run this query, no error was shown but update wasn't applied. I tried this query too:
{
_id: new ObjectId(postId),
"comments._id": new ObjectId(commentId)
}
but not worked either. Am I missing something? I'm using Mongo v3.2.7.
Please try the below code. I think the "ObjectId" (i.e. case) should be the problem. Just check how you defined the object id and keep it consistent in the two places that you have used (i.e. posts _id and comments _id -> both places).
ObjectID = require('mongodb').ObjectID
The below code works fine for me. Basically, your query seems to be correct.
var Db = require('mongodb').Db, MongoClient = require('mongodb').MongoClient, Server = require('mongodb').Server, ReplSetServers = require('mongodb').ReplSetServers, ObjectID = require('mongodb').ObjectID, Binary = require('mongodb').Binary, GridStore = require('mongodb').GridStore, Grid = require('mongodb').Grid, Code = require('mongodb').Code, assert = require('assert');
var db = new Db('localhost', new Server('localhost', 27017));
db.open(function(err, db) {
var collection = db.collection("posts");
var postId = '577c71735d35de6371388efc';
var commentId = '57811681010bd12923eda0ca';
var query = {
_id : new ObjectID(postId),
comments : {
$elemMatch : {
_id : new ObjectID(commentId)
}
}
};
collection.update(query, {
$set : {
"comments.$.author" : "new author",
"comments.$.email" : "newemail#gmail.com",
"comments.$.text" : "new email updated",
"comments.$.updatedAt" : new Date()
}
}, {
multi : false
}, function(err, item) {
assert.equal(null, err);
console.log("comments updated ..." + JSON.stringify(item));
});
});

How to update array in mongodb using mongoose

when i try to update it dose not throw back any error it goes OK but when i check my datebase nothing i their updated nothing is modified pls help
this is my db
{
"_id" : ObjectId("56651f0e4905bd041cad0413"),
"creator" : ObjectId("566299dd17990464160ae27a"),
"content" : "this is my joke 2",
"created" : ISODate("2015-12-07T05:54:22.858Z"),
"__v" : 15,
"comments" : [
{
"posteruserId" : "5665e6867185d87c1e71dbdc",
"postedBy" : "lawrence nwoko",
"postterscomment" : "good joke",
"_id" : ObjectId("56660745f644c2501116acce")
},
{
"posteruserId" : "5665e6867185d87c1e71dbdc",
"postedBy" : "lawrence nwoko",
"postterscomment" : "good joke",
"_id" : ObjectId("56660b6d33c245c012104fdc")
}
]
}
this is my schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var JokesSchema = new Schema({
creator: {type: Schema.Types.ObjectId, ref: 'User'},
content: String,
created:{type:Date, default: Date.now},
comments: [{
text: String,
postedBy: String,
posteruserId :String,
date: String,
postterscomment:String
}]
})
module.exports = mongoose.model('Jokes_db', JokesSchema)
here i my post funtion
api.post('/update', function(req, res) {
// Joke.findById("56651f0e4905bd041cad0413", function (err, meeting) {
Joke.update({_id: "5665e6867185d87c1e71dbdc", 'comments._id' : "56660745f644c2501116acce"},
{'$set': {
'comments.$.postterscomment': "working"
}},
function(err, numAffected) {
if(err){
console.log(err)
}else{
res.json(numAffected)
}
}
);
});
It has been three days of trying to fix this problem but by his grace I have done it without help the problem user that I was not using the right id to make the query thanks for your help guys I hope this helps another user
api.post('/editecomments', function(req, res) {
Joke.update({_id: "56651f0e4905bd041cad0413", 'comments._id' : "56660745f644c2501116acce"},
{'$set': {'comments.$.postterscomment': 'working'}},
function(err, numAffected) {
if(err){
console.log(err)
}else{
res.json(numAffected)
}
}
);
});

Resources