Short description for mongo

I am building a real-time stock trading system and would like to provide a user-friendly, user-friendly way to reference their orders. For example, the identifier must be 8 characters long and only contain uppercase characters, eg. Z9CFL8BA . For obvious reasons, the identifier must be unique in the system.

I am using MongoDB as my base database and have evaluated the following projects which do not meet my requirements.

hashids.org - this looks good, but it generates too large ids:

var mongoId = '507f191e810c19729de860ea';
var id = hashids.encodeHex(mongoId);
console.log(id)

      

which results in: 1E6Y3Y4D7RGYHQ7Z3XVM4NNM

github.com/dylang/shortid - this requires a 64 character alphabet and as mentioned I only want to use uppercase characters.

I understand that the only way to achieve what I am looking for could very well be by generating random codes that match my requirements and then checking the database for collisions. If so, what would be the most efficient way to do it in a nodejs / mongodb environment?

+3


source to share


2 answers


You are trying to convert base-16 (hex) to base-36 (26 characters to the alphabet plus 10 numbers). The easy way could be to just use the parseInt

radix parameter to parse the hex id and then call .toString(36)

to convert it to base-36. Which would turn "507f191e810c19729de860ea" into "VDFGUZEA49X1V50356", reducing the length from 24 to 18 characters.

function toBase36(id) {
  var half = Math.floor(id.length / 2);
  var first = id.slice(0, half);
  var second = id.slice(half);
  return parseInt(first, 16).toString(36).toUpperCase()
       + parseInt(second, 16).toString(36).toUpperCase();
}

      



function toBase36(id) {
  var half = Math.floor(id.length / 2);
  var first = id.slice(0, half);
  var second = id.slice(half);
  return parseInt(first, 16).toString(36).toUpperCase()
       + parseInt(second, 16).toString(36).toUpperCase();
}

// Ignore everything below (for demo only)
function convert(e){ if (e.target.value.length % 2 === 0) base36.value = toBase36(e.target.value) }
var base36 = document.getElementById('base36');
var hex = document.getElementById('hex');
document.getElementById('hex').addEventListener('input', convert, false);
convert({ target: { value: hex.value } });
      

input { font-family: monospace; width: 15em; }
      

<input id="hex" value="507f191e810c19729de860ea">
<input id="base36" readonly>
      

Run codeHide result


0


source


I understand that the only way to achieve what I am looking for could very well be by generating random codes that match my requirements and then checking the database for collisions. If so, what would be the most efficient way to do it in a nodejs / mongodb environment?

Given your description, you are using 8 characters in the range [0-9A-Z] as "id". These are 36⁸ combinations (≈ 2.8211099E12). Assuming your trading system is not gaining insanely huge popularity in the short to medium term, the chances of a collision are pretty low.

So, you can take an optimistic approach, generating a random id with something along the lines of the code below (as @idbehold pointed out in a comment, be warned that it Math.random

is probably not random enough and therefore can increase the chances of a collision - if you go like this, maybe you should research a better random generator [1] )

> rid = Math.floor(Math.random()*Math.pow(36, 8))
> rid.toString(36).toUpperCase()
30W13SW

      

Then, using the correct unique index on that field, you only need to loop through regenerating a new random ID until there is a collision when trying to insert a new transaction. Since the likelihood of a collision is relatively low, this should stop. And most of the time this will insert a new document on the first iteration since there was no collision.



If I am not too mistaken, assuming 10 billion transactions, you will still have only 0.3% chance of collision on the first corner and just over 0.001% on the second corner.


[1] In node you can use crypto.pseudoRandomBytes to generate your random id, you can build something around this, maybe:

> b = crypto.pseudoRandomBytes(6)
<SlowBuffer d3 9a 19 fe 08 e2>
> rid = b.readUInt32BE(0)*65536 + b.readUInt16BE(4)
232658814503138
> rid.toString(36).substr(0,8).toUpperCase()
'2AGXZF2Z'

      

0


source







All Articles