繁体   English   中英

比较聚合中不同对象数组的日期

[英]Compare Dates from arrays of different objects in aggregation

在我的项目中,我有一些用户可以完成课程的组合(称为会话)。 玩课程的事实称为尝试。 在尝试期间,他们可以将其关闭并稍后再回来(因此,我们保留了一个timelog对象)。

我有一个来自客户端的请求,该请求需要针对每个会话返回,在特定时间段内播放了全部或部分会话的用户(及其尝试)。

在某个时间范围内意味着客户发送开始日期和结束日期,并且如果发生以下情况,我们将为特定会话计数用户:-在时间范围结束之前第一次尝试已开始=>第一个<结束时间的第一个时间日志的开始date-在时间范围开始之后完成了最后一次尝试=>最后一次尝试的最后一个时间日志的结束>开始日期

这是一个try对象的示例(这里我们只需要使用一个对象):

{
"_id" : ObjectId("5b9148650ab5f43b5e829a4b"),
"index" : 0,
"author" : ObjectId("5acde2646055980a84914b6b"),
"timelog" : [
    {
        "started" : ISODate("2018-09-06T15:31:49.163Z"),
        "ended" : ISODate("2018-09-06T15:32:03.935Z")
    },
    ...
],
"session" : ObjectId("5b911d31e58dc13ab7586f9b")}
  • 我的想法是对尝试进行汇总,将使用authorsession作为$group阶段的_id进行$group ,并将针对该特定会话的用户所有尝试推入一个数组userAttempts
  • 然后进行$ addField阶段,以检索第一次尝试的第一个时间日志的开始字段和最后一次尝试的最后一个结束时间。
  • 最后使用这些新字段进入$ filter或$ match。

这是我的汇总:

const newDate = new Date()
_db.attempts.aggregate([
        { $match: {
            author: { $in: programSessionsData.users },
            $or: [{ programSession: { $in: programSessionIds } }, { oldTryFor: { $in: programSessionIds } }],
            globalTime: $ex,
            timelog: $ex }
        },
        {
            $group: {
                _id: {
                    user: "$author",
                    programSession: "$programSession"
                },
                userAttempts: { $push: { attemptId: "$_id", lastTimelog: { $arrayElemAt: ["$timelog", -1] }, timelog: "$timelog" } }
            }
        },
        {
            $addFields: { begin: { $reduce: {
                input: "$userAttempts",
                initialValue: newDate,
                in: {
                    $cond: {
                        if: { $lt: ["$$this.timelog.0.started", "$$value"] },
                        then: "$$this.timelog.0.started",
                        else: "$$value"
                    } }
            } } }
        }

我还在addFields阶段尝试过此操作:

{
            $addFields: { begin: { $reduce: {
                input: "$userAttempts",
                initialValue: newDate,
                in: { $min: ["$$this.timelog.0.started", "$$value] }
            } } }
}

但是每次开始都是一个空数组。 我真的不知道如何提取这两个日期,或比较它们之间的日期。

注意 :最后一个比较困难,这就是为什么我必须首先提取lastTimelog。 如果您使用其他方法,我会很乐意接受。

另外,此代码在节点服务器上,因此我无法使用ISODate。 使用的mongo版本是3.6.3。

在玩了一下聚合之后,我想出了两种解决方案:

解决方案1

_db.attempts.aggregate([
        { $match: {
            query
        },
        {
            $group: {
                _id: {
                    user: "$author",
                    programSession: "$programSession"
                },
                userAttempts: { $push: { attemptId: "$_id", timelog: "$timelog" } }
            }
        }, {
            $addFields: {
                begin: { $reduce: {
                    input: "$userAttempts",
                    initialValue: newDate,
                    in: { $min: [{ $reduce: {
                        input: "$$this.timelog",
                        initialValue: newDate,
                        in: { $min: ["$$this.started", "$$value"] }
                    } }, "$$value"] }
                } },
                end: { $reduce: {
                    input: "$userAttempts",
                    initialValue: oldDate,
                    in: { $max: [{ $reduce: {
                        input: "$$this.timelog",
                        initialValue: oldDate,
                        in: { $max: ["$$this.ended", "$$value"] }
                    } }, "$$value"] }
                } }
            }
        },
        {
            $match: {
                begin: { $lt: req.body.ended },
                end: { $gt: req.body.started }
            }
        }
    ], { allowDiskUse: true });

newDate是今天,而oldDate是过去的任意日期。 我必须链接2 reduce,因为“ $$ this.timelog.0.started”将始终不返回任何内容。 真的不知道为什么。

解决方案2

_db.attempts.aggregate([
        { $match: {
           query
        },
        {
            $addFields: {
                firstTimelog: { $arrayElemAt: ["$timelog", 0] },
                lastTimelog: { $arrayElemAt: ["$timelog", -1] }
            }
        },
        {
            $group: {
                _id: {
                    user: "$author",
                    programSession: "$programSession"
                },
                begin: { $min: "$firstTimelog.started" },
                end: { $max: "$lastTimelog.ended" },
                userAttempts: { $push: { attemptId: "$_id", timelog: "$timelog"} }
            }
        },
        {
            $match: {
                begin: { $lt: req.body.ended },
                end: { $gt: req.body.started }
            }
        }
    ], { allowDiskUse: true });

这个简单得多,看起来更简单,但是奇怪的是,根据我的测试,至少在我的项目的对象分发中,解决方案1总是更快。

暂无
暂无

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

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