Search code examples
javamongodb-queryaggregation-frameworkmongodb-aggregation

Mongo DB + java grouping documents by feild on selected date range


I am new to MongoDB, using it in Java using mongoJavaDriver.jar.

I have more documents in my collection, below is a part to sample it.

{
    "_id" : ObjectId("57ee4767d782023f80bd4f97"),
    "USER_NAME" : "[email protected]",
    "LOGIN" : ISODate("2016-09-29T18:30:00Z"),
    "LOGOUT" : ISODate("2016-09-30T11:07:19.598Z"),
    "LONGITUDE" : "long",
    "LATITUDE" : "lat",
    "SOURCE" : "Web",
    "LAST_UPDATED" : ISODate("2016-09-30T11:07:19.598Z")
}
{
    "_id" : ObjectId("57ee4767d782023f80bd4f98"),
    "USER_NAME" : "[email protected]",
    "LOGIN" : ISODate("2016-09-29T18:30:00Z"),
    "LOGOUT" : ISODate("2016-09-30T11:07:19.601Z"),
    "LONGITUDE" : "long",
    "LATITUDE" : "lat",
    "SOURCE" : "Web",
    "LAST_UPDATED" : ISODate("2016-09-30T11:07:19.601Z")
}
{
    "_id" : ObjectId("57ee4767d782023f80bd4f99"),
    "USER_NAME" : "[email protected]",
    "LOGIN" : ISODate("2016-09-29T18:30:00Z"),
    "LOGOUT" : ISODate("2016-09-30T11:07:19.603Z"),
    "LONGITUDE" : "long",
    "LATITUDE" : "lat",
    "SOURCE" : "Web",
    "LAST_UPDATED" : ISODate("2016-09-30T11:07:19.603Z")
}
{
    "_id" : ObjectId("57ee4767d782023f80bd4f9e"),
    "USER_NAME" : "[email protected]",
    "LOGIN" : ISODate("2016-09-29T18:30:00Z"),
    "LOGOUT" : ISODate("2016-09-30T11:07:19.608Z"),
    "LONGITUDE" : "long",
    "LATITUDE" : "lat",
    "SOURCE" : "Web",
    "LAST_UPDATED" : ISODate("2016-09-30T11:07:19.608Z")
}
{
    "_id" : ObjectId("57ee4767d782023f80bd4f9f"),
    "USER_NAME" : "[email protected]",
    "LOGIN" : ISODate("2016-09-29T18:30:00Z"),
    "LOGOUT" : ISODate("2016-09-30T11:07:19.609Z"),
    "LONGITUDE" : "long",
    "LATITUDE" : "lat",
    "SOURCE" : "Web",
    "LAST_UPDATED" : ISODate("2016-09-30T11:07:19.609Z")
}

I am able to run query to find documents on selected date range and it returns some value.

My Code:

    Date current = new Date();
    Date current2  = new Date(current .getYear(), current.getMonth(), current.getDate()-days);
    current2.setHours(0);
    current2.setMinutes(0);
    BasicDBObject dateRange = new BasicDBObject ("$gte",current2 );
    dateRange.put("$lt", new Date(current.getYear(), current.getMonth(), current.getDate()+1));
    BasicDBObject query = new BasicDBObject("LAST_UPDATED", dateRange);
    System.out.println(collection.find().count());
    System.out.println(collection.find(query).count());

Total documents count in this collection is 155.

Count based on the selected date is 37.

In these selected 37 documents, there are redundant values in username. I want to group them based on the USER_NAME field. so that this count will become less than 37. How can I do that?


Solution

  • You can use the aggregation framework to carry out the needed aggregation. Consider running the following aggregation framework pipeline in mongo shell which basically uses the $match filter to restrict documents getting into the pipeline for processing based on the date range query supplied, then groups the documents by the USER_NAME field using $group and count the distinct values using $sum:

    var now = new Date(),
        days = 4,
        start = new Date(now.getYear(), now.getMonth(), now.getDate()-days),
        end = new Date(now.getYear(), now.getMonth(), now.getDate()+1);
    end.setHours(0);
    end.setMinutes(0);
    var pipeline = [
        { "$match": { "LAST_UPDATED": { "$gte": start, "$lte": end } } },
        {
            "$group": {
                "_id": "$USER_NAME",
                "count": { "$sum": 1 }
            }
        }
    ];
    db.collection.aggregate(pipeline);
    

    Converting the above to Java becomes:

    public class JavaAggregation {
        public static void main(String args[]) throws UnknownHostException {
    
            MongoClient mongo = new MongoClient();
            DB db = mongo.getDB("test"); // your database name
    
            DBCollection coll = db.getCollection("collectionName"); // your collection name
    
            // create the pipeline operations, first with the $match
            Date now = new Date();
            Date start = new Date(now.getYear(), now.getMonth(), now.getDate()-days);
            Date end  = new Date(now.getYear(), now.getMonth(), now.getDate()+1);
            end.setHours(0);
            end.setMinutes(0);
            DBObject match = new BasicDBObject("$match",
                                new BasicDBObject("LAST_UPDATED",
                                    new BasicDBObject("$gte", start).append("$lt", end)
                                )
                            );
    
            // build the $group operations
            DBObject groupFields = new BasicDBObject( "_id", "$USER_NAME");
            groupFields.put("count", new BasicDBObject( "$sum", 1));
    
            DBObject group = new BasicDBObject("$group", groupFields);
            List<DBObject> pipeline = Arrays.asList(match, group);
    
            AggregationOutput output = coll.aggregate(pipeline);
    
            for (DBObject result : output.results()) {
                System.out.println(result);
            }
        }
    }