MapReduce란 
1. map이란 방식으로 원하는 데이터를 추출한 다음에 
2. reduce라는 방법으로 추출한 데이터를 정제하는 것을 말한다.


fig 1. MapReduce의 이해(https://docs.mongodb.com/manual/core/map-reduce/)



위의 그림에서 보면 알 수 있듯이 orders라는 컬렉션이 존재하는데 
1. query를 통해 필요한 데이터를 뽑아내고  (status: "A")
2. map을 이용해 원하는 데이터의 key, value를 추출하여  (key: cust_id, value: amount[])
3. reduce를 이용하여 추출한 데이터를 정제한 것을 알 수 있다. (amount[]의 총합.)

만약 컬렉션 전체가 mapping의 대상이라면 위의 1번은 생략이 가능하다.

그럼 아래의 예제 데이터를 이용해서 MapReduce를 연습해보자.



============================ JSON ============================

    "_id" : ObjectId("5a8fa53cf6d63ea4599c476b"), 

    "cust_id" : "A2012001", 

    "order_date" : ISODate("2012-09-30T15:00:00.000+0000"), 

    "status" : "A", 

    "price" : 250.0, 

    "items" : [

        {

            "item_name" : "Bunny Boots", 

            "qty" : 5.0, 

            "price" : 25.0

        }, 

        {

            "item_name" : "Skey Pole", 

            "qty" : 5.0, 

            "price" : 25.0

        }

    ]

}

    "_id" : ObjectId("5a8fa53df6d63ea4599c476c"), 

    "cust_id" : "A2012001", 

    "order_date" : ISODate("2012-09-14T15:00:00.000+0000"), 

    "status" : "A", 

    "price" : 500.0, 

    "items" : [

        {

            "item_name" : "Bunny Boots", 

            "qty" : 15.0, 

            "price" : 25.0

        }, 

        {

            "item_name" : "Skey Pole", 

            "qty" : 5.0, 

            "price" : 25.0

        }

    ]

}

    "_id" : ObjectId("5a8fa53df6d63ea4599c476d"), 

    "cust_id" : "A2012002", 

    "order_date" : ISODate("2012-09-14T15:00:00.000+0000"), 

    "status" : "A", 

    "price" : 500.0, 

    "items" : [

        {

            "item_name" : "Bunny Boots", 

            "qty" : 15.0, 

            "price" : 25.0

        }, 

        {

            "item_name" : "Skey Pole", 

            "qty" : 5.0, 

            "price" : 25.0

        }

    ]

}


============================ JSON ============================


위의 예제 데이터는 _id, cust_id, order_date, status, price, items 컬럼으로 구성된 order라는 컬렉션에 넣으면 된다.

이제 두 가지 MapReduce를 해보겠다.



=========================== Query ===========================

// 분산처리를 활용한 map함수로 데이터 가져오기

var map_function = function(){

// emit : 추출하다. (key, value)

emit(this.cust_id, this.price);  // emit(key, value) 로 작성.

}


// 필터를 위한 reduce 함수

// function(변수, 변수) -> 변수 이름은 마음대로 정할 수 있다.

var reduce_function = function(keyCustId, valuesPrices){

  // price가 들어있는 valuesPrices를 Array로 만들고 다 더한다.

return Array.sum(valuesPrices);

}


// 위에 있는 내용을 적용한 map, reduce메서드 처리

db.order.mapReduce(  // .mapReduce(map함수, reduce함수, 어떻게 처리를 할 것인가.)

map_function, 

reduce_function,                     

{out:"order_cust_total"}  // order_cust_total이라는 컬렉션을 만들어서 그 컬렉션에 저장한다.

)

=========================== Query ===========================



먼저 cust_id를 key, price를 value로 하고 key에 해당하는 모든 price 값을 더해보는 프로그램이다.
map: (key: cust_id, value: price[])
reduce: (key: cust_id, value: sum[price])
데이터를 저장할 collection: order_cust_total

이제 본격적으로 MapReduce를 사용해보겠다.



=========================== Query ===========================

// 제품명별 수량의 평균

// order collection 안에 items라는 항목으로 입력되어 있다.

var map_function = function(){

for(var idx = 0 ; idx < this.items.length ; idx++){  // items는 리스트이므로 리스트 길이만큼 반복하여 모든 key, value를 뽑는다.

var key = this.items[idx].item_name;  // key는 상품명 -> group

// 넘기려는 값이 2개 이상이면 json 데이터를 만든다.

var value = {

count: 1,  // count에 무조건 1을 넣어서 나중에 총계를 확인할 때 쓴다.

qty: this.items[idx].qty  // items에 있는 qty를 qty에 넣는다.

};

// 메서드이기 때문에 local에 위치하더라도 데이터는 emit()이 저장하는 장소에 저장되고 reduce_function에 넘어갈 수 있다.

// 그리고 emit()은 dictionary(자바에서 hashMap) 형태이기 때문에 key라는 네임스페이스에 value값들이 전부 저장되게 된다. 

emit(key, value);

};

}


// map함수에서 넘겨준 데이터를 필터링하는 처리 -> reduce -> 합계를 구한다.

var reduce_function = function(keySKU, valueCountObject){

// 결과값을 내는 데이터가 2개 -> json으로 만든다.

var reducedValue = {

count:0,

qty:0

}  // return 으로 넘길 데이터의 기본형태. reduceValue: count, qty.

for(var idx = 0; idx < valueCountObject.length; idx++){

  // valuesCountObject 안에 있는 count를 reduceValues count 안에 넣어준다.

reducedValue.count += valueCountObject[idx].count; 

reducedValue.qty += valueCountObject[idx].qty;   

}

return reducedValue;

}


// finalize 함수를 정의해서 사용할 수 있다. -> 평균을 구한다.

var finalize_function = function(key, reducedValue){

  // 평균을 구하기 위해 reducedValue에 average를 만들어서 qty/count로 평균을 만든다.

reducedValue.average = reducedValue.qty / reducedValue.count

}


// mapReduce 실행.

db.order.mapReduce(

map_function,

reduce_function,

{

  // 만약 기존에 map_reduce_example 컬렉션이 있으면 이번에 만드는 것으로 대체한다.

out:{replace:"map_reduce_example"},

// 아까 위에 만든 finalize를 여기서 적용시킨다.

finalize:finalize_function

}

)


=========================== Query ===========================



위의 MapReduce보다 조금 어려워졌다.

일단 저 프로그램의 목적은 
각 item_name이 
     1. 몇 번 팔렸는가
     2. 몇 개가 팔렸는가
     3. 평균적으로 1회 구매에 몇 개나 팔렸는가
를 구하는 MapReduce이다.

일단 대부분의 내용은 전부 주석으로 달아놨으므로 읽어보면 충분히 이해 할 수 있을 것이다.


+ Recent posts