자 이번엔 이전 쿼리에서 평균을 내어 보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | db.users.aggregate([ //stage 별로 나뉘며 앞에서 처리 된걸 가지고 다음 stage로 전달 //stage 1 { $match : { "index" :{ $gte: 10000 } } }, //stage2 { $group:{ "_id" : "$eyeColor", "sum" : { $sum : 1 } } }, //stage3 { $group:{ "_id" : "$_id", "avg" : { $avg : "$sum" } } } ]); | cs |
어라.. 먼가 바뀌었다. 위에 $group의 값을 보면 "$eyeColor"가 존재한다. 이 말은 users의 field에 eyeColor 라는넘을 사용하고 싶으면 위의 예시 처럼 사용하면 된다. 마지막 $group에 또 "$sum"이라는 넘이 나온다. 이 "$sum"이 앞서 있던 stage의 key였던 sum이 $sum으로 변경된 것이다.
결론은 collection의 field 명 -> "$필드명" 이전 statge key 값 -> 다음 stage의 "$key" 로 사용 할 수 있다.
근데 결과가 이전에 봤던거랑 같다. 이유는 $avg의 평균을 내면 그 값이 나오므로 ... 그럼 진짜 평균을 내보자. "각 eyeColor의 전체나이와 전체 나이의 평균" 을 내어보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | db.users.aggregate([ //stage 별로 나뉘며 앞에서 처리 된걸 가지고 다음 stage로 전달 //stage 1 { $group : { "_id" : "$eyeColor" , "ageSum" : { $sum : "$age" }, "ageCount" : { $sum : 1 } } }, //stage2 { $project : { //"_id" : 1, "ageAVG" : { $divide : ["$ageSum" ,"$ageCount"] } } } ]); | cs |
또 새로운게 등장 $project 이넘은 없는 필드를 만들거나 기존 필드를 안보이고 싶을때 사용 한다고 공식사이트에 명시 되있다. 위의 예제를 보면 각 stage별로 값들을 전달한 것들을 알 수있다.
이걸로 aggregation은 끗....
aggregation은 '집합' 이란 뜻이다. 고로, 특정 데이터의 통계를 뽑을 때 많이 쓰인다. mapreduce는 통계의 추이를 뽑고 싶을 때 사용되며 aggregation은 양이 적고, 휘발성인 통계를 뽑을 때 사용하면 좋다.
사용법은 공식 싸이트 https://docs.mongodb.org/manual/reference/operator/aggregation/ 이넘을 참고하자. 또는 http://hongtaey.tistory.com/57 이 분 역시 나름 정리를 잘하신듯.. 보인다.
그럼 한번 해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | use dbname db.users.find(); //result /* 1 */ { "_id" : "564c3240e5aa2a5f7c7bd7f5", "index" : 0, "isActive" : false, "age" : 30, "eyeColor" : "green", "name" : "Rodgers Grant", "gender" : "male", "company" : "VERTON", "email" : "rodgersgrant@verton.com" } ........................생략........................ | cs |
1 2 3 4 5 6 7 8 9 | //aggregation db.users.aggregate( //stage 별로 나뉘며 앞에서 처리 된걸 가지고 다음 stage로 전달 [{ $match : { "eyeColor" :"green" } }] ); | cs |
aggregation이라는 함수로 내부에 array를 가지며 이를 다시 object 형태를 가진다. 여기서 중요한 것은 각 object의 순서 마다 stage 라 칭하며 , pipeline 형태로 다음 state로 전달하게 된다.
위의 결과는 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | { "result" : [ { "_id" : "564d1acb42b552b7a4e93ff4", "index" : 19347, "isActive" : true, "age" : 30, "eyeColor" : "green", "name" : "Spencer Brady", "gender" : "male", "company" : "KIDSTOCK", "email" : "spencerbrady@kidstock.com" }, { "_id" : "564c3240e5aa2a5f7c7bd7f5", "index" : 0, "isActive" : false, "age" : 30, "eyeColor" : "green", "name" : "Rodgers Grant", "gender" : "male", "company" : "VERTON", "email" : "rodgersgrant@verton.com" } ............................생략.............................................. | cs |
와 잘나온다. 그리고 쉽다!!!!! 흠.. 그럼 stage라고 하는넘을 해보자.
이제 위의 결과를 가지고 응용하자. "눈이 녹색이고, 나이가 30보다 많은 사람"를 찾아보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | db.users.aggregate([ //stage 별로 나뉘며 앞에서 처리 된걸 가지고 다음 stage로 전달 //stage 1 { $match : { "eyeColor" :"green" } }, //stage 2 { $match :{ "age" : { $gt : 30 } } } ]); | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | "result" : [ { "_id" : "564d1acb42b552b7a4e93ff4", "index" : 19347, "isActive" : true, "age" : 30, "eyeColor" : "green", "name" : "Spencer Brady", "gender" : "male", "company" : "KIDSTOCK", "email" : "spencerbrady@kidstock.com" }, { "_id" : "564c3240e5aa2a5f7c7bd7f5", "index" : 0, "isActive" : false, "age" : 30, "eyeColor" : "green", "name" : "Rodgers Grant", "gender" : "male", "company" : "VERTON", "email" : "rodgersgrant@verton.com" }, ..........................생략.................................. | cs |
잘나온다... 흠. 근데 먼가 예제로서는 부족하다... stage라는 넘을 잘 모르겠다.
쿼리를 좀 바꿔보자. $match 말고 $group도 사용해보자. "index가 10000 이상이며, eyeColor 별 사람은 몇명인지"를 찾아보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | db.users.aggregate([ //stage 별로 나뉘며 앞에서 처리 된걸 가지고 다음 stage로 전달 //stage 1 { $match : { "index" :{ $gte: 10000 } } }, { $group:{ "_id" : "$eyeColor", "sum" : { $sum : 1 } } } ]); | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /* 1 */ { "result" : [ { "_id" : "brown", "sum" : 3296.0000000000000000 }, { "_id" : "blue", "sum" : 3362.0000000000000000 }, { "_id" : "green", "sum" : 3342.0000000000000000 } ], "ok" : 1.0000000000000000, "$gleStats" : { "lastOpTime" : Timestamp(1448006114, 21), "electionId" : ObjectId("564d825928cf905edab537fb") } } | cs |
저번 글까지는 단순히 cluster 환경 구성을 하는 것이었다.. 이제 데이터를 넣어보자.
먼저 필자는 다음과 같은 json 구조로 데이터를 10000건을 만들었다.
1 2 3 4 5 6 7 8 9 10 11 | { "_id" : "564c3240e5aa2a5f7c7bd7f5", "index" : 0, "isActive" : false, "age" : 30, "eyeColor" : "green", "name" : "Rodgers Grant", "gender" : "male", "company" : "VERTON", "email" : "rodgersgrant@verton.com" } | cs |
linux 커널에서 다음과 같은 명령으로 데이터를 넣는다.
1 2 | ./bin/mongoimport -h 192.168.0.105:45005 -d dbname -c collectionName --file ${path}/users.json | cs |
그런후... 샤드의 상태를 확인해보자.
1 2 | sh.status(); | cs |
아마도, 1개의 샤드에만 들어있을 것이다.
분명히 2개의 샤드에 분리 되야 하는데.... =_=;
그럼 chunk의 size를 확인한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | use config db.settings.find(); //result { "_id" : "chunksize", "value" : 64.0000000000000000 } /* 2 */ { "_id" : "balancer", "stopped" : false } | cs |
use config 이 넘은 cluster server의 내부 db 이다. cluster만 존재하며, 설정 정보들이 들어있다.
db.settings.fing()를 하면 위의 결과 처럼 나올 것이다. 기본 mongodb의 chunk size는 64M 인것이다. 10000건의 데이터는 3M도 안된다. =_=; 그러므로 샤딩은 당연히 안될것이다. 그럼 한번 줄여보자.
1 | db.settings.save( { _id:"chunksize", value: 1 } ); | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | --- Sharding Status --- sharding version: { "_id" : 1, "minCompatibleVersion" : 5, "currentVersion" : 6, "clusterId" : ObjectId("564aff51f77bed76dca6ba70") } shards: { "_id" : "elastic", "host" : "elastic/192.168.0.105:45001,192.168.0.105:45002,192.168.0.105:45003" } { "_id" : "shard0000", "host" : "192.168.0.105:55001" } databases: { "_id" : "admin", "partitioned" : false, "primary" : "config" } { "_id" : "latis", "partitioned" : true, "primary" : "elastic" } latis.users shard key: { "index" : 1 } chunks: shard0000 4 elastic 5 { "index" : { $minKey : 1 } } -->> { "index" : 1 } on : shard0000 Timestamp(5000, 1) { "index" : 1 } -->> { "index" : 4369 } on : shard0000 Timestamp(3000, 0) { "index" : 4369 } -->> { "index" : 6554 } on : elastic Timestamp(4000, 1) { "index" : 6554 } -->> { "index" : 8739 } on : elastic Timestamp(1000, 4) { "index" : 8739 } -->> { "index" : 10923 } on : elastic Timestamp(3000, 2) { "index" : 10923 } -->> { "index" : 14000 } on : elastic Timestamp(3000, 3) { "index" : 14000 } -->> { "index" : 16184 } on : shard0000 Timestamp(4000, 2) { "index" : 16184 } -->> { "index" : 19000 } on : shard0000 Timestamp(4000, 3) { "index" : 19000 } -->> { "index" : { $maxKey : 1 } } on : elastic Timestamp(5000, 0) { "_id" : "users", "partitioned" : true, "primary" : "elastic" } { "_id" : "db", "partitioned" : false, "primary" : "elastic" } { "_id" : "test", "partitioned" : false, "primary" : "elastic" } | cs |
오오오오... 잘된다. 다음은 aggregation 및 mapreduce로....