【问题标题】:MongoDB aggregate with event based user time trackingMongoDB 聚合与基于事件的用户时间跟踪
【发布时间】:2019-08-05 13:22:04
【问题描述】:

首先,感谢您阅读并提出解决方案。

目前我正在尝试弄清楚如何聚合和汇总事件库用户日期时间事件。假设存储的数据如下所示:

export type TimeTrackingEvents = "start" | "pause" | "resume" | "end";
export interface ITimeTracking {
  user: string;
  event: TimeTrackingEvents;
  timestamp: Date;
}

在用户工作日后,我们的数据库中有以下数据集:

[{
  _id: 5c87f24d15e381003334f26f,
  user: 5c87bb4aad3c21e90aeda6f7,
  timestamp: 2019-03-14T07:00:00.000Z,
  event: 'start'
},
{
  _id: 5c8a5ceea708844edc9e1d31,
  user: 5c87bb4aad3c21e90aeda6f7,
  timestamp: 2019-03-14T08:00:00.000Z,
  event: 'pause'
},
{
  _id: 5c8a5d02a708844edc9e1d33,
  user: 5c87bb4aad3c21e90aeda6f7,
  timestamp: 2019-03-14T09:00:00.000Z,
  event: 'resume'
},
{
  _id: 5c8a5cf9a708844edc9e1d32,
  user: 5c87bb4aad3c21e90aeda6f7,
  timestamp: 2019-03-14T10:00:00.000Z,
  event: 'end',
}];

根据用户今天工作 2 小时的数据。 这就引出了最后一个问题:根据给定的数据集,我如何实现以下输出?

{
  _id: 5c87f24d15e381003334f26f,
  user: 5c87bb4aad3c21e90aeda6f7,
  duration: 2, // this represents the 2 hours the user has been working
}

这意味着我想将“开始”到“暂停”和“恢复”到“结束”的事件持续时间相加,而不是“暂停”和“恢复”之间的持续时间。

我一直在这里和网上搜索,但找不到合适的聚合解决方案。

到目前为止,这是我的聚合:

[
  {
    $match: {
      user: userId,
      // only today
      timestamp: {
        $gte: today,
        $lt: tomorrow,
      },
    },
  },
  { $sort: { timestamp: -1 } },
  // above works, insert magic here... ;)
]

我的 MongoDB 是第 4 版。

再次感谢大家的宝贵时间。 干杯托尼

编辑

所以现状如下: @AnthonyWinzlet 非常感谢。您提供的链接确实有帮助!

所以 ATM 我现在添加了以下内容:

{   
  $addFields: {
    timeOfDay: { // The result from '2019-03-14T10:00:00.000+00:00' is 36.000.000 which equals 10 hours
      $mod: [
        { $toLong: "$timestamp" },
        1000 * 60 * 60 * 24,
      ],
    },
  },
},

我得到像 timeOfDay:36000000 这样的字段,代表 10 小时。

我无法工作的是@JonasWilms 提到的$switch。结果/“totalTimeInMinutes”始终表示 122.400.000(即 34 小时),即使我将 ms 转换为负值:

// ...
  {
    $group: {
      _id: "result",
      totalTimeInMinutes: {
        // $sum: { $divide: ["$timeOfDay", 60 * 1000] },
        $sum: {
          $switch: {
            branches: [
              { case: "end", then: { $multiply: ["$timeOfDay", 1] } },
              { case: "start", then: { $multiply: ["$timeOfDay", -1] } },
              { case: "resume", then: { $multiply: ["$timeOfDay", -1] } },
              { case: "pause", then: { $multiply: ["$timeOfDay", 1] } },
            ],
            default: 0,
          },
        },
      },
    },
  },
// ...

这是因为 10 + 9 + 8 + 7 等于 34,但它必须是 10 + (-9) + 8 + (-7) 等于 2。

【问题讨论】:

  • 如果我是对的用户今天工作了 4 个小时?
  • @anthony-winzlet 我应该更准确。我说的是“有效”/净工作时间。这意味着:7 点到 8 点等于 1 小时,1 小时休息(8 点到 9 点)和 9 点到 10 点之间的另一个小时。

标签: javascript mongodb aggregate


【解决方案1】:

我想您可以将数组分组为一个对象并总结时间(假设所有时间都匹配):

{
   $group: {
     _id: "result", // you could directly group by user id here
     duration: { $sum: { // in rounded / floored hours
       $switch: { branches: [
          { case: "start", then: { $multiply: [{ $hour: "$timestamp"}, -1] },
          { case: "end", then: { $multiply: [{ $hour: "$timestamp" }, 1] },
          { case: "pause", then: { $multiply: [{ $hour: "$timestamp" }, -1] },
          { case: "resume", then: { $multiply: [{ $hour: "$timestamp" }, 1] }
      ]}
   }}
  }
}

【讨论】:

【解决方案2】:

感谢您将我的头脑引向正确的方向。 我想分享我的最终解决方案。我想会有一个更优雅的版本,边缘情况还没有经过测试,但它的工作原理是这样的:

[
  {
    $match: {
      // this is just for testing purpose atm
      timestamp: {
        $gte: new Date(today),
        $lt: new Date(tomorrow),
      },
    },
  },
  { $sort: { timestamp: -1 } },
  {
    $addFields: {
      timeOfDay: { // The result from '2019-03-14T10:00:00.000+00:00' is 36.000.000 which equals 10 hours
        $mod: [
          { $toLong: "$timestamp" },
          1000 * 60 * 60 * 24,
        ],
      },
    },
  },
  {
    $project: {
      timeOfDay: {
        $cond: {
          if: { $in: ["$event", ["start", "resume"]] },
          then: { $multiply: ["$timeOfDay", -1] },
          else: "$timeOfDay",
        },
      },
    },
  },
  {
    $group: {
      _id: "result",
      workingMinutesToday: {
        $sum: { $divide: ["$timeOfDay", { $multiply: [60, 1000] }] },
      },
    },
  },
]

我很乐意看到你们提供更优雅的方法,但我希望这会节省一些人的时间

干杯 托尼

【讨论】:

    猜你喜欢
    • 2021-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-20
    • 2015-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多