Unique and Sparse Schema-level Index MongoDB and Mongoose

1.3k views Asked by At

I am trying to create an index on two fields of a schema that are to be unique and sparse in MongoDB using Mongoose as follows:

var ArraySchema = new Schema ({
    user_id: {type: mongoose.Schema.Types.ObjectId, ref:'User'},
    event_id: {type: mongoose.Schema.Types.ObjectId, ref:'Event'}
}, {_id:false});

ListSchema.index({user_id:1, event_id:1}, {sparse:true, unique:true});

Which is then used in an array in the User schema as such:

var User = new Schema({
    arrayType1 : {
        type: [ArraySchema]
    },
    arrayType2 : {
        type: [ArraySchema]
    },
    arrayType3 : {
        type: [ArraySchema]
    }
    //More specifications for user schema...
});

However, when trying to save multiple users without the array field, errors are thrown for duplicate fields. The error in Mocha looks similar to this: array.event_id_1 dup key {null, null}. An example of a segment of code that would throw this error is as follows:

var user1, user2;

user1 = new User({
    username : 'username1',
    password : 'password'
});

user2 = new User({
    username : 'username2',
    password : 'password'
});

user1.save(function() {
    user2.save();
});

Here is my reasoning behind making the the fields of ArraySchema unique and sparse: If the array field is specifed, I do not want the array to contain duplicate objects; however, the array field is not required, so there will be many Users that have null for this field. Obviously I cannot use field-level indices since there are multiple fields that would need an index (arrayType1, arrayType2, arrayType3).

1

There are 1 answers

1
c1moore On

It appears that doing this sort of thing is not supported, at least at this time. The alternative would be to create a compound index on these fields then whenever adding a new element to the field use user.arrayType1.addToSet(). Here is an example of how this would work:

ArraySchema:

var ArraySchema = new Schema ({
     user_id: {type: mongoose.Schema.Types.ObjectId, ref:'User'},
     event_id: {type: mongoose.Schema.Types.ObjectId, ref:'Event'}
}, {_id:false});

ListSchema.index({user_id:1, event_id:1});

User schema:

var User = new Schema({
    arrayType1 : {
        type: [ArraySchema]
    },
     arrayType2 : {
         type: [ArraySchema]
    },
    arrayType3 : {
        type: [ArraySchema]
    }
    //More specifications for user schema...
});

Then I could declare new users as usual (as I did in the question); however, when I want to add a new element to arrayType1, for example, I would use the following line of code to add to new element only if it is not already there:

user.arrayType1.addToSet({user_id : user2._id, event_id : event._id});
user.save(function(err, result) {
    //Callback logic.
};

Where user2 and event are defined earlier in the code and saved to the db. Alternatively I could use Mongoose's update function as such:

User.update({_id : user._id}, {
        $addToSet : {
            arrayType1 : {
                user_id : user2._id,
                event_id : event._id
            }
        }
    }, function(err) {
        //Callback logic.
    }
);