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

我使用Node.js和MongoDB与monk.js,我想以一个最小的方式做一个文件每小时的日志logging,如:

最终文件:

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

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

例如,某人在这个小时内首先访问一个网页,action1的增量应该用一个查询创build下面的文档:

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

在这个小时内的另一个用户访问另一个网页,文档应该被扩展到:

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

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

目前我创build了每个行动的文件:

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

这可能与monk.js / mongodb?

编辑:谢谢。 你的解决scheme看起来干净优雅,但看起来像我的服务器无法处理它,或者我不能让它工作。

我写了一个极其肮脏的解决scheme,以action-name为关键字:

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

是的,这是非常可能的,一个考虑周全的问题。 我所做的唯一变化就是计算“时间”值作为一个真正的Date对象(在MongoDB中非常有用,也是操作性的),而只是用基本datemath“舍入”值。 你可以使用“moment.js”来获得相同的结果,但我觉得math很简单。

另一个主要的考虑是混合数组“push”动作和可能的“updsert”文档动作可能是一个真正的问题,所以最好用“多个”更新语句处理,只有你想要的条件会改变任何东西。

最好的方法是使用MongoDB 批量操作 。

考虑一下你的数据是这样的:

 { "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()调用时联系服务器。 所以对服务器只有“一个”请求,只有“一个”响应,尽pipe有多个操作。 这实际上是“一个”的要求。

这样的条件都满足了:

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

  2. 将新项目添加到当前“操作”分类不存在的arrays,并添加一个初始计数。

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

总而言之,只要行动分类在一段时间内不会变得太大(500个数组元素应该被用作最大指导),那么也是一个很好的存储想法,而且这个更新是非常有效率和自我包含的每个时间采样一个文档。

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