日期操作insede和数组(aggreagation)

时间:2016-11-15 05:20:00

标签: node.js mongodb

我想使用mongoDB和nodeJS构建在线测试应用程序。管理员可以查看用户测试历史记录(使用日期过滤器选项)。

如果我只想显示测试结果数组包含admin指定日期的用户,如何进行查询。

日期过滤器将基于来自scheduledAt.startTime的日,月,年,我认为我必须使用聚合框架来实现此目的。

假设我有以下用户文档:

{
    "_id" : ObjectId("582a7b315c57b9164cac3295"),
    "username" : "lalalala@gmail.com",
    "displayName" : "lalala",
    "testResults" : [ 
        {
            "applyAs" : [ 
                "finance"
            ],
            "scheduledAt" : {
                "endTime" : ISODate("2016-11-15T16:00:00.000Z"),
                "startTime" : ISODate("2016-11-15T01:00:00.000Z")
            },
            "results" : [ 
                ObjectId("582a7b3e5c57b9164cac3299"), 
                ObjectId("582a7cc25c57b9164cac329d")
            ],
            "_id" : ObjectId("582a7b3e5c57b9164cac3296")
        },
        { 
           ..... 
        }
    ],
    "password" : "andi",
}

testResults文档:

{
    "_id" : ObjectId("582a7cc25c57b9164cac329d"),
    "testCategory" : "english",
    "testVersion" : "EAX",
    "testTakenTime" : ISODate("2016-11-15T03:10:58.623Z"),
    "score" : 2,
    "userAnswer" : [ 
        {
            "answer" : 1,
            "problemId" : ObjectId("581ff74002bb1218f87f3fab")
        }, 
        {
            "answer" : 0,
            "problemId" : ObjectId("581ff78202bb1218f87f3fac")
        }, 
        {
            "answer" : 0,
            "problemId" : ObjectId("581ff7ca02bb1218f87f3fad")
        }
    ],
    "__v" : 0
}

到目前为止我尝试的内容如下所示。如果我想计算总文档数,我应该更改聚合框架的哪一部分。因为在下面的查询中,totalData是按每个组而不是每个返回文档的总和。

User
      .aggregate([
        {
          $unwind: '$testResults'
        },
        {
          $project: {
            '_id': 1,
            'displayName': 1,
            'testResults': 1,
            'dayOfTest': { $dayOfMonth: '$testResults.scheduledAt.startTime' },
            'monthOfTest': { $month: '$testResults.scheduledAt.startTime' },
            'yearOfTest': { $year: '$testResults.scheduledAt.startTime' }
          }
        },
        {
          $match: {
            dayOfTest: date.getDate(),
            monthOfTest: date.getMonth() + 1,
            yearOfTest: date.getFullYear()
          }
        },
        {
          $group: {
            _id: {id: '$_id', displayName: '$displayName'},
            testResults: {
              $push: '$testResults'
            },
            totalData: {
              $sum: 1
            }
          }
        },
      ])
      .then(function(result) {
        res.send(result);
      })
      .catch(function(err) {
        console.error(err);
        next(err);
      });

1 个答案:

答案 0 :(得分:0)

你可以尝试这样的事情。添加了项目阶段,以便在传递的日期中任何结果元素匹配时保留测试结果。将此添加为管道中的第一步,您可以按照自己的方式添加分组阶段。

$ map在每个测试结果元素中应用日期传递和开始日期之间的等于比较,并生成具有true和false值的数组。 $ anyElementTrue检查此数组,并且仅当数组中至少有一个true值时才返回true。匹配阶段仅包含匹配值为true的元素。

aggregate([{
    "$project": {
        "_id": 1,
        "displayName":1,
        "testResults": 1,
        "matched": {
            "$anyElementTrue": {
                "$map": {
                    "input": "$testResults",
                    "as": "result",
                    "in": {
                        "$eq": [{ $dateToString: { format: "%Y-%m-%d", date: '$$result.scheduledAt.startTime' } }, '2016-11-15']
                    }
                }
            }
        }
    }
}, {
    "$match": {
        "matched": true
    }
}])

替代版本:

与上述版本类似,但这个版本将项目和匹配阶段合二为一。带有$ redact的$ cond用于匹配,当找到匹配时,它会保留完整的树或者丢弃它。

aggregate([{
    "$redact": {
        "$cond": [{
                "$anyElementTrue": {
                    "$map": {
                        "input": "$testResults",
                        "as": "result",
                        "in": {
                            "$eq": [{
                                $dateToString: {
                                    format: "%Y-%m-%d",
                                    date: '$$result.scheduledAt.startTime'
                                }
                            }, '2016-11-15']
                        }
                    }
                }
            },
            "$$KEEP",
            "$$PRUNE"
        ]
    }
}])