Create Document with unique property in Mongoose
Problem
I'm trying to save a document with a unique property across the collection.
So if someone tries to save a Kitten with {name: 'kitty'} and that name already exisits it fails and retries with {name: 'kitty1'} (then 'kitty2' etc).
I really can't figure out a good way to do this, any ideas?
Here's what I have so far (doesn't work).
var kittenSchema = new Schema ({
name: {type: String, index: {unique: true}}
})
var Kitten = mongoose.model('Kitten', kittenSchema);
var kitten = new Kitten({
name: 'kitty'
});
kitten.save(function(err, kitten){
if (err){
saveKitten(kitten, 1, function(err, kitten){
if (err){
console.log("Damn, no kitten");
} else {
console.log("New kitten " + kitten.name + "saved.");
});
} else {
console.log("New kitten " + kitten.name + "saved.");
}
}));
function saveKitten(kitten, count, callback){
kitten.name = kitten.name + count;
kitten.save(function(err, newKitten){
if (err){
if (count > 100){
// Give up!
callback(err, null);
}
saveKitten(kitten, count + 1, callback);
} else {
callback(null, newKitten);
}
}));
}
Solution
After a pointer from NilsH I think I might try to pull back all possible name clashes and look for a new name locally.
Something like this:
var Kitten = mongoose.model('Kitten', kittenSchema);
var kitten = new Kitten({
name: 'kitty'
});
Kitten.find({name: new RegExp('^'+ kitten.name +'*', "i")}, 'name').exec(function(err, docs){
var tryName = name;
var count = 0;
while (True){
if (_.indexOf(docs, tryName) != -1){
break;
} else {
tryName = name + count;
}
}
kitten.name = tryName;
kitten.save(function(err, kitten){
if (err){
console.log("Damn, no kitten");
} else {
console.log("New kitten " + kitten.name + "saved.");
}
}));
});
There's a possible race condition between finding the name and using it but I think I can live with that.
Discussion
View additional discussion.