【问题标题】:Auto increment document number in Mongo / Mongoose在 Mongo / Mongoose 中自动增加文档编号
【发布时间】:2013-01-05 10:19:50
【问题描述】:

我的应用有多个用户,每个用户都有文档。每个文档都需要有一个序列号,可能看起来像这样:2013-1、2013-2(年份和序列号),或者只是一个简单的数字:1、2、3...

目前,我在创建 Mongoose 文档时从用户设置中分配序列号。根据该序列号和用户设置的数字格式,我正在生成最终的文档编号。

我意识到,当同时创建 2 个文档时,它们将获得完全相同的编号,因为我在保存文档后在设置中增加了序列号。但是我在创建(尚未保存)文档时分配了序列号,因此两个文档的序列号将完全相同。

我显然需要一种方法来处理这个序列号在保存时自动递增...

我如何确保这个数字是唯一的并且是自动递增/生成的?

【问题讨论】:

  • [docs](hhttp://www.wiredprairie.us/blog/index.php/archives/1524) 有两个很好的例子。您可以使用计数器示例并为用户 ID 添加辅助键,以便每个用户都是唯一的。
  • 试试这个库:mongodb-autoincrement

标签: mongodb mongoose


【解决方案1】:

@emre 和 @WiredPraire 为我指明了正确的方向,但我想为我的问题提供一个完全兼容 Mongoose 的答案。我最终得到了以下解决方案:

var Settings = new Schema({
  nextSeqNumber: { type: Number, default: 1 }
});

var Document = new Schema({
  _userId: { type: Schema.Types.ObjectId, ref: "User" },
  number: { type: String }
});

// Create a compound unique index over _userId and document number
Document.index({ "_userId": 1, "number": 1 }, { unique: true });

// I make sure this is the last pre-save middleware (just in case)
Document.pre('save', function(next) {
  var doc = this;
  // You have to know the settings_id, for me, I store it in memory: app.current.settings.id
  Settings.findByIdAndUpdate( settings_id, { $inc: { nextSeqNumber: 1 } }, function (err, settings) {
    if (err) next(err);
    doc.number = settings.nextSeqNumber - 1; // substract 1 because I need the 'current' sequence number, not the next
    next();
  });
});

请注意,这种方法没有办法要求模式中的数字路径,也没有意义,因为它是自动添加的。

【讨论】:

  • +1!要详细说明其工作原理,您需要一个阻塞操作(例如写入),以便异步 save() 调用按顺序进行。使用 Find 或 Count 您希望递增的集合的“更简单”解决方案失败,因为 Find 和 Count 都是非阻塞的(读取命令),因此您不能依赖它们与(可能)多个异步的串行行为save() 来电。
  • 感谢您发布代码示例,这看起来也是我一直在寻找的解决方案。如果这对任何人都有用,我还对其进行了调整,以便仅在为创建而不是更新调用 save 时生成 ID: if (doc.isNew) { Settings.findByIdAndUpdate... } else { next(); }
  • 您确定没有遗漏什么吗?我收到此错误“对象# 没有方法'findByIdAndUpdate'”我想我必须先建立一个模型,不是吗?
【解决方案2】:

您可以通过以下方式实现:

  1. 创建序列生成器,它只是另一个保存最后一个数字的计数器的文档。
  2. 使用 mongoose 中间件 更新自动递增所需字段。

这是一个使用 todo 应用程序的工作和测试示例。

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/todoApp');

// Create a sequence
function sequenceGenerator(name){
  var SequenceSchema, Sequence;

  SequenceSchema = new mongoose.Schema({
    nextSeqNumber: { type: Number, default: 1 }
  });

  Sequence = mongoose.model(name + 'Seq', SequenceSchema);

  return {
    next: function(callback){
      Sequence.find(function(err, data){
        if(err){ throw(err); }

        if(data.length < 1){
          // create if doesn't exist create and return first
          Sequence.create({}, function(err, seq){
            if(err) { throw(err); }
            callback(seq.nextSeqNumber);
          });
        } else {
          // update sequence and return next
          Sequence.findByIdAndUpdate(data[0]._id, { $inc: { nextSeqNumber: 1 } }, function(err, seq){
            if(err) { throw(err); }
            callback(seq.nextSeqNumber);
          });
        }
      });
    }
  };
}

// sequence instance
var sequence = sequenceGenerator('todo');

var TodoSchema = new mongoose.Schema({
  name: String,
  completed: Boolean,
  priority: Number,
  note: { type: String, default: '' },
  updated_at: { type: Date, default: Date.now }
});

TodoSchema.pre('save', function(next){
  var doc = this;
  // get the next sequence
  sequence.next(function(nextSeq){
    doc.priority = nextSeq;
    next();
  });
});

var Todo = mongoose.model('Todo', TodoSchema);

您可以在节点控制台中进行如下测试

function cb(err, data){ console.log(err, data); }
Todo.create({name: 'hola'}, cb);
Todo.find(cb);

随着每个新创建的对象,您将看到优先级增加。干杯!

【讨论】:

    【解决方案3】:

    此代码取自 MongoDB 手册,它实际上描述了使 _id 字段自动递增。但是,它可以应用于任何领域。您想要的是在插入文档后检查插入的值是否存在于数据库中。如果它已经插入,重新增加值然后尝试再次插入。通过这种方式,您可以检测重复值并重新增加它们。

    while (1) {
    
        var cursor = targetCollection.find( {}, { f: 1 } ).sort( { f: -1 } ).limit(1);
    
        var seq = cursor.hasNext() ? cursor.next().f + 1 : 1;
    
        doc.f = seq;
    
        targetCollection.insert(doc);
    
        var err = db.getLastErrorObj();
    
        if( err && err.code ) {
            if( err.code == 11000 /* dup key */ )
                continue;
            else
                print( "unexpected error inserting data: " + tojson( err ) );
        }
    
        break;
    }
    

    在此示例中,f 是文档中要自动递增的字段。要完成这项工作,您需要使您的字段 UNIQUE 可以使用索引来完成。

    db.myCollection.ensureIndex( { "f": 1 }, { unique: true } )
    

    【讨论】:

    • 嗯,这可能行得通,但是知道如何用 Mongoose 实现 thsi 吗?此外,我需要确保文档编号在一个用户的上下文中是唯一的。可以有 2 个用户,都只能有一个文档,编号为 1。因此唯一索引应考虑编号和用户 _id。
    【解决方案4】:

    你可以使用mongoose-auto-increment包如下:

    var mongoose      = require('mongoose');
    var autoIncrement = require('mongoose-auto-increment');
    
    /* connect to your database here */
    
    /* define your DocumentSchema here */
    
    autoIncrement.initialize(mongoose.connection);
    DocumentSchema.plugin(autoIncrement.plugin, 'Document');
    var Document = mongoose.model('Document', DocumentSchema);
    

    您只需要初始化一次autoIncrement

    【讨论】:

    猜你喜欢
    • 2018-04-27
    • 1970-01-01
    • 2018-12-22
    • 1970-01-01
    • 1970-01-01
    • 2018-05-23
    • 1970-01-01
    • 2019-04-18
    • 2012-05-30
    相关资源
    最近更新 更多