Skip to content

Instantly share code, notes, and snippets.

@boutell
Created April 15, 2012 01:42
Show Gist options
  • Save boutell/2389267 to your computer and use it in GitHub Desktop.
Save boutell/2389267 to your computer and use it in GitHub Desktop.
Guarantee short yet unique slugs for any model
// Extend any Mongoose model that has a unique index on a 'slug' field so that
// if a unique index error occurs on 'slug', a random digit is added to the slug
// and the save operation is retried until it works. This is concurrency-safe even
// if you have lots of inserts going on from multiple machines etc.
//
// By Tom Boutell, tom@punkave.com
//
// NOTE: YOU MUST HAVE THIS PULL REQUEST... https://github.com/LearnBoost/mongoose/pull/837
//
// (As of this writing that commit is not in LearnBoost/mongoose yet.)
//
// Here's how to use it:
// Make your model the usual way
// var MediaItem = mongoose.model('MediaItem', mediaItemSchema);
// Call my function to install the extensible slug behavior
// modelExtendSlugOnUniqueIndexError(MediaItem);
// ALSO: note that it is also very important to make sure your indexes have really
// been applied before you try to do things like inserts! Otherwise you won't get
// your unique index error until it is too late.
// MediaItem.on('index', function()
// {
// nowICanSafelyInsertThings();
// });
// Call this function with a model object to install the automatic slug extension behavior
function modelExtendSlugOnUniqueIndexError(model)
{
// Stash the original 'save' method so we can call it
model.prototype.saveAfterExtendSlugOnUniqueIndexError = model.prototype.save;
// Replace 'save' with a wrapper
model.prototype.save = function(f)
{
var self = this;
// Our replacement callback
var extendSlugOnUniqueIndexError = function(err, d)
{
if (err)
{
// Spots unique index errors relating to the slug field
if ((err.code === 11000) && (err.err.indexOf('slug') !== -1))
{
self.slug += (Math.floor(Math.random() * 10)).toString();
self.save(extendSlugOnUniqueIndexError);
return;
}
}
// Not our special case so call the original callback
f(err, d);
};
// Call the original save method, with our wrapper callback
self.saveAfterExtendSlugOnUniqueIndexError(extendSlugOnUniqueIndexError);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment