Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB/Mongoose - Querying an array of objects by date

I have the following metrics collection:

{
    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

What I would like to get is all the values between a custom date range. I've tried the following query but I still get the entire values array returned:

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

Maybe I saved my dates in a wrong format ? Any help would be greatly appreciated.

like image 476
aroundsix Avatar asked Nov 16 '25 05:11

aroundsix


1 Answers

You can run an aggregation pipeline that uses the $filter operator on the values array. The following mongo shell query demonstrates this:

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 ] }
                        ]
                    }
                }
            }
        }
    }
])

Sample Output

/* 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")
        }
    ]
}

For MongoDB 3.0, the following workaround applies:

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]
                ]
            }
        }
    }
])
like image 195
chridam Avatar answered Nov 17 '25 20:11

chridam



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!