-
[MongoDB] 4. 집계 연산 질의(Map Reduce)CSE/MongoDB 2015. 6. 13. 11:434. 집계 연산 질의집계 연산을 수행하기 앞서, 예제 데이터를 생성해야 한다. 순서는 아래와 같다.1. 예제 데이터 생성하기(generate_date.php)2. 작성자별 아티클 수 세기3. 태그 클라우드 생성하기4. 작성자별 평균 평가 점수 계산하기5. 아티클의 유일한 카테고리 열거하기1. 예제 데이터 생성하기본 예제는 첨부된 파일을 받아서 실행하여, 데이터를 생성한다.생성된 데이터를 mongo shell에서 확인한다.use myblogsitedb.sample_articles.find()위와 같이 데이터가 보이면 성공!맵리듀스(Map Reduce)는 데이터 처리를 위한 Design Pattern이다. 맵리듀스 이면의 사상은 큰 과업을 좀 더 작은 과업으로 분해하는 방법이다. 하위 과업은 독자적으로 수행한다. 쪼개진 하위 과업의 결과가 합쳐져 최종 결과를 생성한다. 두 단계로 나뉘어 진다.- Map 단계: 과업을 좀 더 작은 하위 과업으로 분해하고 중간 결과를 생성하기 위해 하위 과업 수행- Reduce 단계: 중간 결과를 결합해 최종 결과물을 만들어낸다.맵리듀스 참고 자료2. 작성자별 아티클 수 세기1) 구동된 mongo shell에 use myblogsite 입력2) var map = function() { emit(this.author, 1); }3) var reduce = function(key, values) {var count = 0;for (var i = 0; i < values.length; i++) {count += values[i];}return count;};4) db.runCommand({mapreduce: 'sample_articles',map: map,reduce: reduce,out: 'articles_per_author'})5) 위에까지 순차적으로 에러없이 입력한 뒤, db.articles_per.author.find()로 맵리듀스 연산 결과를 확인한다.본 방식은 SQL의 질의는SELECT COUNT(id) FROM sample_articles GROUP BY author 수행방식과 유사하다.map 함수 정의map 함수는 Collection에 들어있는 Document를 받아 새로운 Key / Value 쌍 집합을 만든다. 본 예시에서 정의한 map함수에서 Key는 document를 작성한 작성자 이름이며 Value는 정수 1을 담은 배열인 Key / Value 쌍을 생성한다. 매번 Map이 Key와 동일한 document를 만날 때마다, Value 배열에 단순히 1을 더한다.reduce 함수 정의map 함수가 emit()으로 내보내는 key /value 쌍은 reduce 함수로 들어간다. reduce 함수는 각 작성자를 받아 값 배열에서 1의 수를 더해 전체 카운트를 구한다.3. 태그 클라우드 생성하기PHP내에서 맵리듀스를 수행해보도록 하겠다. 먼저 tagcloud.php를 생성하여 아래 코드를 입력한다.tagcloug.php123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112<?phprequire("dbconnection.php");$mongo = DBConnection::instantiate();$db = $mongo->database;// map 함수 정의(Mongo)$map = new MongoCode("function() {"."for (var i = 0; i < this.tags.length; i++) {"."emit(this.tags[i], 1);"."}"."}");// reduce 함수 정의(Mongo)$reduce = new MongoCode("function(key, values) {"."var count =0;"."for (var i = 0; i < values.length; i++) {"."count += values[i];"."}"."return count;"."}");// map과 reduce 함수를 실행, 결과를 tagcount라는 Collection에 저장$command = array('mapreduce' => 'sample_articles','map' => $map,'reduce' => $reduce,'out' => 'tagcount');$db->command($command);// 모든 태그를 배열에 올려 빈도순으로 정렬$tags = iterator_to_array($db->selectCollection('tagcount')->find()->sort(array('value' => -1)));// 가장 높은 빈도로 출현하는 태그를 찾기 위한 함수function getBiggestTag($tags) {// 배열을 재설정해 첫 항목을 가르킴reset($tags);// 연관 배열의 첫 키를 얻음$firstKey = key($tags);return (int)$tags[$firstKey]['value'];}$biggestTag = getBiggestTag($tags);foreach($tags as &$tag) {$weight = floor(($tag['value'] / $biggestTag) * 100);switch ($weight) {case ($weight < 10):$tag['class'] = 'class1';break;case (10 <= $weight && $weight < 20):$tag['class'] = 'class2';break;case (20 <= $weight && $weight < 30):$tag['class'] = 'class3';break;case (30 <= $weight && $weight < 40):$tag['class'] = 'class4';break;case (40 <= $weight && $weight < 50):$tag['class'] = 'class5';break;case (50 <= $weight && $weight < 60):$tag['class'] = 'class6';break;case (60 <= $weight && $weight < 70):$tag['class'] = 'class7';break;case (70 <= $weight && $weight < 80):$tag['class'] = 'class8';break;case (80 <= $weight && $weight < 90):$tag['class'] = 'class9';break;case ($weight > 90):$tag['class'] = 'class10';break;}}?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><link rel="stylesheet" href="style.css"/><title> Tag Cloud </title></head><body><div id="contentarea"><div id="innercontentarea"><h1> Tag Cloud </h1><ul id="tagcloud"><?php foreach($tags as $tag):?><li><a href="#" class="<?php echo $tag['class']; ?>"><?php echo $tag['_id'];?></a></li><?php endforeach;?></ul></div> <!--End of div innercontentarea --></div></body></html>
cs 다음으로 첨부된 css 파일을 다운 받아서 적용시키자.그 다음, 웹에서 tagcloud.php에 접속하자. 아래와 같이 나오는데 아마 다르게 나올 것이다. 랜덤으로 생성한 값이 본 예제와 일치하지 않기 때문.4. 작성자별 평균 평가 점수 계산하기group() 메소드를 통하여 평균 평가 점수를 작성자별로 출력하겠다.먼저 avg_rating.php를 작성한다. 아래와 같이avg_rating.php12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061<?phprequire('dbconnection.php');$mongo = DBConnection::instantiate();$collection = $mongo->getCollection('sample_articles');$key = array('author' => 1);// 집계 연산 카운터와 전체 평가 점수를 0으로 설정$initial = array('count' => 0, 'total_rating' => 0);// reduce 함수 - 카운터를 1 증가시키고 평가점수를 더함$reduce = "function(obj, counter) { counter.count++; counter.total_rating += obj.rating; } ";// 평균 평가점수를 찾는다$finalize = "function(counter) { counter.avg_rating = Math.round(counter.total_rating / counter.count); } ";$condition = array('published_at' => array('$gte' => new MongoDate(strtotime(' day'))));$result = $collection->group($key, $initial, new MongoCode($reduce),array('finalize' => new MongoCode($finalize),'condition' => $condition));// echo json_encode($result);?><head><title>Author Rating</title><link rel="stylesheet" href="style.css"/></head><body><div id="contentarea"><div id="innercontentarea"><h1>Authors' Ratings</h1><table class="table_list" cellspacing="0" cellpadding="0"><thead><tr><th width="50%">Author</th><th width="24%">Articles</th><th width="*">Average Rating</th></tr></thead><tbody><?php foreach($result['retval'] as $obj):?><tr><td><?php echo $obj['author'];?></td><td><?php echo $obj['count'];?></td><td><?php echo $obj['avg_rating'];?></td></tr><?php endforeach;?></tbody></table></div></div></body></html>cs 페이지를 열어 확인한다. 아래와 비슷하게 나왔는지 확인위 과정을 설명하자면, sample_articles 컬렉션을 표현하는 MongoCollection에 존재하는 group() 메소드를 호출했다.매개변수를 살펴보면,첫째로, 작성자 이름(author)으로 아티클을 Group짓기 위해 Key parameter로 array('author' => 1)을 넘겼다. => $key둘째로, 집계 연산 카운터(counter)의 count와 total_rating을 0으로 초기화했다. => $initial셋째로, $reduce는 다큐먼트를 순회하며, 1씩 집계 연산 카운터의 count필드를 증가시키며, 이 카운터의 total_rating 필드에 현재 다큐먼트의 평가 점수를 더한다.넷째로, $finalize는 카운터 수로 전체 평가 점수를 ㄴㅏ눈 다음에 몫을 반올림하는 방법으로 평균을 계산한다.다섯쨰로, $condition은 그룹 연산을 수행하기 위해 넘겨주는 parameter이다.5. 아티클의 유일한(distinct) 카테고리 열거하기마지막으로 distinct에 대해 알아보자.distinct.php12345678910111213141516171819202122232425262728<?phprequire('dbconnection.php');$mongo = DBConnection::instantiate();$db = $mongo->database;$result = $db->command(array('distinct' => 'sample_articles', 'key' => 'category'));?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"<head><title> Categories </title><link rel="stylesheet" href="style.css"/></head><body><div id="contentarea"><div id="innercontentarea"><h1> Distinct Categories </h1><ul><?php foreach($result['values'] as $value):?><li><?php echo $value;?></li><?php endforeach;?></ul></div></div></body></html>cs 바로 웹에서 수행한다.각 아티클 다큐먼트의 카테고리 필드에 속한 유일한 값을 모두 열거하는 스크립트이다.mongo shell에서 distinct()를 써보도록 하겠다.사용법은 아래와 같다.db.collectionName.distinct(Key)본 예제에서는 db.sample_artilces.distinct('category')이것으로 4장을 마무리하겠습니다.'CSE > MongoDB' 카테고리의 다른 글
[MongoDB] 7. GridFS로 대용량 파일 처리 (0) 2015.06.13 [MongoDB] 6. 관계형 DB와 함께하는 몽고DB 활용 (0) 2015.06.13 [MongoDB] 5. 몽고DB를 사용한 웹 분석 (0) 2015.06.13 [MongoDB] 3. 세션관리자 제작 (0) 2015.06.13 [MongoDB] 2-1. 몽고DB를 사용한 첫 웹 애플리케이션 제작 (0) 2015.06.13 [MongoDB] 2. 몽고DB를 사용한 첫 웹 애플리케이션 제작 (0) 2015.06.13