아래와 같은 데이터가 있다고 하자.


데이터베이스 이름은 dataset 이고 컬렉션의 이름은 data 이다.


mongos> db.data.find()

{ "_id" : 1, "from" : 998, "to" : 807 }

{ "_id" : 2, "from" : 269, "to" : 209 }

{ "_id" : 3, "from" : 78, "to" : 105 }

{ "_id" : 4, "from" : 1, "to" : 824 }

{ "_id" : 5, "from" : 785, "to" : 244 }

{ "_id" : 6, "from" : 145, "to" : 971 }

{ "_id" : 7, "from" : 597, "to" : 644 }

{ "_id" : 8, "from" : 33, "to" : 82 }

{ "_id" : 9, "from" : 257, "to" : 900 }

{ "_id" : 10, "from" : 281, "to" : 38 }

{ "_id" : 11, "from" : 448, "to" : 24 }

{ "_id" : 12, "from" : 625, "to" : 302 }

{ "_id" : 13, "from" : 132, "to" : 170 }

{ "_id" : 14, "from" : 67, "to" : 120 }

{ "_id" : 15, "from" : 323, "to" : 145 }

{ "_id" : 16, "from" : 800, "to" : 773 }

{ "_id" : 17, "from" : 395, "to" : 524 }

{ "_id" : 18, "from" : 1, "to" : 621 }

.....


여기서 우리는 from 을 그룹으로 묶은 후 to의 합을 구하고 싶다.


아래처럼 mongo aggregate query 를 사용하여 구할 수 있다.


db.data.aggregate([{ $group: { _id:"$from", sum: {$sum:"$to"} }}]) 




그럼 자바 코드에서는 어떻게 사용할 수 있을까?


아래 명령어처럼 사용하면 된다.


 


import
java.util.Arrays; import org.bson.Document; import com.mongodb.MongoClient; import com.mongodb.MongoClientURI; import com.mongodb.client.AggregateIterable; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; .... MongoClient mongoClient = new MongoClient(new MongoClientURI("mongodb://[MongoDB IP Address]:27017")); MongoDatabase database = mongoClient.getDatabase("dataset"); MongoCollection<Document> coll = database.getCollection("data"); AggregateIterable<Document> output = coll.aggregate(Arrays.asList( new Document("$group", new Document("_id", "$from").append("sum", new Document("$sum", "$to"))))); for (Document dbObject : output) { System.out.println("_id : " + dbObject.get("_id") + " sum : " + dbObject.get("sum")); }


위에 [MongoDB IP Address]에 적절한 ip 값만 넣어주면 MongoDB 에 접속해서 dataset.data 에 접근하게 되고


coll.aggregate 메소드로 aggregate 쿼리를 보내게 된다.


응답은 AggregateIterable<Document> 형식으로 받아지고, 그것을 파싱해서 프린트하면 된다.


실행 결과 화면








다른 예를 들어보자.


from 의 unique 한 아이템들의 개수를 구하고 싶다고 하자.


아래처럼 Set 에 넣고 size 를 구하면 된다.


db.data.aggregate([{

    $group: {

    _id: null,

    count: {

        "$addToSet": "$from"

    }

    }

}, {

    $project: {

    "size": {

        $size: "$count"

    }

    }

}])




자바 프로그래밍을 할 때는 아래처럼 하면 된다.


AggregateIterable<Document> output = coll .aggregate( Arrays.asList( new Document("$group",

new Document("_id", "null").append("fromCount", new Document("$addToSet", "$from"))), new Document("$project", new Document("size", new Document("$size", "$fromCount"))))); for (Document dbObject : output) { System.out.println(" size : " + dbObject.get("size")); }















다른 예제를 한 번 들어보자.


from 과 to 모두에서 unique 한 값들의 개수를 구하고 싶다고 하자.


아래처럼, Set 에 넣고 setUnion 으로 합친다.


그 후 size 를 구하면 from 과 to 의 unique 한 값들의 개수를 구할 수 있다.


db.data.aggregate([{

    $group: {

    _id: null,

    fromCount: {

        "$addToSet": "$from"

    },

    toCount: {

        "$addToSet": "$to"

    },

    }

}, {

    $project: {

    "size": {

        $size: { "$setUnion": [ "$fromCount", "$toCount" ] }

    }

    }

}])




자바 코드로 만들면 아래와 같다.

studio 3T 를 이용해서 만든 코드임.


// Requires official Java MongoDB Driver 3.6+

import com.mongodb.Block;

import com.mongodb.MongoClient;

import com.mongodb.MongoException;

import com.mongodb.client.MongoCollection;

import com.mongodb.client.MongoDatabase;

import java.util.Arrays;

import java.util.List;

import org.bson.BsonNull;

import org.bson.Document;

import org.bson.conversions.Bson;


public class Program {

    

    public static void main(String[] args) {

        

        try (MongoClient client = new MongoClient("localhost", 27017)) {

            

            MongoDatabase database = client.getDatabase("dataset");

            MongoCollection<Document> collection = database.getCollection("data");

            

            // Created with Studio 3T, the IDE for MongoDB - https://studio3t.com/

            

            Block<Document> processBlock = new Block<Document>() {

                @Override

                public void apply(final Document document) {

                    System.out.println(document);

                }

            };

            

            List<? extends Bson> pipeline = Arrays.asList(

                    new Document()

                            .append("$group", new Document()

                                    .append("_id", new BsonNull())

                                    .append("fromCount", new Document()

                                            .append("$addToSet", "$from")

                                    )

                                    .append("toCount", new Document()

                                            .append("$addToSet", "$to")

                                    )

                            ), 

                    new Document()

                            .append("$project", new Document()

                                    .append("size", new Document()

                                            .append("$size", new Document()

                                                    .append("$setUnion", Arrays.asList(

                                                            "$fromCount",

                                                            "$toCount"

                                                        )

                                                    )

                                            )

                                    )

                            )

            );

            

            collection.aggregate(pipeline)

                    .allowDiskUse(false)

                    .forEach(processBlock);

            

        } catch (MongoException e) {

            // handle MongoDB exception

        }

    }

    

} 





예제를 하나 더 들어보자.


from 값으로 그룹을 묶은 다음, to 값들을 모두 array 로 만들어보는 쿼리를 날리고 싶다.

그리고 그 값을 새로운 mongodb document에 저장하고 싶다.

몽고 쿼리는 아래처럼 만들면 된다.


$group 으로 나온 결과값을

$out 을 통해 새로운 document 에 넣을 수 있다.


db.data.aggregate(

  [

    {

      $group:

        {

          _id: "$from",

          init: {$min:"1"},

          to: { $push: "$to" },

        }

    },

    {

        $out: "result"

    }

  ]

)




중간에 init 을 넣은 건


"원하는 컬럼 이름에 원하는 값을 넣고 싶은" 의도였다.


group 으로 묶었을 때 컬럼을 만들기 위해선 accumulator operator 를 써야 하고,


accumulator operator 를 쓰면 값이 다 다르게 나오는데


저런 야매 방식으로 min:1 을 쓰면 모든 레코드들이 하나의 똑같은 값 1을 갖게 된다.


( 모든 값에 똑같은 하나의 숫자가 필요했기 때문에 저런 식으로 넣음. )


저렇게 야매로 넣는 것 말고 다른 방법이 있으면 그걸로 넣자..... 이상한 것 같아.




아무튼, 위의 몽고 쿼리를 java code 로 바꾸면 아래처럼 된다.


역시 studio T3의 도움을 받아 converting 을 했다.


// Requires official Java MongoDB Driver 3.6+

import com.mongodb.Block;

import com.mongodb.MongoClient;

import com.mongodb.MongoException;

import com.mongodb.client.MongoCollection;

import com.mongodb.client.MongoDatabase;

import java.util.Arrays;

import java.util.List;

import org.bson.Document;

import org.bson.conversions.Bson;


public class Program {

    

    public static void main(String[] args) {

        

        try (MongoClient client = new MongoClient("localhost", 27017)) {

            

            MongoDatabase database = client.getDatabase("dataset");

            MongoCollection<Document> collection = database.getCollection("data");

            

            // Created with Studio 3T, the IDE for MongoDB - https://studio3t.com/

            

            Block<Document> processBlock = new Block<Document>() {

                @Override

                public void apply(final Document document) {

                    System.out.println(document);

                }

            };

            

            List<? extends Bson> pipeline = Arrays.asList(

                    new Document()

                            .append("$group", new Document()

                                    .append("_id", "$from")

                                    .append("init", new Document()

                                            .append("$min", "1")

                                    )

                                    .append("to", new Document()

                                            .append("$push", "$to")

                                    )

                            ), 

                    new Document()

                            .append("$out", "result")

            );

            

            collection.aggregate(pipeline)

                    .allowDiskUse(false)

                    .forEach(processBlock);

            

        } catch (MongoException e) {

            // handle MongoDB exception

        }

    }

    

}







aggregate 하다가 memory limit 에 걸렸을 경우

아래와 같은 에러가 뜬다.


com.mongodb.MongoCommandException: Command failed with error 16945 (Location16945): 'Exceeded memory limit for $group, but didn't allow external sort. Pass allowDiskUse:true to opt in.' on server mongo-router:27017. 


경고 로그에서 말 하는 대로 allowDiskUse 를 true 로 만들어 보자.

db.data.aggregate(

  [

    {

      $group:

        {

          _id: "$from",

          init: {$min:"1"},

          to: { $push: "$to" },

        }

    },

    {

        $out: "result"

    }

  ],

  {allowDiskUse : true}

) 



자바 코드에서는 collection.aggregate 하는 부분에 있는 allowDiskUse 의 파라미터를 true 로 바꿔주면 됨.

            collection.aggregate(pipeline)

                    .allowDiskUse(true)

                    .forEach(processBlock); 















자바 코드 기타 예제


MongoCollection<Document> collection = database.getCollection("myCollection");

AggregateIterable<Document> output = collection.aggregate(Arrays.asList(
        new Document("$unwind", "$views"),
        new Document("$match", new Document("views.isActive", true)),
        new Document("$sort", new Document("views.date", 1)),
        new Document("$limit", 200),
        new Document("$project", new Document("_id", 0)
                    .append("url", "$views.url")
                    .append("date", "$views.date"))
        ));

// Print for demo
for (Document dbObject : output)
{
    System.out.println(dbObject);
}









mongo aggregate query 를 java 나 python 등의 code 로 변환시켜주는 툴이 있다.


https://studio3t.com/knowledge-base/articles/query-code/





30일 무료 평가판으로 사용 가능하니 적극 이용하자.











참고 


https://docs.mongodb.com/manual/aggregation/

https://stackoverflow.com/questions/31643109/mongodb-aggregation-with-java-driver














+ Recent posts