Elisa L
Elisa L

Reputation: 322

Mongodb and Mongoose error: E11000 duplicate key error index

So I've been working on a project and I finished most of it, but then this error popped up, saying there is something that is undefined, here is the error:

E11000 duplicate key error index: build-a-voting-app.polls.$votedIp_1 dup key: { : undefined }

Here is my code for my create new mongo schema file (polls.model.js)

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const pollSchema = new Schema({
title: { type: String, unique: true, required: true },
choices: [
    {
        title: { type: String, required: true },
        count: { type: Number, default: 0 }
    }
],
votedIp: [{ type: String, unique: true }],
createdAt: {type:Date, default:Date.now()},
createdBy: String
});

const Poll = mongoose.model('polls', pollSchema);

module.exports = Poll;

Here is the function where I add the inputs

function submitVote(field, res, ip) {

Poll.findOneAndUpdate(
    { choices: { $elemMatch: { title: field } } },
    { $inc: { 'choices.$.count': 1 }, $addToSet: { 'votedIp': ip } },
    { new: true },
    function (err, poll) {
        if (err) throw err;
        res.json({ updated: poll });
    }
);
}

Here is how I first created it

var newPoll = new Poll({
    title: req.body.title,
    choices: choicesArr,
    createdBy: req.session.user.username || req.session.user
}).save(function (err, poll) {
    if (err) throw err
    res.redirect('/mypolls')
});

If you want to see the full code please go to https://github.com/ElisaLuo/Freecodecamp-Build-A-Voting-App

I'm using the ip addresses for checking if the user has voted or not (I'm building a voting app), but right now, I cannot even create a new schema / poll. Does anyone know why the error happens and how I can solve it?

Upvotes: 1

Views: 1028

Answers (2)

user2347763
user2347763

Reputation: 489

@Elisa l - you may want to read this - mongoose enforce unique attribute on subdocument property

However, I did manage to test with mongoose-mock and the behavior is as expected - test results below (please do check the two versions of votedIp in the test code snippets)

enter image description here

and as described in the MongoDb document referenced in the above link. Mongoose does not enforce the unique integrity, MongoDb does.

With the mocha test below (inserted as snippets, not to run the code but just for better readability, please ignore the messy look of the comments in the code but the permutation and combination had to be worked out!), I did manage to create the mongoose schema by adding a create method in "Poll". please note the change in the schema - votedIp: { type: String, unique: true }, you can change it to array in the test code.

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var pollSchema = new Schema({
title: { type: String, unique: true, required: true },
choices: [
    {
        title: { type: String, required: true },
        count: { type: Number, default: 0 }
    }
],
votedIp: { type: String, unique: true },
createdAt: {type:Date, default:Date.now()},
createdBy: String
});
// Below code added by RJv (ie. me :)
var NewPoll = new mongoose.Schema(pollSchema);
NewPoll.statics.create = function (params, callback) {
  var newUpdate = new NewPoll(params);
  newUpdate.save(function(err, result) {
    callback(err, result);
  });
  return newUpdate;
};
var Poll = mongoose.model('Model', NewPoll);
module.exports = Poll;

var expect = require('chai').expect,mongooseMock = require('mongoose-mock'),proxyquire=require('proxyquire'),
sinon = require('sinon'), chai=require('chai'),sinonChai = require("sinon-chai");chai.use(sinonChai);

var Poll;
before(function(done){
        Poll = proxyquire('./Poll', {'mongoose': mongooseMock});
        done();
})
describe('model', function() {
        /*	beforeEach(function (done) {
	           Poll = proxyquire('./Poll', {'mongoose': mongooseMock});
                done();	
        });*/
it("should be called once",function(done){
        setTimeout(done, 15000);
        var callback = sinon.spy();
        var poll1 = Poll.create({ "title": 'jv', "choices": [{"title":"[email protected]","count":"1"}],
            "votedIp":"9.1.2.1","createdAt":"23/07/2017","createdBy":"Lisa"}, callback);
    
        // Below to pass data for votedIp as an array as described in the original schema by Elisa 
        //"votedIp":[{"add1":"9.","add2":"1.","add3":"2.","add4":"1"}],"createdAt":"23/07/2017","createdBy":"Lisa"}, callback);
        //expect(poll1.votedIp[0].add1+poll1.votedIp[0].add2+poll1.votedIp[0].add3+poll1.votedIp[0].add4).equals("9.1.2.1");
                            
        expect(poll1.save).calledOnce;
        console.log(JSON.stringify(poll1));
        expect(poll1.votedIp).equals("9.1.2.1");
                    
        done(); 
});
it('should expect same ip to get added', function(done) {
        this.timeout(5000);
        setTimeout(done, 15000);

		var callback = sinon.spy();//mock(new Poll({ title: 'jv', choices: [{title:"[email protected]","count":"1"}], votedIp:[{ad1:"9.",add2:"1.",add3:"2.",add4:"1"}],createdAt:"25/07/2017",createdBy:"Lisa"}));
        var poll = Poll.create({ "title": 'jv', "choices": [{"title":"[email protected]","count":"1"}],
            "votedIp":"9.1.2.1","createdAt":"23/07/2017","createdBy":"Lisa"}, callback);
        // var poll = Poll.create({ "title": 'jv', "choices": [{"title":"[email protected]","count":"1"}],

        // Below to pass data for votedIp as an array as described in the original schema by Elisa 
        //            "votedIp":[{"add1":"9.","add2":"1.","add3":"2.","add4":"1"}],"createdAt":"25/07/2017","createdBy":"Lisa"}, callback);
                // expect(poll.votedIp[0].add1+poll.votedIp[0].add2+poll.votedIp[0].add3+poll.votedIp[0].add4).equals("9.1.2.1");
        expect(poll.save).calledOnce;                                      
        expect(poll.votedIp).equals("9.1.2.1");  
    
        //assert(spy.calledOnce);
        done();
    });
});

Upvotes: 1

Barış Uşaklı
Barış Uşaklı

Reputation: 13532

Are you calling submitVote multiple times in quick succession? You might be running into https://jira.mongodb.org/browse/SERVER-14322.

The suggested fix for this is to check the error and if one of the calls fails retry it.

https://docs.mongodb.com/manual/reference/method/db.collection.update/#use-unique-indexes

Upvotes: 0

Related Questions