【问题标题】:Auto-increment field in MongoDb with async bulk insertion具有异步批量插入的 MongoDb 中的自动增量字段
【发布时间】:2014-10-30 06:05:05
【问题描述】:

我正在看这篇关于 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.
});

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

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

你有什么好的解决办法吗?

【问题讨论】:

  • 获取自定义 _id 值通常不是一个好主意,除非您有一个非常好的案例。但是“批量”对您意味着什么?您是否始终知道要“批量”插入多少个文档?或者你至少可以吗?
  • 这不适用于 _id 字段,但我知道它仍然会产生同样的问题。这是为了增加发票编号。有人告诉我,人们比我更喜欢会计,这非常可取。批量插入:docs.mongodb.org/manual/core/bulk-inserts 我可能会插入 5 到 500 个文档。
  • 但是您确实知道您事先处理的金额对吗?所以你不是从一个流中处理,如果你是,那么你仍然有可能在执行之前知道项目的数量?这基本上就是我要澄清的内容。您还想要“批量 API”表单吗?这在 mongoskin 中可能看起来并不简单,但这是可能的。
  • 我总是知道要插入的项目数量是的。我想要的是获取发票对象的数组(长度已知)并将其提供给 db.invoices.insert。我希望那里的所有发票对象都有一个递增的 invoiceNr 字段。
  • 我现在可能正在寻找解决方案...午餐和思考时间是件好事 :)

标签: javascript node.js mongodb mongoskin


【解决方案1】:

因此文档推荐.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 ) );
    }
  );

});

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

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

正如您从 cmets 中了解到的,这就是我试图引导您的地方,您可能已经掌握了基础知识。没有什么说您不能将该计数器增加超过 1,因此只需增加您想要的数量即可。

【讨论】:

  • 这其实很聪明!谢谢你的想法。
猜你喜欢
  • 2012-05-29
  • 2015-10-14
  • 2014-01-10
  • 1970-01-01
  • 2012-03-10
  • 2016-10-31
  • 1970-01-01
  • 2014-12-25
  • 2020-05-22
相关资源
最近更新 更多