2

我正在看这篇关于 MongoDb 中自动递增字段的文章。 http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/

这在理论上似乎很好,但在我的所有数据库调用都是异步的应用程序中不起作用。Ps,我正在使用 Mongoskin。考虑一下:

var getNextSequence function (name) {
  db.counters.findAndModify({_id: 'invoiceNr'}, {}, {$inc: {seq: 1}}, {new: true}, function(err, doc) {
    if (err) {
      return next(err);
    }
    return doc.seq;
  });
}; 

var seq = getNextSequence('invoiceNr');
console.log(seq);

这个(Console.log)在执行时当然没有任何价值......

这让我想到了嵌套回调(经典的 Node 风格):

db.counters.findAndModify({_id: 'invoiceNr'}, {}, {$inc: {seq: 1}}, {new: true}, function(err, doc) {
  if (err) {
    return next(err);
  }
  var seq = getNextSequence('invoiceNr');
  // Do something with the seq, like using it when inserting another document.
});

这对于一个文档来说可以正常工作,但是如果我进行批量插入(传递要插入的多个文档的数组),我看不出这将如何工作。我需要批量插入。

回到循环和单次插入似乎不是一个好的解决方案。

您对此有什么好的解决方案吗?

4

1 回答 1

3

因此文档推荐.findAndModify()这种类型的操作,因为“更新/递增”和检索结果的操作是“原子”操作。当然,如果您一次插入 500 个文档,您不想返回“获取下一个增量”500 次,因为那太疯狂了。但是您缺少概念中的某些内容。

谁说你必须递增1?简单地说,当您知道要插入 500 个项目时,您只想将当前计数器增加 501,以便下一个请求下一个计数器值的操作获得已经增加了 501 的值,因此该部分是好的。

现在您要求计数器增加 500 块,您基本上只是使用它们。因此,您知道要使用的第一个值基本上从您的返回值减去 500 开始,并且您希望在实际返回的值上结束插入。

所以你想要一些这样的代码:

var async = require('async'),
    mongo = require('mongoskin'),
    db = mongo.db('mongodb://localhost/test');


// The general incrementer

function getNextSequence( name, increment, callback ) {

  // Handling passing in "increment" as optional
  var args = Array.prototype.slice.call(arguments, 1);

  callback = args.pop();
  increment = args.length ? args.shift() || 1 : 1; // default 1

  db.collection('counters').findAndModify(
    { "_id": name },
    {},
    { "$inc": { "seq": increment } },
    { "new": true, "upsert": true },
    function(err,doc) {
      callback(err,doc.seq);
    }
  );

}

// Meat of your insertion logic

db.collection('target',function(err, collection) {

  var arrayToInsert = []; // simulating, but 500 items long assumed
  for (var x=0; x < 500; x++) {
    arrayToInsert.push({
      "x": x
    });
  }

  var bulk = collection.initializeOrderedBulkOp();

  async.waterfall(
    [
      function(callback) {
        getNextSequence('invoiceNo',arrayToInsert.length + 1,callback);
      },
      function(seq,callback) {
        var current = seq - arrayToInsert.length;
        var index = 0;

        async.whilst(
          function() { return index < arrayToInsert.length },
          function(callback) {
            doc = arrayToInsert[index];
            doc["invoice"] = current;
            bulk.insert( doc );
            index++;
            current++
            callback();
          },
          function(err) {
            bulk.execute(callback);
          }
        );
      }
    ],
    function(err,result) {
      if (err) throw err;
      // Log BulkWrite result
      console.log( JSON.stringify( result, undefined, 2 ) );
    }
  );

});

嗯,这是基本的模拟器,但你应该明白了。本质上,只需为您打算插入多少条目获取一个“值”块,并在插入实际文档时使用每个“值”。

最坏的情况?插入在某处失败,并非所有值都最终出现在文档中。所以呢?没关系,因为关键是您已经增加了计数器,并且没有其他进程可以从计数器中获取相同的“值”。

正如您可以从评论中收集到的那样,这就是我试图引导您的地方,您可能已经制定了基础知识。没有什么说你不能将该计数器增加超过 1,所以只需增加你想要的数量。

于 2014-09-05T12:23:23.040 回答