MongoDB Collection更新:使用默认值初始化文档

时间:2017-01-09 16:24:00

标签: mongodb time-series upsert

我正在尝试使用MongoDB处理时间序列。社区采用的通用解决方案是使用子文档以不同的粒度级别存储信息(参见 Schema Design for Time Series Data in MongoDB)。

例如,请查看以下文档:

$setOnInsert

该文档由分钟信息编制索引,并包含一个子文档,用于存储每秒的更详细信息。

到目前为止一切顺利。这种方法需要优化才能正常工作:

  

另一个优化[..]正在为即将到来的时间段预分配所有文件;这永远不会导致现有文档在磁盘上增长或移动。

要实现上述优化,可以使用update方法上的db.getCollection('aCollection').update( { timestamp_minute: ISODate("2013-10-10T23:06:00.000Z"), type: “memory_used” }, { $setOnInsert: { values: {'0': 0, '1': 0, '2': 0}}, $inc: {"values.30": 1} }, { upsert: true } ) 属性。

Cannot update 'values' and 'values.30' at the same time

问题是在两个不同的操作中不可能在同一更新中使用相同的字段。以上更新istruction会生成以下错误:

type

issue上会跟踪此问题。

我的问题是:有没有解决方法?我的前缀是我无法使用任何预先分配空文档的批处理,因为我无法知道索引字段的值先验(在上例中,字段{{1}}的值。

提前致谢。

1 个答案:

答案 0 :(得分:5)

我和我的同事找到了解决方法。我们可以称之为三步初始化

请记住,MongoDB保证了单个文档的操作原子性。考虑到这一点,我们可以通过以下方式运作:

  1. 尝试更新文档,在指定的时间块正确递增计数器。不要做任何upsert,只是一个老式的更新操作。请记住,update语句的执行会返回写入的文档数。如果写入的文档数大于零,那么就完成了。
  2. 如果更新写入的文档数为零,则表示要更新的相关文档尚未出现在集合中。尝试为指定的标记插入整个文档。将所有计数器(字段值)置于零。插入语句的执行也返回写入的文档数。如果它返回零或抛出异常,请不要介意:这意味着其他一些进程已经为相同的标签插入了文档。
  3. 再次执行相同的上述更新。
  4. 代码看起来应该类似于以下代码段。

    // Firt of all, try the update
    var result = db.test.update(
      {timestamp_minute: ISODate("2013-10-10T23:06:00.000Z"), type: “memory_used”},
      {$inc: {"values.39": 1}},
      {upsert: false}
    );
    // If the update do not succeed, then try to insert the document
    if (result.nModified === 0) {
      try {
        db.test.insert(/* Put here the whole document */);
      } catch (err) {
        console.log(err);
      }
      // Here we are sure that the document exists.
      // Retry to execute the update statement
      db.test.update(/* Same update as above */);
    }
    

    如果前提条件成立,则上述过程有效:_id值应从文档中的其他字段派生。在我们的示例中,_id值为'2013-10-10T23:06:00.000Z-memory_used。只有使用这种技术,第2点的插入才会正确失败。