조회수 확인
4. 집계 연산 질의

집계 연산을 수행하기 앞서, 예제 데이터를 생성해야 한다. 순서는 아래와 같다.
 1. 예제 데이터 생성하기(generate_date.php)
 2. 작성자별 아티클 수 세기
 3. 태그 클라우드 생성하기
 4. 작성자별 평균 평가 점수 계산하기
 5. 아티클의 유일한 카테고리 열거하기
 




1. 예제 데이터 생성하기
 본 예제는 첨부된 파일을 받아서 실행하여, 데이터를 생성한다.




 생성된 데이터를 mongo shell에서 확인한다.
  use myblogsite
  db.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.php
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
<?php
require("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">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
    <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.php

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php
require('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$initialnew MongoCode($reduce),
array(
'finalize' => new MongoCode($finalize),
'condition' => $condition
)
);
 
// echo json_encode($result);
?>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
    <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.php

 

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
<?php
require('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"
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="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 카테고리의 포스트를 톺아봅니다