【问题标题】:MongoDB/Mongoose - Querying an array of objects by dateMongoDB/Mongoose - 按日期查询对象数组
【发布时间】:2016-12-05 10:20:19
【问题描述】:

我有以下指标集合:

{
    name: "Hello",
    values: [
        {
            value: 2629,
            date: "2016-10-28T07:00:00.000Z",
            _id: "58453abfef7aaa15ac1fdee8"
        },
        {
            value: 1568,
            date: "2016-10-29T07:00:00.000Z",
            _id: "58453abfef7aaa15ac1fdee7"
        },
        {
            value: 1547,
            date: "2016-10-30T07:00:00.000Z",
            _id: "58453abfef7aaa15ac1fdee6"
        },
        {
            value: 1497,
            date: "2016-10-31T07:00:00.000Z",
            _id: "58453abfef7aaa15ac1fdee5"
        },
        {
            value: 3031,
            date: "2016-11-01T07:00:00.000Z",
            _id: "58453abfef7aaa15ac1fdee4"
        },
        {
            value: 2559,
            date: "2016-11-02T07:00:00.000Z",
            _id: "58453abfef7aaa15ac1fdee3"
        },
        {
            value: 2341,
            date: "2016-11-03T07:00:00.000Z",
            _id: "58453abfef7aaa15ac1fdee2"
        },
        {
            value: 2188,
            date: "2016-11-04T07:00:00.000Z",
            _id: "58453abfef7aaa15ac1fdee1"
        },
        {
            value: 3280,
            date: "2016-11-05T07:00:00.000Z",
            _id: "58453abfef7aaa15ac1fdee0"
        },
        {
            value: 4638,
            date: "2016-11-06T07:00:00.000Z",
            _id: "58453abfef7aaa15ac1fdedf"
        }
    ]
},
.... more of the same

我想得到的是自定义日期范围之间的所有值。 我尝试了以下查询,但仍然返回了整个值数组:

{
    name: "Hello", 
    values: {
        $elemMatch: {
            date: {
                $lt: "2016-11-03T07:00:00.000Z", 
                $gt: "2016-10-28T07:00:00.000Z" 
            }
        }
    }
}

也许我以错误的格式保存了我的日期?任何帮助将不胜感激。

【问题讨论】:

    标签: node.js mongodb mongodb-query


    【解决方案1】:

    您可以在 values 数组上运行使用 $filter 运算符的聚合管道。以下 mongo shell 查询演示了这一点:

    var start = new Date("2016-10-28T07:00:00.000Z"),
        end = new Date("2016-11-03T07:00:00.000Z");
    
    db.metrics.aggregate([
        { 
            "$match": { 
                "name": "Hello",
                "values.date": { "$gt": start, "$lt": end }
            } 
        },
        {
            "$project": {
                "name": 1,
                "values": {
                    "$filter": {
                        "input": "$values",
                        "as": "value",
                        "cond": { 
                            "$and": [
                                { "$gt": [ "$$value.date", start ] },
                                { "$lt": [ "$$value.date", end ] }
                            ]
                        }
                    }
                }
            }
        }
    ])
    

    样本输出

    /* 1 */
    {
        "_id" : ObjectId("5845453145fda1298fa50db9"),
        "name" : "Hello",
        "values" : [ 
            {
                "value" : 1568,
                "date" : ISODate("2016-10-29T07:00:00.000Z"),
                "_id" : ObjectId("58453abfef7aaa15ac1fdee7")
            }, 
            {
                "value" : 1547,
                "date" : ISODate("2016-10-30T07:00:00.000Z"),
                "_id" : ObjectId("58453abfef7aaa15ac1fdee6")
            }, 
            {
                "value" : 1497,
                "date" : ISODate("2016-10-31T07:00:00.000Z"),
                "_id" : ObjectId("58453abfef7aaa15ac1fdee5")
            }, 
            {
                "value" : 3031,
                "date" : ISODate("2016-11-01T07:00:00.000Z"),
                "_id" : ObjectId("58453abfef7aaa15ac1fdee4")
            }, 
            {
                "value" : 2559,
                "date" : ISODate("2016-11-02T07:00:00.000Z"),
                "_id" : ObjectId("58453abfef7aaa15ac1fdee3")
            }
        ]
    }
    

    对于 MongoDB 3.0,适用以下解决方法:

    var start = new Date("2016-10-28T07:00:00.000Z"),
        end = new Date("2016-11-03T07:00:00.000Z");
    
    db.metrics.aggregate([
        { 
            "$match": { 
                "name": "Hello",
                "values.date": { "$gt": start, "$lt": end }
            } 
        },
        {
            "$project": {
                "name": 1,
                "values": {
                    "$setDifference": [
                        {
                            "$map": {
                                "input": "$values",
                                "as": "value",
                                "in": {
                                    "$cond": [
                                        { 
                                            "$and": [
                                                { "$gt": [ "$$value.date", start ] },
                                                { "$lt": [ "$$value.date", end ] }
                                            ]
                                        },
                                        "$$value",
                                        false
                                    ]
                                }
                            }
                        },
                        [false]
                    ]
                }
            }
        }
    ])
    

    【讨论】:

    • 嗨,我收到以下错误:"errmsg" : "exception: invalid operator '$filter'", "code" : 15999, "ok" : 0
    • 您的 MongoDB 服务器版本是多少?
    • 当前版本:3.0.12
    • 在这种情况下,除非您将版本升级到 3.2,否则上述方法将不起作用。让我用解决方法更新我的答案。
    • 哇,好用。非常感谢。我研究 Mongo 的时间不长,所以事情还没有像应该的那样清楚。非常感谢您的帮助。
    【解决方案2】:

    MongoDB 2.2+ 中的 Aggregation 框架提供了 Map/Reduce 的替代方案。 $unwind 运算符可用于将您的 values 数组分隔为可匹配的文档流:

    db.tmp.aggregate(
        // Start with a $match pipeline which can take advantage of an index and limit documents processed
        { $match : {
            name: "Hello",
            "values.date": {
                $lt: "2016-11-03T07:00:00.000Z",
                $gt: "2016-10-28T07:00:00.000Z" }
        }},
        { $unwind : "$values" },
        { $match : {
            name: "Hello",
            "values.date": {
                $lt: "2016-11-03T07:00:00.000Z",
                $gt: "2016-10-28T07:00:00.000Z" }
        }}
    )
    

    样本输出

    {
    "_id":ObjectId("5845432720ce37bdc7e9ca1c"),
       "name":"Hello",
       "values":{  
          "value":1568,
          "date":"2016-10-29T07:00:00.000Z",
          "_id":"58453abfef7aaa15ac1fdee7"
       }
    },{  
       "_id":ObjectId("5845432720ce37bdc7e9ca1c"),
       "name":"Hello",
       "values":{  
          "value":1547,
          "date":"2016-10-30T07:00:00.000Z",
          "_id":"58453abfef7aaa15ac1fdee6"
       }
    },{  
       "_id":ObjectId("5845432720ce37bdc7e9ca1c"),
       "name":"Hello",
       "values":{  
          "value":1497,
          "date":"2016-10-31T07:00:00.000Z",
          "_id":"58453abfef7aaa15ac1fdee5"
       }
    },{  
       "_id":ObjectId("5845432720ce37bdc7e9ca1c"),
       "name":"Hello",
       "values":{  
          "value":3031,
          "date":"2016-11-01T07:00:00.000Z",
          "_id":"58453abfef7aaa15ac1fdee4"
       }
    },{  
       "_id":ObjectId("5845432720ce37bdc7e9ca1c"),
       "name":"Hello",
       "values":{  
          "value":2559,
          "date":"2016-11-02T07:00:00.000Z",
          "_id":"58453abfef7aaa15ac1fdee3"
       }
    }
    

    【讨论】:

    • 嗨。现在只返回一个值。我想获取 2016-10-28 和 2016-11-03 之间的所有值。
    • 再检查一遍!
    • 它现在只返回一个空数组。
    猜你喜欢
    • 2014-05-13
    • 1970-01-01
    • 2016-10-03
    • 2012-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-28
    相关资源
    最近更新 更多