在我的项目中,我有一些用户可以完成课程的组合(称为会话)。玩课程的事实称为尝试。在尝试期间,他们可以将其关闭并稍后再回来(因此我们保留了一个timelog对象)。
我有一个来自客户端的请求,该请求需要针对每个会话返回,在特定时间段内播放了全部或部分会话的用户(及其尝试)。
在特定时间范围内,这表示客户发送开始日期和结束日期,并且在以下情况下,我们会为特定会话计数用户: -在时间范围结束之前开始了第一次尝试=>第一个<结束日期的第一个时间日志的开始 -在时间范围开始=>上次尝试的最后一个时间日志的结束>开始日期之后
这是一个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")}
author
阶段将使用session
和_id
作为$group
的尝试归为一组,然后将所有用户对该特定会话的尝试将其尝试放入数组userAttempts
。这是我的总数:
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。
答案 0 :(得分:0)
玩完聚合后,我提出了两种解决方案:
解决方案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至少在我项目的对象分发中总是更快。