MongoDB:21-MongoDB-自增Id

本文介绍如何在MongoDB中实现类似SQL的自增ID功能,通过定义函数getNextSequence并在counters集合中维护序列值来达到目的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. MongoDB 没有像 SQL 一样有自动增长的功能, MongoDB _id 是系统自动生成的12字节唯一标识。
  2. 但在某些情况下,我们可能需要实现 ObjectId 自动增长功能
  3. 由于 MongoDB 没有实现这个功能,我们可以通过编程的方式来实现,以下我们将在 counters 集合中实现_id字段自动增长。
  1. 首先需要建立一个表结构,用于存储你的id信息:
    1. db.counters.insert(
    2. {
    3. _id: "userid",
    4. seq: 0
    5. }
    6. )

  2. 然后定义一个函数,每次调用实现id++功能
    1. 我们创建函数 getNextSequence 来作为序列名的输入, 指定的序列会自动增长 1 并返回最新序列值。
    2. 在本文的实例中序列名为 userid

    1. function getNextSequence(name) {
    2. var ret = db.counters.findAndModify(
    3. {
    4. query: { _id: name },
    5. update: { $inc: { seq: 1 } },
    6. new: true
    7. }
    8. );
    9. return ret.seq;
    10. }
    1. 上面的函数,主要用到了findAndModify这个系统函数,此系统函数其实就是实现了updatequery语句。
    1. 拥有类似事务特性的更新与查询操作——findAndModify.
    2. 它是原子性的,会返回符合查询条件的更新后的文档。
    3. db.COLLECTION_NAME.findAndModify({query:{},
    4. update:{},
    5. remove:true|false,
    6. new:true|false,
    7. sort:{},
    8. fields:{},
    9. upsert:true|false});
    10. query是查询选择器,与findOne的查询选择器相同
    11. update是要更新的值,不能与remove同时出现
    12. remove表示删除符合query条件的文档,不能与update同时出现
    13. newtrue:返回个性后的文档,false:返回个性前的,默认是false
    14. sort:排序条件,与sort函数的参数一致。
    15. fields:投影操作,与find*的第二个参数一致。
    16. upsert:与updateupsert参数一样。
    17. 不论是update的第二个参数,还是findAndModifyupdate,在不指定更新操作符的情况下,将会用指定的新值替换旧值。
    18. 比如,
    19. use iteye;
    20. db.blog.update({_id:ObjectId('......')},{title:'new title'});
    21. //上面的操作就把指定_id的文档的标题改成了‘new title’
    22. 如果指定了更新操作符,就可以实现更复杂灵活的更新操作。
    23. 可以通过更新操作符,增加或减少数值,针对数组类型的属性,做类似队列或栈的操作。
    24. 单从这一点来说,mongo要比sql数据库强大的多了。

  3. 接下来就可以在其他集合中进行应用了    
  • 插入单条
    1. db.users.insert(
    2. {
    3. _id: getNextSequence("userid"),
    4. name: "FLY"
    5. }
    6. )
    输出结果:
    1. /* 1 */
    2. {
    3. "_id" : 1.0,
    4. "name" : "FLY"
    5. }
  • 插入多条
    1. db.users.insertMany([
    2. {
    3. _id: getNextSequence("userid"),
    4. name: "QQ"
    5. },
    6. {
    7. _id: getNextSequence("userid"),
    8. name: "Wechat"
    9. },
    10. {
    11. _id: getNextSequence("userid"),
    12. name: "WeiBo"
    13. }
    14. ])
    输出结果
    1. /* 1 */
    2. {
    3. "acknowledged" : true,
    4. "insertedIds" : [
    5. 2.0,
    6. 3.0,
    7. 4.0
    8. ]
    9. }
    1. /* 1 */
    2. {
    3. "_id" : 1.0,
    4. "name" : "FLY"
    5. }
    6. /* 2 */
    7. {
    8. "_id" : 2.0,
    9. "name" : "QQ"
    10. }
    11. /* 3 */
    12. {
    13. "_id" : 3.0,
    14. "name" : "Wechat"
    15. }
    16. /* 4 */
    17. {
    18. "_id" : 4.0,
    19. "name" : "WeiBo"
    20. }
注意:
  1. 按照上面的方法,有一个问题。就是每次退出mongo重进之后,我的getNextSequence都无法正常使用了,会出现下面所示的错误:
    1. Failed to execute script.
    2. Error:
    3. ReferenceError: getNextSequence is not defined :
    4. @(shell):3:6

原因分析:
  1. 本实例中定义的函数没有保存,在mongo实例退出后会自动清零。所以需要对我们辛辛苦苦定义的函数进行保存,要不然岂不是白干了。
  2. 那好我们就保存在一个表中,这样总不至于把函数搞丢了吧。
  • 保存的方式也有两种,一种是插入
  1. db.system.js.insert({
  2. _id:"getNextSequence",
  3. value:function getNextSequence(name) {
  4. var ret = db.counters.findAndModify(
  5. {
  6. query: { _id: name },
  7. update: { $inc: { seq: 1 } },
  8. new: true
  9. }
  10. );
  11. return ret.seq;
  12. }
  13. });

 
  • 还有一种保存方法,在官网上有提到,就是用db.collection.save()函数进行保存,其实都是一样的。
    1. db.system.js.save({
    2. _id : "myAddFunction" ,
    3. value : function (name){
    4. var ret = db.counters.findAndModify(
    5. {
    6. query: { _id: name },
    7. update: { $inc: { seq: 1 } },
    8. new: true
    9. }
    10. );
    11. return ret.seq;
    12. }
    13. });


  1. 可能有人会问,为什么是system.js这个表,而不是其他的,因为这个表是专门用来保存js函数的。
  2. 如果你定义完函数退出mongo实例,会发现还是无法使用函数getNextSequence,
  3. 这是因为虽然你定义了函数,但是没有把函数引入进来,也就是说每次重新进入mongo的实例后需要把相应的js函数重新导入一遍
  1. 通过,db.loadServerScripts();把system.js中的函数,引入到mongo实例。
  2. 这也是保存到system.js中原因,mongodb数据库提供了很好的库函数进行函数引用。
  1. 还有一种引入方式如下:
  2. db.eval(‘getNextSequence(“userid”)’);
  1. db.loadServerScripts()
  2. db.users.insert(
  3. {
  4. _id: getNextSequence("userid"),
  5. name: "DD"
  6. }
  7. )
参考来源:http://blog.youkuaiyun.com/baiyvwuxia/article/details/47274243

### 实现 MongoDBID 的自功能 #### 创建 `counters` 集合 为了实现 `_id` 字段的自动长,首先需要创建一个名为 `counters` 的集合来存储序列号: ```javascript db.createCollection("counters")[^1] ``` 接着向该集合插入一条记录用于初始化计数器: ```javascript db.counters.insert({ _id: "userid", seq: 0 }) ``` 这里以 `"userid"` 为例说明如何设置初始值。 #### 使用 `findAndModify` 命令更新计数值 每当要插入新文档时,通过调用 `findAndModify()` 方法查找并修改对应的计数器条目,确保此过程具有原子性,防止多线程环境下发生冲突: ```javascript var ret = db.counters.findAndModify({ query: { _id: "userid" }, update: { $inc: { seq: 1 } }, new: true, upsert: true }); print(ret.seq); ``` 上述代码片段展示了如何安全地获取下一个可用编号,并将其打印出来。其中参数解释如下: - `query`: 定位到特定名称的计数器; - `update`: 对匹配的对象执行量操作; - `new`: 返回被更改后的最新版本的数据项而非原始数据项; - `upsert`: 如果找不到符合条件的结果,则会先插入再返回新加的内容; 最后,在准备存入的新文档中加入刚刚得到的唯一标识符作为 `_id` 属性即可完成整个流程的设计[^2]。 对于某些场景下可能存在的高并发请求情况,考虑到性能因素还可以考虑引入缓存机制或者其他分布式锁方案进一步优化效率和可靠性[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萤火AI百宝箱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值