MongoError: E11000 duplicate key error collection with passport - javascript

I have the following userSchema
var mongoose = require("mongoose"),
passportLocalMongoose = require("passport-local-mongoose");
var userSchema = new mongoose.Schema({
email: String,
password: String,
userName: String,
fname: String,
lname: String,
userType: Number,
subscribedThreads: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Threads"
}
]
});
// add passport methods
userSchema.plugin(passportLocalMongoose);
// export modules to be used by the file requiring it
module.exports = mongoose.model("Users",userSchema);
The first entry into the collection occurs as it should but the next ones give
{ [MongoError: E11000 duplicate key error collection: KManV3.users
index: username_1 dup key: { : null }]
name: 'MongoError',
message: 'E11000 duplicate key error collection: KManV3.users index:
username_1 dup key: { : null }',
driver: true,
code: 11000,
index: 0,
errmsg: 'E11000 duplicate key error collection: KManV3.users index:
username_1 dup key: { : null }',
getOperation: [Function],
toJSON: [Function],
toString: [Function]
}
Also, dbname.users.getIndexes() gives:
> db.users.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "KManV3.users"
},
{
"v" : 1,
"key" : {
"password" : 1
},
"name" : "password_1",
"ns" : "KManV3.users",
"background" : true
},
{
"v" : 1,
"unique" : true,
"key" : {
"username" : 1
},
"name" : "username_1",
"ns" : "KManV3.users",
"background" : true
}
]
Apparently every property of schema has been set as unique and I can't add data into collections even if the data is totally different. I'm not sure if it's due to integration of passport.

Looking at the options for passport-local-mongoose:
usernameField: specifies the field name that holds the username. Defaults to 'username'.
usernameUnique : specifies if the username field should be enforced to be unique by a mongodb index or not. Defaults to true.
Which explains why your collection has a unique index on the (non-existent-in-your-schema) username field.
If you don't actually set this field in documents that you add to the database, MongoDB will use null, and once the first document has been inserted, a subsequent document (also with the field value null for username) will throw an E11000 error.
So first, remove the index on username (and also password, I assume you once marked that field as unique in your schema), and set the proper field name for passport-local-mongoose to use:
userSchema.plugin(passportLocalMongoose, { usernameField : 'userName' });
(or email, if you want that field to be used as unique user identifier)

Error reason - The index is not present in your collection, in which you are trying to insert.
Solution - drop that collection and run your program again.

Related

Mongoose array query

I've been trying to query my database for a specific array element thats contained inside a user model, the schema looks a bit like this:
const schemaUser = new mongoose.Schema({
username: { type: String, required: true, unique: true },
email: {type: String, unique: true},
password: { type: String, required: true},
accounts: [{
id : String,
account_name : String,
name : String,
password : String,
description: String
}]
});
How could I go about selecting a specific account out of the "accounts" array using its custom id shown (not the Mongodb one) that is also contained inside a specific users object?
Cheers.
If you want to query for a record that contains a specific account depending on its id, you could use :
userSchema.findOne({ "accounts.id": { $eq: id }})
Since your question revolves around finding the user based on an array, rather than querying the mongoDB, you could use the array.find() method to return the object you want, based on the id property.
More about array.find() here.
Example:
const accountList = [
{
id : 1,
account_name : 'account_one',
name : 'John Doe',
password : 'asdhui"#¤7aysdg',
description: 'dummy account #1'
},
{
id : 2,
account_name : 'account_two',
name : 'Jane Doe',
password : 'asdhui"#¤7aysdg',
description: 'dummy account #2'
},
{
id : 3,
account_name : 'account_three',
name : 'Derrick Johnson',
password : 'asdhui"#¤7aysdg',
description: 'dummy account #3'
}
];
console.log(accountList.find(account => account.id === 1));
Note that the array.find() method will only return the first instance. If you want to do other types of lookups and return a list of users based on something that isn't unique, then you'd have to use the array.filter() method instead.
In the case you did mean to query the mongoDB, you could query for the field value of the specific document directly by specifying the field you want, followed by the $eq operator and value.
Example:
userSchema.findOne({ "id": { $eq: value } })
value being the specific id of the user you want.
More about the $eq operator here.

Editing a value of an object in an array

I have this object:
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877a8"),
"websites" : [
"",
"",
""
],
"keys" : [
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877af"),
"name" : "Google",
"value" : ""
},
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877ae"),
"name" : "Built With",
"value" : ""
},
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877ad"),
"name" : "Check Host",
"value" : ""
},
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877ac"),
"name" : "Alexa",
"value" : ""
},
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877ab"),
"name" : "Facebook",
"value" : ""
},
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877aa"),
"name" : "Instagram",
"value" : ""
},
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877a9"),
"name" : "Moz",
"value" : ""
}
],
"username" : "admin#admin",
"isPremium" : false,
"accType" : "admin",
"hash" : "very long hash",
"salt" : "long salt",
}
Now. Using NodeExpress and Mongoose I need to be able to edit the value field inside of every object inside the keys array.
My GET operation is this:
// GET: /websites/:_id - show edit form
router.get('/keys/edit/:_id', isAdmin, function(req, res, next) {
// console.log('tada');
// console.log(req.params._id);
Account.findOne({ _id: req.user._id }, function(err, user) {
var selectedKey = findById(user.keys, req.params._id);
// var keys = user.keys.findOne(req.params._id);
console.log(selectedKey);
res.render('admin/edit', {
title: 'Edit websites',
user: req.user,
value: selectedKey.value,
});
});
});
How the app works is: The admin logs in. He sees all users and chooses which one he wants to modify, then admin sees all keys. I will attach screenshots to explain it more clearly.
Now. I think I know what I need to do, but I have no clue how to translate it to code.
I think I need to: Find the index of the array element, like in the GET request, update the value with the posted value. I think I need to find the index in the array.
But as I said I have no clue how to do it.
My POST looks like this right now:
// POST: /keys/edit/_id - save updates
router.post('/keys/edit/:_id', isAdmin, function(req, res, next) {
var p = req.params;
var b = req.body;
Account.findOne({ _id: req.user._id }, function(err, user) {
var selectedKey = findById(user.keys, req.params._id);
// console.log('Key value: ' + req.body.keyValue);
// console.log('Selected key: ' + selectedKey);
console.log('id:' + req.params._id);
if (err) {
console.log(err);
} else {
console.log(user);
user.keys.set(req.params._id, req.body.keyValue);
user.save(err => {
if (err) {
console.log(err);
} else {
console.log('all good');
}
res.redirect('/admin');
});
}
});
EDIT: So I was working on it for a while now and I figured out this. I am using the correct user, I am grabbing the keys array inside, but I don't know how to find the id of the object in the array, which (object) I need to edit.
There is a lot of nesting and this might cause some issues.
EDIT 2: I'm attacking my account model. Forgot about it earlier. Sorry.
var mongoose = require('mongoose');
var website = require('./website');
var plm = require('passport-local-mongoose');
var accountSchema = new mongoose.Schema({
isPremium: Boolean,
accType: String,
websites: [],
keys: [
{ name: String, value: String },
{ name: String, value: String },
{ name: String, value: String },
{ name: String, value: String },
{ name: String, value: String },
{ name: String, value: String },
{ name: String, value: String },
],
});
accountSchema.plugin(plm);
module.exports = mongoose.model('Account', accountSchema);
You can perform the update atomically using $positional operator.
You include the field (_id) from the keys to locate the index of element and replace the placeholder($) with the found index from query part in the update part to set the value in keys.
router.post('/keys/edit/:_id', isAdmin, function(req, res, next) {
var p = req.params;
var b = req.body;
Account.findOneAndUpdate(
{_id: req.user._id,'keys._id':req.params._id },
{$set:{'keys.$.value':req.body.keyValue}},
{new: true},
function(err, account) {}
);
The question isn't entirely clear to me what you're looking to do, but what I can infer is that you want to do the following:
You have some object that has an Array of keys that has the following shape:
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877af"),
"name" : "Google",
"value" : ""
}
Judging from your sample object, I'm inferring the schema is defined something like:
const mongoose = require('mongoose')
const definition = {
websites: [String],
keys: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Key'
}]
}
const accountSchema = new mongoose.Schema(definition)
module.exports = mongoose.model('Account', topicSchema)
By the looks of the route, you want to update/edit that object at the given index: keys[i]. If this is the case, then there is no need to manually traverse the array, update the model directly:
const Key = require('./path/to/models/Key')
router.post('/keys/edit/:id', async (req, res) => {
const { keyValue } = req.body
const conditions = { _id: req.params.id }
await Key.findOneAndUpdate({ id }, { value: keyValue }).exec()
res.status(201).json()
})
The item in the array will be updated when you query the parent model.

Mongo db reference duplicate?

I have this error i cannot seem to understand what I'm doing wrong.
In my database i have an object called Question, questions have a reference to Subject and to User. When I'm trying to post to Question i get a strange error.
E11000 duplicate key error index: codenoname.questions.$subject.name_1 dup key: { : null }
My Question schema:
var questionSchema = mongoose.Schema({
title: { type : String , required : true},
text: { type : String , required : true},
subject: {type:String, ref: 'Subject', required: true},
createdBy: {type: String, ref:'User', required: true},
difficulty: { type : String , required : true},
alternatives: [{alternative: {type:String, required:true}, isCorrect: {type:Boolean, required:true}}]
});
and my Subject
var subjectSchema = mongoose.Schema({
name: { type : String , required : true, unique:true}
});
Save method:
var question = new Question(
{title: title,
text: text,
subject: ObjectId(subject),
difficulty: difficulty,
createdBy: id,
alternatives:alternatives
});
question.save( function(err, newQuestion) {
if(err) {
res.status(400).json({err:err});
} else {
res.status(200).json({status:"Question added"});
}
});
What i have tried
Delete all Questions, then I can post, but just one...
Remove the reference and just keep it as a string. No difference.
Restarted the server a few times.
Try removing the unique: true from subject. I think questionSchema inherits the unique property and once you try to save two different questions with the same subject, you'll get duplicate key.
Follow these steps:
Remove unique: true from your model
Find the index name by typing db.questions.getIndexes() in your terminal.
Remove it by typing db.questions.dropIndex(name) where name is the "name"-property from step 2
Example from my database where i'll remove the unique-property from usernames:
> db.accounts.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "passport_local_mongoose_express4.accounts"
},
{
"v" : 1,
"unique" : true,
"key" : {
"email" : 1
},
"name" : "email_1",
"ns" : "passport_local_mongoose_express4.accounts",
"background" : true
},
{
"v" : 1,
"unique" : true,
"key" : {
"username" : 1
},
"name" : "username_1",
"ns" : "passport_local_mongoose_express4.accounts",
"background" : true
}
]
> db.accounts.dropIndex('username_1')

Create mongoose schema and insert document

I'm new to mongo and mongoose. I need to create schema from this object
trailers: [
{
id: 310131,
results: [
{
id: "564b9086c3a3686026004542",
iso_639_1: "en",
key: "gy_-pupKvxg",
name: "The Witch Official Trailer 1 2016 HD",
site: "YouTube",
size: 1080,
type: "Trailer"
},
{
id: "56aab2b19251417e110008b2",
iso_639_1: "en",
key: "feRupW34rUU",
name: "Trailer 2",
site: "YouTube",
size: 1080,
type: "Trailer"
}
]
}
],
Here is how I am doing it at the moment
// My Schema
var movieSchema = new mongoose.Schema({
....
...
..
.,
trailers : [
{
id: Number,
results : [
{
id: String,
iso_639_1: String,
key: String,
name: String,
site: String,
size: Number,
type: String,
}
],
_id : false
}
],
....
..
.
});
var Movie = mongoose.model('Movies', movieSchema);
// Insert movie into schema and save to database
movies.forEach(function(m) {
var movie = new Movie({
....
...
..
.,
trailers : m.trailers ? m.trailers : "",
....
...
..
.
});
movie.save(function(err) {
if(err) console.log('Error on save' + err);
});
}, this);
But I am doing it wrong can anyone spot or tell me how to insert each movie into schema object. Thanks in advance.
Per mongoose subdoc, please try to use .push to push trailers to movies. Here are the sample codes.
movies.forEach(function(m) {
var movie = new Movie({
....
//trailers : m.trailers ? m.trailers : "",
....
});
m.trailers.forEach(function(t){
movie.trailers.push(t);
});
movie.save(function(err) {
if(err) console.log('Error on save' + err);
});
});
According to me this should be your schema :
var movieSchema = new mongoose.Schema({
trailers: [
....
........
....
_id: Number,
results: [{
iso_639_1: String,
key: String,
name: String,
site: String,
size: String,
type: String
}]
........
....
]
});
Looking at your object, what I suggest is :
You should assign the _id field of the trailers array the Number that you currently assign to id field. And remove the id field from the schema.
_id field uniquely identifies a document inside a collection. So you should use it instead of id field that you created.
Secondly, you should let MongoDB create an _id field for your results array objects. That's why I haven't included an _id field in the results array object.
Next, to create new documents and save them in your collection, use your logic :
var Movie = mongoose.model('Movies', movieSchema);
// Insert movie into schema and save to database
movies.forEach(function(m) {
var movie = new Movie({
....
...
..
.,
(m.trailers) ? trailers = m.trailers : trailers = null,
....
...
..
.
});
movie.save(function(err) {
if(err) console.log('Error on save' + err);
});
}, this);
What's wrong with your logic is that you're trying to use ternary operator to check if m.trailers exist and if it does, you want to assign it to trailers array, and null otherwise. What you are using is :
trailers : m.trailers ? m.trailers : "",
What you should be using instead is :
(m.trailers) ? trailers = m.trailers : trailers = null,
Please do check it and let me know if it works.

compound index not working?

I am trying to create a compound index using mongoose:
var ProjectSchema = new mongoose.Schema({
name: {type: String, required: true},
user: {type: mongoose.Schema.ObjectId, ref: 'User', required: true}
});
ProjectSchema.index({user: 1, name: 1}, {unique: true});
after that I dropped the old database in mongo
db.dropDatabase()
but I still can insert multiple documents with the same name and user id. why?
the index that it created shows in mongo as
> db.projects.getIndexes();
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "mydatabase.projects",
"name" : "_id_"
}
]
This is the pure mongo console function and it works,
Click Here for more detail. This is not descibe in mongoose's API.
I think it might be work.
db.collection.ensureIndex( { a: 1 }, { unique: true, dropDups: true } )
Actually your index does not appear to have been created. You are showing just the default primary key. Your output from .getIndexes() should be more like:
> db.projects.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "project.projects",
"name" : "_id_"
},
{
"v" : 1,
"key" : {
"user" : 1,
"name" : 1
},
"unique" : true,
"ns" : "project.projects",
"name" : "user_1_name_1",
"background" : true,
"safe" : null
}
]
There might be something up in your code, but this works for me:
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/project');
var db = mongoose.connection;
var Schema = mongoose.Schema;
var UserSchema = new Schema({
name: { type: String, required: true },
info: String
});
var ProjectSchema = new Schema({
name: { type: String, required: true},
user: { type: Schema.ObjectId, ref: 'User', required: 'true'}
});
ProjectSchema.index({ user: 1, name: 1}, { unique: true });
var User = mongoose.model( "User", UserSchema );
var Project = mongoose.model( "Project", ProjectSchema );
var user = new User({ name: "me" });
user.save(function(){
var project = new Project({ name: "Project1", user: user._id });
project.save(function(err, project, numAffected){
if (err) { // Should be fine
console.log(err);
}
console.log("First project created");
User.findOne({}).exec(function(err, user) {
console.log(user._id);
var project = new Project({ name: "Project1", user: user._id });
project.save(function(err, project, numAffected){
if (err) {
console.log(err); // Expect a problem here
}
console.log({ project: project, num: numAffected });
});
});
});
});
I had the exact same problem and this Github issue explained what was happening.
Firstly, compound indexes are only created after ensureIndex() is called. The problem for me is that I was using an import script that would drop my database and re-create it. ensureIndex() is not called until the server is restarted, so the compound index was not re-created after this.
The solution for me was to not drop my database in my import script, but instead iterate through my models and remove all the documents. That way the data was destroyed, but the indexes remained and hence the compound index would work.
I just had this problem, the compound index was not created at startup and I was checking the mongo logs, I could see that it was starting the build of the index but nothing was created, no errors etc...
Then I tried to manually create the index - in mongo console - and here I got an error (duplicate error in my case), so I removed the duplicates and I was able to create the index. I don't know why this was not popping up on my mongo logs.
Mongo v4

Categories

Resources