0

I made a user schema based on mongoose or mongodb, in which there is a invitation code and this code is always unique in the database.

import randomize from 'randomatic';

const userSchema = new Schema<UserDataInterface>(
  {
    username: {
      type: String,
      required: [true, "Please username is mandatory."],
      unique: false,
    },
    inviteCode: { type: String, default: randomize('0', 8)  },
    ...
  }

The invite code is 8 length long numbers generated by a randomatic library, I expect it will be different for every user, so I didn't enforce inviteCode to be unique

but during the production, I found two latest registered users who are registered at different time have exact the same invitecode.

{
  "_id": {
    "$oid": "6656e341495d3ec1782baa7d"
  },
  "username": "ddd333",
  "inviteCode": "94770150",
  "createdAt": {
    "$date": "2024-05-29T08:11:45.240Z"
  }
}
{
  "_id": {
    "$oid": "66572d68495d3ec1782bab0f"
  },
  "username": "ddd233",
  "inviteCode": "94770150",
  "createdAt": {
     "$date": "2024-05-29T13:28:08.943Z"
  }
}

I found the problem is all user's inviteCode is the same value,

inviteCode: { type: String, default: randomize('0', 8) }.

It looks like randomize function only initalize only once

1
  • The problem is you are calling randomize() when the schema is defined instead of passing the function. That means when the script runs the default value will be the return value of the function invocation and therefore will always be the same as it's only called once. Pass a function instead. Commented May 30, 2024 at 8:02

3 Answers 3

1

Please wrap the invocation within a function and pass it instead. @jQueeny has already commented the same. Please see the sample code below.

  const schema = new mongoose.Schema({
    _id: {
      type: String,
      default: () => randomize('0', 8),
    },
  });

It generated the following output while testing:

{ _id: '96726096', __v: 0 },
{ _id: '08571337', __v: 0 },
{ _id: '60216517', __v: 0 },
{ _id: '03736755', __v: 0 },
{ _id: '73289283', __v: 0 }
Sign up to request clarification or add additional context in comments.

Comments

1

Here is an alternative that does not rely on external library but use only MongoDB aggregation operators.

  1. generate random number with $rand
  2. $multiply the number with 100000000 and $toLong to make take only integer part
  3. $toString and pad with 8 zeros in front
  4. take substring for last 8 digits
db.collection.aggregate([
  {
    "$set": {
      // generate the inviteCode in long first; could be less than 8 digits
      "inviteCode": {
        "$toLong": {
          "$multiply": [
            {
              "$rand": {}
            },
            100000000
          ]
        }
      }
    }
  },
  {
    "$set": {
      // convert to string and pad zeros in front
      "inviteCode": {
        "$concat": [
          "00000000",
          {
            $toString: "$inviteCode"
          }
        ]
      }
    }
  },
  {
    "$set": {
      "inviteCode": {
        // get the last 8 digits
        "$substrCP": [
          "$inviteCode",
          {
            "$subtract": [
              {
                "$strLenCP": "$inviteCode"
              },
              8
            ]
          },
          8
        ]
      }
    }
  },
  {
    "$merge": {
      "into": "collection",
      "on": "_id"
    }
  }
])

Mongo Playground


To ensure the uniqueness of the field, add a unique index around it.

Comments

0

I don't know why inviteCode: { type: String, default: randomize('0', 8) } only init once for all registered users.

so I tried

userSchema.pre("save", async function (next) {
  this.inviteCode = randomize('0', 8);
  next();
});

and it works out.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.