1

我将 Node.js 和 MongoDB 与 Monk.js 一起使用,我想以最小的方式进行日志记录,每小时一个文档,例如:

最终文档:

{时间:YYYY-MM-DD-HH,日志:[{action:action1,count:1},{action:action2,count:27},{action:action3,count:5}]}

应该通过增加一个值来创建完整的文档。

例如,有人在这个小时首先访问了一个网页,并且 action1 的增量应该创建以下带有查询的文档:

{时间:YYYY-MM-DD-HH,日志:[{action:action1,count:1}]}

在这一小时内,其他用户访问了其他网页,并且文档应扩展至:

{时间:YYYY-MM-DD-HH,日志:[{action:action1,count:1},{action:action2,count:1}]}

并且计数中的值应该在访问不同的网页时增加。

目前我为每个动作创建一个文档:

tracking.update({ time: moment().format('YYYY-MM-DD_HH'), action: action, info: info }, { $inc: {count: 1} }, { upsert: true }, function (呃){}

这对mongo.js / mongodb有可能吗?

编辑: 谢谢。您的解决方案看起来干净而优雅,但看起来我的服务器无法处理它,或者我无法让它工作。

我写了一个非常肮脏的解决方案,以动作名称为关键:

tracking.update({ time: time, ts: ts}, JSON.parse('{ "$inc": {"'+action+'": 1}}') , { upsert: true }, function (err) { });

4

1 回答 1

1

是的,这是很有可能的,也是一个经过深思熟虑的问题。我对该方法所做的唯一变化是将“时间”值计算为真实Date对象(在 MongoDB 中非常有用,并且也可以操作),而只是用基本的日期数学“四舍五入”这些值。您可以使用“moment.js”获得相同的结果,但我发现数学很简单。

这里的另一个主要考虑因素是将数组“推送”操作与可能的“updsert”文档操作混合可能是一个真正的问题,因此最好使用“多个”更新语句来处理这个问题,其中只有您想要的条件会改变任何事物。

最好的方法是使用 MongoDB Bulk Operations

考虑到您的数据是这样的:

{ "timestamp": 1439381722531, "action": "action1" }

其中“时间戳”是精确到毫秒的纪元时间戳值。所以这个处理看起来像:

 // Just adding for the listing, assuming already defined otherwise
var payload = { "timestamp": 1439381722531, "action": "action1" };

// Round to hour
var hour = new Date(
    payload.timestamp - ( payload.timestamp % ( 1000 * 60 * 60 ) )
);

// Init transaction
var bulk = db.collection.initializeOrderedBulkOp();

// Try to increment where array element exists in document
bulk.find({ 
    "time": hour,
    "log.action": payload.action
}).updateOne({
    "$inc": { "log.$.count": 1 }
});

// Try to upsert where document does not exist
bulk.find({ "time": hour }).upsert().updateOne({
    "$setOnInsert": {
        "log": [{ "action": payload.action, "count": 1 }]
    }
});

// Try to "push" where array element does not exist in matched document
bulk.find({
    "time": hour,
    "log.action": { "$ne": payload.action }
}).updateOne({
    "$push": { "log": { "action": payload.action, "count": 1 } }
});

bulk.execute();

因此,如果您查看那里的逻辑,那么您会发现,对于文档的任何给定状态,无论是否存在,这些陈述中的“一个”永远可能为真。从技术上讲,带有“upsert”的语句实际上可以匹配一个存在的文档,但是所$setOnInsert使用的操作确保没有进行任何更改,除非该操作实际上“插入”了一个新文档。

由于所有操作都是以“批量”方式触发的,因此唯一一次联系服务器是在.execute()通话中。因此,尽管有多个操作,但对服务器只有“一个”请求,并且只有“一个”响应。它实际上是“一个”请求。

这样,条件都满足了:

  1. 为不存在的当前期间创建一个新文档,并将初始数据插入到数组中。

  2. 在当前“动作”分类不存在的数组中添加一个新项目,并添加一个初始计数。

  3. 在执行语句时增加数组中指定操作的计数属性。

总而言之,是的,也是一个存储的好主意,只要动作分类在一段时间内不会变得太大(应该使用 500 个数组元素作为最大指导)并且更新非常有效且自包含每个时间样本的单个文档。

该结构也很好,非常适合其他查询和可能的附加聚合目的。

于 2015-08-12T12:37:33.800 回答