개발관련/MongoDB 2015. 11. 20. 16:18

mapreduce는 map 과 reduce의 합성어 이다. 

map은 특정 collection의 특정 key 와 value를 map으로 하며, map의 key는 중복이 안되며, value는 list에 쌓이는 형태로 된다. 


reduce는 map의 데이터를 감소 시키는 역활을 한다. reduce를 통해 특정 값을 뽑아낸다.이를 통계치 데이터로 사용하게 된다. 


1
2
3
use dbname
 
db.users.find();
cs
먼저 데이터를 확인한다. 앞서 사용한 collections을 사용할 것이다. 


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

자 map을 설계 해보자. 필자는 "각 eyeColor 별로  성별를 구분하고, 전체 나이와 인원을 구하고 싶다" 이 기준으로 먼저 map을 만들어 보자. 
1
2
3
4
5
6
7
8
9
10
11
12
13
//make map 
var map = function () {
  //맵은 단순 key value로 선언을 하며 , 이 값들은 emit을 통해 reduce로 전달을 한다. 
  //this는 현재 collection에 document가 저장된다. 
  var key = {};
  key.eyeColor = this.eyeColor;
  key.gender = this.gender;
  
  var value = {};
  value.age = this.age;
  value.count = 1;
  emit(key, value);
};
cs

어라 js랑 똑같다. 걍 js 함수다 . map이라는 함수에 key에는 eyeColor와 gender를 저장하고, value에는 age와 count를 저장한다. 여기서 눈여겨 볼 껀 this라는 넘이다. 이 this는 현재 사용하고 있는 collection을 뜻하며, 이 collection의 filed명을 쓰면 그 값들이 전달된다.  자 위에 map을 선언 하면 다음과 같은 비슷한 결과 형태로 될것이다. (머 내부적으로 어떻게 저장하는지는 몰라서 ... 추측하건데...=_=;)

1
green.male : [{ 30,1},{36,1}............]
cs

위에 작업이 끝나면 map; 를 실행해보자. 위에 코드가 나올것이다. 
여기에 보면 emit이라는 넘이 있다. 저 emit은 reduce로 값을 전달하며 map의 결과를 reduce 매개변수로 전달하게 된다. 그럼 reduce를 해보자. 

1
2
3
4
5
6
7
8
9
10
11
12
var reduce = function (key, values){
    var returnVal = {};
    var totalAge =0;
    var totalCount = 0;
    for( index in values){
        totalAge += values[index].age;
        totalCount += values[index].count;
    }
    //return type은 map의 기준의 value로 결정되므로 reduce의 return type의 value랑 똑같은 형태로 맞춰줘야한다.!!
    return {"age":totalAge , "count" : totalCount};
    
}
cs

이것도 js함수다. key, values에는 emit으로 전달한 파라미터들이 있다. values에는 배열로 값이 들어있으므로, 위에 예시 처럼 loop문을 돌려주면서 totalage와 totalcount에 전체 나이 전체 인원을 계산한다. reduce의 중요한 사실은 return 값에 있다. 이 리턴은 map에서 이미 정해야 한다. 위의 map의 value를 보자. map의 value는 age와 count이다. 이 value 형태로 return이 되야만한다. 

다시 mapreduce에 대해 정리하자. 
1. map은 key, value를 정의하며 emit으로 전달한다. 이때 value는 reduce의 return값과 동일하다. 
2. reduce는 value에 대해 값을 재정의 할수 있으며, return은 map의 value와 동일해야한다.

여기에는 한계가 있다. reduce의 return이 map의 value와 같으므로 값을 정형화 하지 못한다. 이때 쓰는 함수가 finalize이다. 


1
2
3
4
5
6
7
8
9
 
var finalize = function(key, values){
    return {
        "age" : values.age,
        "count" : values.count,
        "avg" :  values.age/values.count
    };
}
 
cs

finalize는 reduce의 마지막 나온 결과를 건네주게 된다. 여기서 마지막이란 trycatch의 finaliy 정도 생각 하면 되겠다. finalize는 결과를 마음 대로 수정해도 된다. 


자 이제 mapreduce 함수를 호출 해보자. 


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
33
34
35
36
37
38
39
40
41
42
43
44
 
db.users.mapReduce(
    map,reduce, 
    {
        //없으면 현재 db에  만든다.
        /**
        *
        out: { <action>: <collectionName>
        [, db: <dbName>]
        [, sharded: <boolean> ]
        [, nonAtomic: <boolean> ] }
        <action>
         replace - 기존의 있는 콜렉션을 없애고 mapreduce결과를 집어 넣는다.
         merge - 이미 있는 콜렉션을 넣는데 현재 키가 존재할경우 오버라이트 한다. 
         reduce - 같이 키로 결과가 있으면 그 결과들 끼리 reduce를 한다. 
         db 
         - output collection 을 쓸 db이름을 정의 default db는 mapreduce collection과 같은 db이다. 
         
         sharded 
         - 맵리듀스 결과 collection을 바로 샤딩하고 싶을때 true로 변경. 
         nonAtomic
         - mapreduce 사용시 true로 사용하면 lock이 걸리지 않는다. (상황에 따라 써야겠구만.)
        **/
        out : "testReduce",
        //map들어갈 collection 필터링 할때 
        query : {
            "index" : {
                $gte : 1000
            }
        },
        //가령 추천수가 가장 많은 사람을 쓸때 오름 차순으로 하면 제일 먼저 나오는 사람이 젤위이므로 이 사람을 빨리 가져올때 사용.
        //sort : {},
        //reduce 를 한 결과를 가공을 할때 사용. 
        finalize : finalize,
        //scope는 map, reduce, finalize 함수의 전역 변수로 사용된다. 
        //사용시는 $변수명으로 사용하면된다. 
        //scope: { "temp" : 1},
        //몽고디비는 bson 타입으로 저장을한다. 이때 속도가 좀 걸리는데 json 형태로 할것인지를 선택하는 옵션이다. 
        //그러나 json의 경우 속도는 빠르나 collection document의 제한이 걸린다. 그래서 안쓰는게 상책.
        //jsMode: <boolean>,
        //verbose: <boolean>
    }
);
 
cs


 파라미터는 총 3개다. map, reduce, 3번째는 object이다. 중요한것은 이 3번째 파라미터...

먼저 out 이라는 넘이 나온다. 단순히 값을 쓰면, 지정한 값으로 collection을 만들고 그 collection에 mapreduce 값을 쓴다. 이때 위에 주석으로 써놓았듯이 옵션에는 총 3가지가 있다. replace, merge, reduce 이 3개가 있는데 default는 replace 로 쓴다. 


query라는 넘은 mapreduce를 할 collection의 값을 특정 결과로 만들고 난 후 mapreduce로 전달한다. 위에 예제는 users collection에 index가 1000 이상인 사람들에 대해 mapreduce를 한다는 예시이다. 


finalize 는 위에 선언해놓은 finalize 함수의 변수를 쓰면 된다. 나머지 옵션은 주석으로 써놓았으니 살펴 보면된다. 


이 mapreduce는 cursor라는 곳에 저장을 한다. 이 cursor는 휘발성이므로  계속적으로 사용 할때는 위와 같이 사용 해서는 안된다. 다음에는 이를 막고자 할때 사용하는 system.js 에 대해 알아본다. 



posted by 제스트
: