Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do you ensure that the ShortId used in Mongoose won't be duplicated? Or if duplication happens then? #65

Closed
Kaijun opened this issue Mar 20, 2016 · 5 comments

Comments

@Kaijun
Copy link

Kaijun commented Mar 20, 2016

As we see in the doc

Mongoose Unique Id

_id: {
    type: String,
    unique: true,
    'default': shortid.generate
},

I consider if shortid.generate will accidentally generate a duplicated id for the record.

If it really happens, will mongoose throw an error something like duplicated index error by saving the new record, or will it regenerate a new id automatically until it's not duplicated anymore?

@manuel-di-iorio
Copy link

Yes, mongoose will give you the error E11000 duplicate key error index but it will not automatically retry the query. You have to handle this part yourself or with a wrapper module.

There is https://github.com/jjwchoy/mongoose-shortid but this actually uses an internal shortid generation system. If there aren't yet modules, you could look at the source code of this one and build your own module!

@Kaijun
Copy link
Author

Kaijun commented Mar 26, 2016

@manuel-di-iorio thanks! i just implemented something by using the pre() hook in mongoose, here is my code if anyone is interested 😄

import mongoose from 'mongoose'
import shortid from 'shortid'

const xxxSchema = mongoose.Schema({
  code: {
    type: String,
    unique: true,
  },
  ...
})

// generating a non-duplicate Code
xxxSchema.pre('save', function(next){  // can't use arror function, or this will be undefinded. fat arrow is lexically scoped.
  let ctx = this
  attempToGenerate(ctx, next)
})

function attempToGenerate(ctx, callback) {
    let newCode = shortid.generate()
    ctx.constructor.findOne({'code': newCode}).then((course) => {
      if(course){
        attempToGenerate(ctx, callback)
      }
      else{
        ctx.code = newCode
        next();
      }
    }, (err) => {
      next(err)
    })
  }

export default mongoose.model('XXX', xxxSchema)

Note: ctx.constructor.findOne is kind of tricky way to access the model. Solution found on StackOverflow

@manuel-di-iorio
Copy link

👍

Module wrapper:

const shortid = require("shortid");

exports.inject = (schema) => {
    schema.pre('save', kaijun code here..
};

Use it with:

const mongooseShortid = require("chooseModuleName")   
mongooseShortid.inject(yourSchema);

PS: I would also place the attempToGenerate private function outside of the pre save hook

@Kaijun
Copy link
Author

Kaijun commented Mar 26, 2016

@manuel-di-iorio thanks a lot!

@Kaijun Kaijun closed this as completed Mar 26, 2016
@doomedramen
Copy link

Just some notes for anyone reading this in the future.

In the Kaijun code, I believe next() is supposed to be callback() inside of attempToGenerate.
Also, if you move this code from pre('save') to pre('validate') you can add required: true to the code param in the schema.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants