繁体   English   中英

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

[英]MongoDB update/insert document and Increment the matched array element

我将Node.js和MongoDB与monk.js结合使用,我希望以最小的方式每小时记录一个文档,例如:

最终文件:

{时间:YYYY-MM-DD-HH,日志:[{动作:action1,计数:1},{动作:action2,计数:27},{动作:action3,计数:5}]}

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

例如,这个小时有人首先访问网页,而action1的增加应创建带有查询的以下文档:

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

在这个小时内,另一个用户访问了另一个网页,文档应被排除在:

{时间:YYYY-MM-DD-HH,日志:[{动作:action1,计数:1},{动作:action2,计数:1}]}

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

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

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

monk.js / mongodb是否可能?

编辑:谢谢。 您的解决方案看上去干净优雅,但看起来我的服务器无法处理它,或者我要nooby使其正常工作。

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

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

是的,这是很有可能的,也是一个深思熟虑的问题。 我对该方法所做的唯一修改是将“时间”值计算为一个实际的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. 执行语句后,增加数组中指定操作的count属性。

总而言之,是有可能的,而且对于存储来说也是个好主意,只要操作分类在一段时间内不会增长得太大(应将500个数组元素用作最大指导)并且更新非常有效且包含在其中每个时间样本一个文档。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM