Mongoose中使用shortid遇到的坑


mongoose用的不多,感觉还挺好用,对比mysql/mariadb的orm如sequelize, bookshelf等,要灵活的多,不过其接口和参数比较多,有一些默认的行为,用起来需要比较小心,要多进行测试。

shortid 是一个比较不错的生成较短的唯一ID的库,根据前人经验和shortid文档,将mongodb document的默认_id设置为shortid,感觉是个不错的主意。但是经过使用,我碰到了一些棘手的问题,然后我打算放弃使用shortid。

很多开源的小项目中使用shortid定义_id是这样的:

1
_id: { type: String, default: shortid.generate }

这样挺好(如果使用时提前知道一些潜在问题并规避的话),有些问答文章中答主这么写:

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

我不知道是版本问题还是答主想当然,这样是会报错的:

1
mongoose: Cannot specify a custom index on `_id` for model name "xxx", MongoDB does not allow overwriting the default `_id` index. See http://bit.ly/mongodb-id-index

也就是你可以自定义_id的类型和默认值,但是不能自定义其相关索引。那么自定义类型和默认值就没有问题了吗?事实上,我没有在任何官方的说法中看到以上用法。而事实上确实有些问题(也就是上面说的需要规避的), 我碰到有下面几点:

  1. Model.findOneAndUpdate(cond, data, {upsert: true}) 调用mongodb的findAndModify函数,这样会跳过我们的_id定义,在插入数据是生成ObjectId类型的_id; 导致后续查询失败。

  2. Model.create函数更神奇:当传递array做参数时,会好好调用shortid.generate生成_id,当传递单个object, 则生成ObjectId。从mongoose官方文档上你看不出来为什么会有这个区别(代码上我也没看出来,没有更深入debug)。

这俩问题暂时都没有找到很好的解决办法,只能规避;想用shortid,创建时记得用Model.create(array)或这document.save。