MongoDB更新/插入文档并增加匹配的数组元素

时间:2015-08-12 11:32:18

标签: node.js mongodb mongodb-query increment monk

我使用Node.js和MongoDB和monk.js,我希望以最小的方式进行日志记录,每小时一个文档,如:

最终文档:

  

{time:YYYY-MM-DD-HH,log:[{action:action1,count:1},{action:action2,count:27},{action:action3,count:5}]}

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

例如,某人此时首先访问网页,并且action1的增加应使用查询创建以下文档:

  

{time:YYYY-MM-DD-HH,log:[{action:action1,count:1}]}

此时的其他用户访问其他网页,文档应扩展为:

  

{time:YYYY-MM-DD-HH,log:[{action:action1,count:1},{action:action2,count:1}]}

并且在访问不同的网页时,应增加count中的值。

目前我在每个动作中创建一个doc:

  

tracking.update({   时间:时刻()。格式(' YYYY-MM-DD_HH'),   行动:行动,   信息:信息   },{$ inc:{count:1}},{upsert:true},function(err){}

这可以用monk.js / mongodb吗?

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

我写了一个非常脏的解决方案,其中action-name为关键:

  

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

1 个答案:

答案 0 :(得分:1)

是的,这是非常可能的,也是一个考虑周全的问题。我在这种方法上唯一的变化是计算"时间"值作为一个真正的Date对象(在MongoDB中非常有用,也可以在操作中使用)但只是简单地“#34; round"基本日期数学的值。你可以使用" moment.js"为了相同的结果,但我发现数学很简单。

这里的另一个主要考虑因素是混合阵列" push"有可能的行动" updsert"文档操作可能是一个真正的问题,因此最好使用" multiple"更新语句,只有你想要的条件才会改变任何东西。

最好的方法是使用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操作会确保进行更改,除非该操作实际上是"插入"一份新文件。

由于所有操作都是在" Bulk"中触发的,因此唯一一次联系服务器的时间是.execute()。所以只有"一个"请求服务器,只有"一个"响应,尽管多次操作。它实际上是一个"一个"请求。

这样就满足了条件:

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

  2. 将新项目添加到当前" action"分类不存在并添加初始计数。

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

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

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