mongo的用户友好ID

时间:2015-05-20 15:51:03

标签: node.js mongodb

我正在创建一个实时股票交易系统,并希望以人类可读,用户友好的方式提供用户来参考他们的订单。例如,ID应该是8个字符长,并且只包含大写字符,例如的 Z9CFL8BA 即可。出于显而易见的原因,id在系统中必须是唯一的。

我使用MongoDB作为后端数据库,并评估了以下不符合我要求的项目。

hashids.org - 这看起来不错,但它会生成太长的ID:

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

导致:1E6Y3Y4D7RGYHQ7Z3XVM4NNM

github.com/dylang/shortid - 这要求您指定64个字符的字母,如上所述,我只想使用大写字符。

我知道实现我正在寻找的东西的唯一方法可能是生成符合我要求的随机代码,然后检查数据库是否存在冲突。如果是这种情况,在nodejs / mongodb环境中执行此操作的最有效方法是什么?

2 个答案:

答案 0 :(得分:0)

您正在尝试将base-16(十六进制)转换为base-36(字母表中的26个字符加上10个数字)。一种简单的方法可能是简单地使用parseInt的radix参数来解析十六进制id,然后调用.toString(36)将其转换为base-36。这将把“507f191e810c19729de860ea”变成“VDFGUZEA49X1V50356”,将长度从24个字符减少到18个字符。

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>

答案 1 :(得分:0)

  

我知道实现我正在寻找的东西的唯一方法可能是生成符合我要求的随机代码,然后检查数据库是否存在冲突。如果是这种情况,在nodejs / mongodb环境中执行此操作的最有效方法是什么?

根据您的描述,您使用[0-9A-Z]范围内的8个字符作为“id”。这是36⁸组合(≈2.8211099E12)。假设您的交易系统在短期到中期内没有获得极大的普及,那么碰撞的可能性相当低。

所以你可以采取乐观的方法,使用下面代码中的某些内容生成一个随机id(正如@idbehold在评论中注意到的那样,警告Math.random可能不够随意,所以可能增加碰撞的几率 - 如果你这样做,也许你应该调查better random generator [1]

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

然后,在该字段上使用正确的唯一索引,您只需循环,重新生成新的随机ID,直到尝试插入新事务时没有冲突。由于碰撞的机会相对较小,此应该终止。并且大部分时间这将在第一次迭代时插入新文档,因为没有碰撞。

如果我没错,假设有100亿次交易,你在第一回合时仍有0.3%的碰撞几率,而第二回合的碰撞次数则略高于0.001% < / p>

[1] 在节点上,您可能更喜欢使用crypto.pseudoRandomBytes来生成随机ID。你可以围绕它建立一些东西,也许:

> 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'