아래와 같은 데이터가 있다고 하자.
데이터베이스 이름은 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"} }}]) |
그럼 자바 코드에서는 어떻게 사용할 수 있을까?
아래 명령어처럼 사용하면 된다.
|
위에 [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" } } }]) |
자바 프로그래밍을 할 때는 아래처럼 하면 된다.
|
다른 예제를 한 번 들어보자.
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/
참고
https://docs.mongodb.com/manual/aggregation/
https://stackoverflow.com/questions/31643109/mongodb-aggregation-with-java-driver
'MongoDB' 카테고리의 다른 글
[MongoDB] Sharding Chunk size 바꾸기 (0) | 2019.05.14 |
---|---|
[MongoDB] YCSB 를 이용한 벤치마킹 튜토리얼 링크 (0) | 2019.05.10 |
[MongoDB] sudo service mongod start 가 실행되지 않을 때 (0) | 2019.01.29 |
[MongoDB] text 데이터를 csv 파일로 만드는 쉘 스크립트 (0) | 2018.12.01 |
[MongoDB] config servers 세팅 에러 하나 (0) | 2018.11.30 |