Search code examples
mongodbgroupinglimitaggregation-frameworkmongodb-aggregation

Mongodb group documents and limit each group


Considering those documents:

{
    "Field1":"Test_1",
    "Speaker":1,
    "Listener":2,
    "ListenTime": ISODate("2016-10-15T14:17:49.336Z")
},
{
    "Field1":"Test_2",
    "Speaker":1,
    "Listener":2,
    "ListenTime": ISODate("2016-10-13T14:17:49.336Z")
},
{
    "Field1":"Test_3",
    "Speaker":1,
    "Listener":3,
    "ListenTime": ISODate("2016-10-10T14:10:49.336Z")
}

What i what to do is to extract with a single query in MongoDB (3.2) only those documents grouping them by Speaker and Listener, taking only the document with the oldest ListenTime.
So, in this case, the result will be:

{
    "Field1":"Test_1",
    "Speaker":1,
    "Listener":2,
    "ListenTime": ISODate("2016-10-15T14:17:49.336Z")
},
{
    "Field1":"Test_3",
    "Speaker":1,
    "Listener":3,
    "ListenTime": ISODate("2016-10-10T14:10:49.336Z")
}

Is it possible do that with a single query?


Solution

  • Run the following aggregation pipeline to get the desired result:

    db.collection.aggregate([
        { "$sort": { "ListenTime": -1 } },
        {
            "$group": {
                "_id": {
                    "Speaker": "$Speaker",
                    "Listener": "$Listener"
                },
                "Field1" : { "$first": "$Field1" },
                "ListenTime" : { "$first": "$ListenTime" }
            }
        },
        {
            "$project": {
                "Field1": 1,
                "Speaker": "$_id.Speaker",
                "Listener": "$_id.Listener",
                "ListenTime": 1,
                "_id": 0
            }
        }
    ])
    

    Sample Output

    /* 1 */
    {
        "Field1" : "Test_3",
        "ListenTime" : ISODate("2016-10-10T14:10:49.336Z"),
        "Speaker" : 1,
        "Listener" : 3
    }
    
    /* 2 */
    {
        "Field1" : "Test_1",
        "ListenTime" : ISODate("2016-10-15T14:17:49.336Z"),
        "Speaker" : 1,
        "Listener" : 2
    }