【问题标题】:MongoDB update/insert document and Increment the matched array elementMongoDB更新/插入文档并增加匹配的数组元素
【发布时间】:2015-11-04 23:45:26
【问题描述】:

我将 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({ 时间:moment().format('YYYY-MM-DD_HH'), 行动:行动, 信息:信息 }, { $inc: {count: 1} }, { upsert: true }, 函数(错误){}

mongodb 可以做到这一点吗?

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

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

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

【问题讨论】:

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


    【解决方案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 个数组元素作为最大指导)并且更新非常有效并且每个时间样本都包含在一个文档中。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-19
      • 1970-01-01
      • 2020-12-05
      • 1970-01-01
      • 1970-01-01
      • 2018-10-23
      • 2019-06-21
      相关资源
      最近更新 更多