NOSQL 검색 결과

12개 발견
  1. 미리보기
    2015.06.13 - Palpit

    [MongoDB] 9 & 10. 보안과 성능향상 & RockMongo와 phpMoAdmin을 사용한 몽고DB 관리

  2. 미리보기
    2015.06.13 - Palpit

    [MongoDB] 8. 몽고DB와 PHP로 위치 인식 웹 애플리케이션 제작

  3. 미리보기
    2015.06.13 - Palpit

    [MongoDB] 7. GridFS로 대용량 파일 처리

  4. 미리보기
    2015.06.13 - Palpit

    [MongoDB] 6. 관계형 DB와 함께하는 몽고DB 활용

  5. 미리보기
    2015.06.13 - Palpit

    [MongoDB] 5. 몽고DB를 사용한 웹 분석

  6. 미리보기
    2015.06.13 - Palpit

    [MongoDB] 4. 집계 연산 질의(Map Reduce)

  7. 미리보기
    2015.06.13 - Palpit

    [MongoDB] 3. 세션관리자 제작

  8. 미리보기
    2015.06.13 - Palpit

    [MongoDB] 2-1. 몽고DB를 사용한 첫 웹 애플리케이션 제작

조회수 확인
이번 포스팅은 9장, 10장 동시에 작성하겠습니다!

분량이 적은 관계로 한번에 쓸수 있을 것 같더군요~

오늘 갑자기 날씨가 추워져서 감기가 걸릴지도 모르겠네요~ 관리 잘하세요 다들~

먼저 9장의 순서입니다.
 1. 몽고DB 컬렉션에 색인 생성
 2. 몽고DB에 사용자 인증 추가



1. 몽고DB 컬렉션에 색인 생성

 이번 실습은 몽고 쉘에서 작업하므로 쉘을 열어주세요~

 use testdb

 명령을 쳐서 db를 testdb로 사용하겠습니다!


 그런 다음 movies 컬렉션에 여러 영화들을 넣어보죠~

  db.movies.insert({name: "The Matrix", genre: "sci-fi", year: 1998})
  db.movies.insert({name: "Lord of the Rings", genre: "fantasy", year: 2002})
  db.movies.insert({name: "Saving private Ryan", genre: "war", year: 1997})
  db.movies.insert({name: "Goodwill Hunting", genre: "drama", year: 1995})
  db.movies.insert({name: "The Dark Knight", genre: "action", year: 2008})
  db.movies.insert({name: "Inception", genre: "sci-fi", year: 2010})
  db.movies.insert({name: "Oldboy", genre: "thriller", year: 2003})
  db.movies.insert({name: "Forrest Gump", genre: "drama", year: 1994})
  db.movies.insert({name: "Love me if you dare", genre: "romance", year: 2004})
  db.movies.insert({name: "My Love, Don't Cross That River", genre: "documentary", year: 2014})
  db.movies.insert({name: "The Theory of Everything", genre: "romance", year: 2014})


다음으로 genre필드에 색인(Index)를 생성합니다.

 db.movies.ensureIndex({genre:1})


생성한 색인을 열거하기 위해 아래 명령을 줍니다.

 
 db.movies.getIndexes()



 마지막 명령어를 치면 아래와 같이 출력이 되는데, 두개의 인덱스가 보일겁니다!



 



id 와 genre로 두개의 인덱스가 있네요~ id는 자동으로 생성된 것이고, 아래 genre가 저희가 만들 것이죠~

간단하게 색인을 만들어 봤습니다. 그럼 다음으로 색인의 여러 방식에 대해 알아보겠습니다.


색인(Index)에는 여러 방식이 존재합니다.
 - 유일한 색인
 - 복합 키 색인
 - 내장된 다큐먼트 필드에 대한 색인 
 - 배열 필드에 대한 색인

간단하게 짚고 넘어가겠습니다.

 - 유일한 색인
 유일한 색인은 MySQL에서 unique key처럼 동작합니다. 특정 Collection에 들어있는 모든 Document를 대상으로 색인된 특정 필드의 값이 중복되지 않도록 보증해줍니다. 아래 예시를 통해 이 색인을 생성하는 방법을 나타내겠습니다.

 db.movies.ensureIndex( {name: 1}, {unique: 1} )

이전의 색인 생성하는 방법 뒤에, {unique:1} 옵션을 주어서 유일한 색인을 생성합니다. 생성이 되고나서, name 필드 값이 동일한 Document를 삽입하려고 하면 오류가 발생하게 됩니다.



 - 복합 키 색인
 복합 키 색인은 Document의 다중 필드에 대한 색인을 말합니다. 사용법은 아래와 같습니다. 장르와 년도 필드에 대한 복합 키 생인을 생성합니다.

 db.movies.ensureIndex( {genre: 1}, {year: -1} )



 - 내장된 다큐먼트 필드에 대한 색인
 예를 들어, movies 컬렉션에 영화 상영 시간과 스튜디오 이름을 저장하기 위한 meta 필드가 있다고 가정합니다.

 db.movies.insert({name: "Thor", genre: "action", year: 2011, meta: {duration_minutes: 115, studio: "Paramount"}} )

 위 meta내의 필드를 색인하는 방법은 다음과 같습니다.

 db.movies.ensureIndex( {"meta.duration_minutes":1, "meta.studio": 1} )

 meta 내의 필드이므로 .(점)표기법을 사용하여 색인을 생성합니다. 




 - 배열 필드에 대한 색인
 예를 들어, movies 컬렉션에 tags 필드가 있다고 가정합니다.

 db.movies.insert({name: "Iron Man 2", genre: "action", year: 2010, tags: ['superhero', 'marvel', 'comics', 'scifi'})

 위 tags 필드를 색인하는 방법은 일반적으로 색인을 생성하는 법과 같습니다.

 db.movies.ensureIndex( {tags: 1} )

 아래 방법으로 배열의 구성 요소를 색인 할 수 있습니다.

 db.movies.find({tags: 'superhero'})





2. 몽고DB에 사용자 인증 추가

 이 실습은 진행하지 않겠습니다. 왠지 사용자 계정 생성이나 삭제는 초장에 다뤄좌야 될 것 같은데 막바지에 다룬다는게 참 아이러닉하고, 실습되는 내용이 addUser를 사용하는데, deprecated된 메소드이므로 진행하지 않겠습니다!
 



아 몽고DB에서도 SQL Injection을 주의해야합니다!!! 아래 링크에 대처법을 제시해 둔 것 같네요~


또한 몽고DB에서 제공하는 사용자 인증 기법은 아주 기초적이며, 복잡한 공경을 방어할만큼 안전하지 않습니다!!!
그러므로, 상용 몽고DB 서버를 신뢰하는 환경에 배치해야 합니다!! 방법은 아래와 같습니다.
 - 서버는 방화벽 뒤에 배치
 - 컴퓨터에 모든 가용 IP에 귀를 귀울이는 대신, 특정 IP에만 귀를 기울이도록 구성해야 함
 - 서버가 사용하는 TCP 포트는 신뢰할 수 있는 컴퓨터만 접근 가능하도록 구성되어야 함


이것으로 9장을 마치고 10장으로 넘어가겠습니다!!!



10. RockMongo와 phpMoAdmin을 사용한 몽고DB 관리



설명은 집어치우고 RockMongo부터 설치해보도록 하죠!!

1. RockMongo 설치


위 경로로 진입하시면 아래와 같이 비슷하게 뜨실겁니다. 보시면 가장 상위에 rockmongo-v.1.1.3.zip으로 되어있는데 요걸 클릭하셔서  다운 받아주세요!! (1.1.3은 현재 포스팅된 날짜 기준으로 최신버전입니다. 버전업은 continued 된다는 사실....)





 





내려받은 rockmongo.zip 파일을 압축을 풀 뒤, 푼 디렉토리명을 rockmongo로 바꿔주세요!!



rockmongo 폴더를 htdocs로 이동해주세요! 아래처럼 말이죠~





 



 


다음으로 rockmongo/rockmongo 안에 config.php의 아래부분처럼 수정해줍니다. (false로 되어있습니다. true로 바꿔주세요~)





 






위와같이 설정하고 rockmongo내의 index.php를 실행해 주면!!!


 


 




 
네... Fatal error가 납니다. 저는...

보니깐 index.php에서 수정해줘야되나 봅니다ㅜㅜ index.php를 열어줍니다. 아래와 같이 만들어주세요~
처음 index.php열면 27번 라인처럼 설정되어있는데
27번 라인 주석처리하고, 28번으로 대체한 것입니다.





 


수정 후, 다시 실행하니깐 되는군요... 휴...









하... 근데 연결해서 로그인해보니 이런 맛깔나는 상황이 있네요...







죄다 사장 된 건가... 뭐지 ... 

해법을 찾다찾다 못찾겠더군요....ㅜㅜㅜㅜ 죄송합니다. 저만 이런건지, 케이스가 좀 부족하네요ㅜㅜ 다음으로 바로 넘어가겠습니다.

혹시나 다른분들 되실수도 있으니 여꺼증 남겨놓것습니다.





2. phpMoAdmin 설치
이것 또한, 사장된 건지 뭐 잘 안되네요ㅠㅠ 
여기까지가 끝인가보오~

진행이 더이상 안됩니다 ㅜㅜ GUI툴로 관리한다는건 정말 DB관리시 큰 일인데... 

참고 서적이 너무 옛날 버전인지, 그에 대응하는 해법이 너무 웹에 적게 나와있는지라... 송구하옵니다 ㅜㅜ



뭔가 찜찜한 이느낌은 뭐지 ㅠㅠ


다른 카테고리의 글 목록

CSE/MongoDB 카테고리의 포스트를 톺아봅니다
조회수 확인

이번 장은 요즘 핫한!!! 위치 기반의 서비스를 만들어 보도록 하겠습니다!!!

순서는 아래와 같습니다.
 1. W3C API로 위치 파악
 2. 지리 공간 색인 생성
 3. 현재 위치에 가까운 음식점 찾기
 4. 버거를 판매하는 근처 음식점 찾기






1. W3C API로 위치 파악하기

이번 실습에서, 지리 공간 API를 사용하여 방문객의 위치를 파악하는 웹페이지를 구축하도록 하겠습니다. 

먼저 location.html을 작성해주세요!

location.html
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
<!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"/>
        <style type="text/css" media="screen">
div#map {
width: 450px;
height: 400px;
}
</style>
        <title> Locating your position </title>
    </head>
    <body>
        <div id="contentarea">
            <div id="innercontentarea">
                <h2> Locating your position </h2>
                <div id="map"></div>
            </div>
        </div>
        <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
        <script type="text/javascript" src="geolocation.js"></script>
    </body>
</html>
 
cs





다음 geolocation.js를 작성합니다.

geolocation.js
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
 
var mapContainer = document.getElementById('map');
var map;
 
function init() {
    // Google Map Set(축척, 지도 유형 등)
    var mapOptions = { 
        zoom: 16,
        disableDefaultUI: true,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    
    // 지도는 mapContainer 내부에 mapOptions으로 그려짐
    map = new google.maps.Map(mapContainer, mapOptions);
    detectLocation();
}
 
function detectLocation() {
    var options = {
        enableHighAccuracy: true,
        maximumAge: 1000,
        timeout: 30000
    };
    
    // 브라우저가 Geolocation 객체를 지원하는지 check
    if (window.navigator.geolocation) {
        window.navigator.geolocation.getCurrentPosition(
            drawLocationOnMap,
            handleGeolocateError,
            options
        );
    } else {
        alert("Sorry, your browser doesn't seem to support geolocation :-(");
    }
}
 
// getCurrentPosition()의 콜백함수로 구글 맵에 위치를 찍음
function drawLocationOnMap(position) {
    // Position 객체에서 위도 / 경도 얻기
    var lat = position.coords.latitude;
    var lon = position.coords.longitude;
    var msg = "You ar here: Latitude +" + lat + ", Longitude " + lon;
    
    var pos = new google.maps.LatLng(lat, lon);
    var infoBox = new google.maps.InfoWindow({
        map: map,
        position: pos,
        content: msg
    });
    
    map.setCenter(pos);
    
    return;
}
 
function handleGeolocateError() {
    alert("Sorry, couldn't get your geolocation: -(");
}
 
window.onload = init;
cs




브라우저에서 location.html을 열어주세요. 열어주면, 위치 접근 허가를 묻는데 허용을 해야 어플리케이션이 구동되는지 알수 있겠죠?? 
아래와 같이 결과가 나온답니다~




 





이번 장의 구현된 객체 등에 대해 설명하겠습니다.

 Geolocation 객체
  - 이 객체는 W3C 지리 공간 API를 구현합니다. 
     JS 엔진은 이 객체를 사용해 브라우저가 동작 중인 컴퓨터나 전화의 지리 공간 정보를 얻습니다. 
     Geolocation은 브라우저 객체(window.navigator)의 속성이며, window.navigator.geolocation으로 접근합니다. 
     이번 예제에서 Geolocation 객체에 접근하는 방법으로 브라우저에 지리 공간 기능이 있는지 검사하고,, 브라우저가 테스트에 실패하면 이를 사용자에게 통지하도록 구축했습니다.


 - getCurrentPosition() 메소드: 객체에 존재하는 getCurrentPosition() 메소드를 호출하는 방법으로 위치 정보를 얻습니다.
  쓰임새는 다음과 같습니다.

   getCurrentPosition(callbackOnSuccess, [callbackOnFailure, options])

  callbackOnSuccess 인수는 콜백 함수를 참조합니다. 이 콜백 함수는 getCurrentPosition() 메소드가 성공적으로 지리 좌표를 파악할 때 호출됩니다. 
  callbackOnFailure는 선택 옵션이며, 지리 공간을 얻는 데 실패할 경우 처리하기 위한 콜백 함수입니다.
  options는 추가적인 메소드에 대한 구성 매개변수를 지정하는 PositionOptions 객체를 표현합니다.
 
  PositionOptions에는 다음과 같은 속성이 있고, 저희는 이 속성을 건드렸죠~
    · enableHighAccuracy: API에게 정확한 현재 위치를 얻기 위해 최선을 다하도록 요청합니다. 기본값은 false로 설정되어 있습니다. true로 설정할 때, API반응이 느려지는 경향이 있습니다.
    · maximumAge: API 반응을 캐시하는 경우에, 이 설정값은 maximumAge 밀리초보다 오래된 캐시값을 사용하지 않도록 지정합니다.
    · timeout: API응답을 받기 위해 밀리초 단위로 지정한 타임아웃 값입니다.








2. 지리 공간 색인 생성하기


몽고 쉘을 열어서 아래와 같이 작성해주세요~ 빨간색으로 체크된 부분만 작성해주시면 됩니다~







  
위 작업에 대해 보충 설명을 하자면, map이라는 데이터베이스에 geocollection이라는 몽고 DB Collection을 만들었습니다. 수동으로 컬렉션에 다큐먼트를 삽입했고, 다큐먼트마다 들어있는 coordinate라는 내장된 다큐먼트는 임의로 설정한 latitude와 longitude 값을 포함합니다. 
 그러고 나서, 해당 컬렉션에 대해 ensureIndex()를 호출하여 latitude/longitude 쌍에 대한 지리 공간 색인(Index)를 구축했습니다.






3. 현재 위치에 가까운 음식점 찾기
 다음으로 색인이 걸린 필드를 대상으로 위치 질의를 수행하는 방법을 배울 시간입니다~ 
 먼저, restaurants라는 컬렉션으로 몇 가지 데이터를 삽입합니다~

 db.restaurants.insert({name:"McDowells", serves: "Fast Food", "location": [23.755235, 90.375739]})
 db.restaurants.insert({name:"Bucksters Coffee", serves: "Fast Food", "location": [23.755339, 90.375408]})
 db.restaurants.insert({name:"Dinkin Donuts", serves: "Fast Food", "location": [23.752538, 90.382792]})

그러고 나서, 지리 공간 색인을 구축합니다.
 
 db.restaurants.ensureIndex({location:"2d"})


 




다음으로 restaurants.html을 작성합니다.

restaurants.html
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
<!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"/>
        <style type="text/css" media="screen">
            div#map {
                width: 450px;
                height: 400px;
            }
        </style>
        <title> Restaurants near me </title>
    </head>
    <body>
        <div id="contentarea">
            <div id="innercontentarea">
                <h2> Restaurants near me </h2>
                <div id="map"></div>
                <br />
            </div>
        </div>
        <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
        <script type="text/javascript" src="restaurants.js"></script>
    </body>
</html>
 
cs

 





다음으로 restaurants.js를 작성합니다

restaurants.js
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
 
var mapContainer = document.getElementById('map');
var map;
 
function init() {
    // Google Map Set(축척, 지도 유형 등)
    var mapOptions = { 
        zoom: 14,
        disableDefaultUI: true,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    
    // 지도는 mapContainer 내부에 mapOptions으로 그려짐
    map = new google.maps.Map(mapContainer, mapOptions);
    detectLocation();
}
 
function detectLocation() {
    var options = {
        enableHighAccuracy: true,
        maximumAge: 1000,
        timeout: 30000
    };
    
    // 브라우저가 Geolocation 객체를 지원하는지 check
    if (window.navigator.geolocation) {
        window.navigator.geolocation.getCurrentPosition(
            markMyLocation,
            handleGeolocateError,
            options
        );
    } else {
        alert("Sorry, your browser doesn't seem to support geolocation :-(");
    }
}
 
function markMyLocation(position) {
    // Position 객체에서 위도 / 경도 얻기
    var lat = position.coords.latitude; 
    var lon = position.coords.longitude;
    var msg = "You are here";
    
    var pos = new google.maps.LatLng(lat, lon);
    
    map.setCenter(pos);
    
    var infoBox = new google.maps.InfoWindow({
        map: map,
        position: pos,
        content: msg
    });
    
    // 현재 위치 구글 맵 표식 그리기
    var myMarker = new google.maps.Marker({
        map: map,
        position: pos
    });
    
    getNearByRestaurants(lat, lon);
    
    return;
}
 
function handleGeolocateError() {
    alert("Sorry, couldn't get your geolocation: -(");
}
 
function getNearByRestaurants(lat, lon) {
    $.ajax({
        url: 'query.php?lat=' + lat + '&lon=' + lon
        , dataType: 'json'
        , success: ajaxSuccess
    });
}
 
// AJAX 를 위한 콜백 함수
function ajaxSuccess(data) {
    data.forEach(function(restaurant) {
        var pos = new google.maps.LatLng(restaurant.latitude, restaurant.longitude);
        var marker = new google.maps.Marker({
            map: map,
            position: pos
        });
    });
}
 
window.onload = init;
cs




마지막으로 query.php

query.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
<?php
$lat = (float) $_GET['lat'];
$lon = (float) $_GET['lon'];
 
$mongo = new Mongo();
$collection = $mongo->selectDB('geolocation')->selectCollection('restaurants');
 
$query = array(
'location' => array('$near' => array($lat$lon))
);
 
$cursor = $collection->find($query);
$response = array();
 
while ($doc = $cursor->getNext()) {
$obj = array(
'name' => $doc['name'],
'serves' => $doc['serves'],
'latitude' => $doc['location'][0],
'longitude' => $doc['location'][1]
);
array_push($response$obj);
}
 
echo json_encode($response);
?>
cs




 

요렇게 작성하고 실행해주시면!!!!! 각자의 위치와 함께 식당이 떠야되는데.... 되는데... 식당은 안뜨고... you are here 만 떳네요...


 





아무래도 몽고DB에 넣은 위치값은 저멀리 있었나 봅니다.... 방글라데시에 있네요....






 






4. 버거(Fast Food)를 판매하는 근처 음식점 찾기
이번에는 지리 공간 헤이스택 색인을 사용하여 버거를 판매하는 곳을 찾아보겠습니다. 

수행에 앞서, 저는 저희 동네 버거 집을 물색하여 insert해놨습니다.

그럼 진행 하도록 하겠습니다.

몽고 쉘에서  
 db.restaurants.ensureIndex({location:"geoHaystack", serves:1}, {bucketSize: 1})

 입력하세요~
 

다음으로 haystack.html을 작성합니다.

haystack.html

 

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
<!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"/>
        <style type="text/css" media="screen">
div#map {
width: 450px;
height: 400px;
}
</style>
        <title> Burger place near me </title>
    </head>
    <body>
        <div id="contentarea">
            <div id="innercontentarea">
                <h2> Burger place near me </h2>
                <div id="map"></div>
                <br />
            </div>
        </div>
        <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
        <script type="text/javascript" src="haystack.js"></script>
    </body>
</html>
 
cs





haystack.js


 

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
 
var mapContainer = document.getElementById('map');
var map;
 
function init() {
    // Google Map Set(축척, 지도 유형 등)
    var mapOptions = { 
        zoom: 15,
        disableDefaultUI: true,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    
    // 지도는 mapContainer 내부에 mapOptions으로 그려짐
    map = new google.maps.Map(mapContainer, mapOptions);
    detectLocation();
}
 
function detectLocation() {
    var options = {
        enableHighAccuracy: true,
        maximumAge: 1000,
        timeout: 30000
    };
    
    // 브라우저가 Geolocation 객체를 지원하는지 check
    if (window.navigator.geolocation) {
        window.navigator.geolocation.getCurrentPosition(
            markMyLocation,
            handleGeolocateError,
            options
        );
    } else {
        alert("Sorry, your browser doesn't seem to support geolocation :-(");
    }
}
 
function markMyLocation(position) {
    // Position 객체에서 위도 / 경도 얻기
    var lat = position.coords.latitude; 
    var lon = position.coords.longitude;
    var msg = "You are here";
    
    var pos = new google.maps.LatLng(lat, lon);
    
    map.setCenter(pos);
    
    var infoBox = new google.maps.InfoWindow({
        map: map,
        position: pos,
        content: msg
    });
    
    // 현재 위치 구글 맵 표식 그리기
    var myMarker = new google.maps.Marker({
        map: map,
        position: pos
    });
    
    getNearByRestaurants(lat, lon);
    
    return;
}
 
function handleGeolocateError() {
    alert("Sorry, couldn't get your geolocation: -(");
}
 
function getNearByRestaurants(lat, lon) {
    $.ajax({
        url: 'haystack.php?lat=' + lat + '&lon=' + lon
        , dataType: 'json'
        , success: ajaxSuccess
    });
}
 
// AJAX 를 위한 콜백 함수
function ajaxSuccess(data) {
    data.forEach(function(restaurant) {
        var pos = new google.maps.LatLng(restaurant.latitude, restaurant.longitude);
        var marker = new google.maps.Marker({
            map: map,
            position: pos
        });
        var infoBox = new google.maps.InfoWindow({
            map: map,
            position: pos,
            content: restaurant.name
        });
    });
}
 
window.onload = init;
cs





haystack.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
<?php
$lat = (float) $_GET['lat'];
$lon = (float) $_GET['lon'];
 
$mongo = new Mongo();
$db = $mongo->selectDB('geolocation');
 
$command = array(
'geoSearch' => 'restaurants',
'near' => array($lat$lon),
'search' => array('serves' => 'Fast Food'),
'maxDistance' => 10
);
 
$response = $db->command($command);
$jsonResponse = array();
 
foreach ($response['results'as $result) {
$obj = array(
'name' => $result['name'],
'serves' => $result['serves'],
'latitude' => $result['location'][0],
'longitude' => $result['location'][1]
);
 
array_push($jsonResponse$obj);
}
 
echo json_encode($jsonResponse);
 
?>
cs





이렇게 하고 실행하면!!!






요렇게 나오네요~

네. 이상으로 이번 장 포스팅을 마치겠습니다~







다른 카테고리의 글 목록

CSE/MongoDB 카테고리의 포스트를 톺아봅니다
조회수 확인

이번 포스팅은 GridFS로 파일처리를 해보도록 하겠습니다!

우선 GridFS란??
 "데이터에비스에 이진 데이터를 저장하기 위한 몽고DB의 해법"이라고 일단락 지어두죠.
 
 이론적 설명: 설계상, 몽고DB 다큐먼트(BSON 객체)는 16MB를 넘을 수 없습니다. 이런 제약은 최적의 수준으로 성능을 유지하기 위해서 입니다. 위 크기보다 커지게 되면, 질의시 메모리를 엄청나게 소비합니다. GridFS는 대규모 파일을 여러 다큐먼트로 쪼개는 기법을 명세합니다. 이를 구현하는 언어 드라이버(ex: PHP driver)가 저장된 파일을 분리하는 작업은 이면에 숨겨져 있죠. 이 드라이버를 사용하는 개발자는 내부 지식이 필요하지 않습니다. 이런 식으로 GridFS는 개발자에게 파일을 투명하고 효율적인 방법으로 저장하고 조작하는 작업을 허용하게 됩니다. 

자 그럼 이번 장의 실습 순서를 보겠습니다.
 1. GridFS로 이미지 올리기
 2. GridFS에서 이미지 서비스하기
 3. chunks에서 이미지 읽기

먼저, dbconnection.php와 style.css를 가져와 둡시다.








1. GridFS로 이미지 올리기
먼저, upload.php를 작성합니다.

upload.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
 
<?php
require 'dbconnection.php';
 
$action = (isset($_POST['upload']) && $_POST['upload'] === 'Upload') ? 'upload' : 'view';
 
switch ($action) {
case 'upload': {
// 파일 업로드 성공을 검사
if ($_FILES['image']['error'] !== 0) {
die("Error uploading file. Error code ".$_FILES['image']['error']);
}
 
$mongodb = DBConnection::instantiate();
$gridFS = $mongodb->database->getGridFS();
$filename = $_FILES['image']['name'];
$filetype = $_FILES['image']['type'];
$tmpfilepath = $_FILES['image']['tmp_name'];
$caption = $_POST['caption'];
 
// 업로드된 파일을 저장
$id = $gridFS->storeFile($tmpfilepatharray('filename' => $filename,
 'filetype' => $filetype,
 'caption' => $caption)
);
break;
}
 
default:
}
?>
 
<!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> Upload Files </title>
    </head>
    <body>
        <div id="contentarea">
            <div id="innercontentarea">
                <h1> Upload Image </h1>
                <?php if ($action === 'upload'):?>
                    <h3> File Uploaded. Id <?php echo $id;?>
                        <a href="<?php echo $_SERVER['PHP_SELF']; ?>">
                            Upload another?
                        </a>
                    </h3>
                <?php else:?>
                    <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST" accept-charset="utf-8" enctype="multipart/form-data">
                        <h3> Enter Caption&nbsp;
                            <input type="text" name="caption" />
                        </h3>
                        <p>
                            <input type="file" name="image"/>
                        </p>
                        <p>
                            <input type="submit" name="upload" value="Upload"/>
                        </p>
                    </form>
                <?php endif;?>
            </div>
        </div>
    </body>
</html>
 
cs




다음, dbconnection.php의 DBNAME상수를 'myfiles'로 변경합니다.







 

1
2
3
 
        const DBNAME = 'myfiles';
 
cs




브라우저에서 upload.php를 열어줍니다. 파일 Caption을 입력하고, 파일 선택을 눌러 이미지 파일을 골라주세요.
그러고나서 Upload를 눌러주면 됩니다.






아래와 같이 나오면 성공!!



 




몽고 쉘에서도 확인해 봐야겠죠???

use myfiles
db.fs.files.find() 


해주시면 이미지가 들어간게 나옵니다!!!




 







2. GridFS에서 이미지 서비스하기
list.php 파일을 작성합니다.

list.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
 
<?php
require 'dbconnection.php';
 
$mongo = DBConnection::instantiate();
$gridFS = $mongo->database->getGridFS();
$objects = $gridFS->find();
?>
 
<!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> Uploaded images </title>
    </head>
    <body>
        <div id="contentarea">
            <div id="innercontentarea">
                <h1> Uploaded images </h1>
                <table class="table_list" cellpadding="0" cellspacing="0">
                    <thead>
                        <tr>
                            <th width="40%">Caption </th>
                            <th width="30%">Filename </th>
                            <th width="*">Size </th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php while ($object = $objects->getNext()):?>
                            <tr>
                                <td><?php echo $object->file['caption'];?></td>
                                <td>
                                    <a href="image.php?id=<?php echo $object->file['_id'];?>">
                                        <?php echo $object->file['filename'];?>
                                    </a>
                                </td>
                                <td><?php echo ceil($object->file['length'] / 1024).' KB';?></td>
                            </tr>
                        <?php endwhile;?>
                    </tbody>
                </table>
            </div>
        </div>
    </body>
</html>
 
cs


 



다음으로 image.php파일을 작성합니다.

image.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
<?php
$id = $_GET['id'];
require 'dbconnection.php';
 
$mongo = DBConnection::instantiate();
$gridFS = $mongo->database->getGridFS();
 
// 파일 객체를 질의
$object = $gridFS->findOne(array('_id' => new MongoId($id)));
 
// Content-type 헤더를 설정, 브라우저 출력
header('Content-type: '.$object->file['filetype']);
 
echo $object->getBytes();
?>
 
 
cs


 



브라우저에서 list.php파일을 수행합니다. 그러면 아까 1번에서 올린 사진이 들어가 있습니다. 사진파일 명을 클릭해주세요!




 





그러면 저는 Eva green을 좋아하기 때문에 요런 사진을 출력하네요~












3. Chunks에서 이미지 읽기
 직전 예제에서 getBytes() 메소드를 사용해 파일 내용을 읽었습니다. getBytes()가 파일의 전체 내용을 메모리에 올리려고 시도하기에 이런 접근 방식에 잠재적인 문제가 있습니다!! 그래서!!! chunks에 파일 내용이 나뉘어져 있습니다. 이 chunk를 이용한 실습을 진행하겠습니다.


stream.php


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 
<?php
$id = $_GET['id'];
require 'dbconnection.php';
 
$mongo = DBConnection::instantiate();
$gridFS = $mongo->database->getGridFS();
$object = $gridFS->findOne(array('_id' => new MongoId($id)));
 
// 이파일에 대한 chunk를 찾는다.
$chunks = $mongo->database->fs->chunks->find(array('files_id' => $object->file['_id']))->sort(array('n' => 1));
 
header('Content-type: '.$object->file['filetype']);
 
foreach($chunks as $chunk) {
echo $chunk['data'] -> bin;
}
 
?>
 
 
cs


 


다음으로 list.php 파일에서 <a href="image.php?id................ 요부분을 찾아서 아래처럼 바꿔주세요!!



 

1
2
3
 
<a href="stream.php?id=<?php echo $object->file['_id'];?>">
                                    
cs





그 다음, 실행!!!!! 아까와 똑같은 list.php입니다. 기능적으로는 동일하죠. Filename을 클릭하세요.



 







그러면 아래와 같이 chunks를 읽어서 이미지를 출력합니다.(이번에도 Eva green~)











chunks를 이용하면 직전의 image.php보다 작업 과정에서 메모리를 적게 소비합니다. 왜냐하면 stream.php에서는 동일한 메모리를 계속해서 재사용하기 때문이죠~


자 이렇게 좋은 GridFS가 사용이 되면 안될 때가 있습니다~
 몽고DB는 주 핵심은 확장성입니다. 어플리케이션을 수평 확장하도록 도와주기 위한 기능을 염두해서 설계되었죠. 이러한 몽고DB가 트래픽의 규모가 작거나 중간 정도인데 이 GridFS를 쓰면 파일 서비스는 Xhit 입니다!!!!! 

 또한, 작은 정적 파일(웹사이트에서 JS와 CSS등)을 서비스할 경우에 파일 시스템 상에서 아파치나 엔진엑스 웹 서버를 사용하는 경우가 GridFS보다 더 빠르다는 사실이 벤치마크 결과로 나와있다네요~ 따라서 HTTP로 작은 파일만 서비스할 필요가 있다면 파일 시스템에 의존해야 마땅하답니다~



개념 정리를 마지막으로 이 포스팅을 마치겠습니다~


다른 카테고리의 글 목록

CSE/MongoDB 카테고리의 포스트를 톺아봅니다
조회수 확인

이번 포스팅은 관계형 DB와 몽고DB를 조합한 어플리케이션 형을 만들어 보겠습니다!!


먼저 RDBMS와 MongoDB를 사용하는 잠재적인 사례에 대해 알아보도록 하겠습니다!

 - 집계 연산 질의 결과 저장: Cost가 많이 드는 집계 연산 질의 결과(COUNT, GROUP BY 등)를 몽고DB에 저장할 수 있습니다. 이런 기능은 결과가 유효하지 않을 때까지 애플리케이션에서 동일 질의를 다시 수행할 필요 없이 몽고DB에서 결과를 잽싸게 가져오게 만듭니다. 몽고DB 컬렉션의 스키마는 유연하므로, 결과 데이터의 구조를 미리 걱정할 필요가 없습니다. 집계 연산 질의가 반환하는 Row는 BSON 다큐먼트로 저장이 가능합니다.

 - 데이터 저장: 데이터량이 커질수록, 관계형 테이블에서 질의와 다른 연산 과정에 필요한 시간이 점점 더 늘어납니다. 이런 문제를 풀기위한 한가지 해법으로 데이터를 나눠 테이블 두개에 넣습니다. 여기서 Online 테이블은 작업 중인 데이터 집합을 포함하고, Archival 테이블은 옛날 데이터를 포함합니다. Online 테이블의 크기는 비슷한 수준을 유지하는 반면, Archival 테이블은 점점 커집니다. 이런 접근 방법의 단점은 Online 테이블의 스키마가 변경될 때, Archival 테이블의 스키마 역시 동일하게 변경되어야만 한다는 점이죠. 스키마 변경이 아주 느린 연산인 이유는 데이터량 때문입니다. 또한, Online 테이블에서 컬럼을 하나 이상 삭제하면, Archival 테이블에서도 동일 컬럼을 삭제해야만 합니다. 따라서 가치 있을지도 모르는 옛날 데이터를 잃어버리는 결과를 초래합니다. 이때, 몽고DB의 컬렉션을 사용하여 해법을 풀수 있는 겁니다. 몽고DB의 유연한 스키마 덕문에 예전 테이블과 새로운 테이블의 구조가 달라질 경우에도 뭔가를 반드시 수행할 필요가 없죠.

 - 로깅: 애플리케이션에서 이벤트 로깅 목적으로 몽고DB를 활용할 수 있습니다. RDBMS로도 구현 가능하지만, 몽고DB의 비동기식 삽입 기능과 몽고 질의 언어(Map reduce)로 더 나은 로깅 벡엔드를 만들수 있습니다.


 그럼 실습을 진행하도록 하겠습니다. 순서는 아래와 같습니다. 
  1. MySQL에서 DB 생성
  2. 몽고DB에 일일 제품 판매 이력 저장
  3. 몽고DB에 예전 판매 기록 저장
  4. 몽고DB를 사용해 고객 메타 데이터 저장


이번 장은 MySQL에 DB를 생성하여 예제 데이터를 입력해서 실습해야합니다. 책에서 아주 친절히(?) 예제 생성하는 방법만 알려주고, 예제 데이터에 대한 예시는 없습니다... 저 또한, 귀차?니즘? 때문에 만들지 않았구요... 그래서 웹페이지 실습시 텅텅 비어있을 겁니다...







1. MySQL에서 DB 생성
 먼저 mysql shell을 열어 줍니다.(mysql -u root -p) 
 첨부된 sql파일을 받아서 mysql에서 수행합니다. 모든 sql문 들이 성공했는지 확인하시면 끝!




2. 몽고DB에 일일 제품 판매 이력 저장하기
mysql.php를 생성하여 아래 코드를 입력합니다.

* 주의: 여기서 MYSQL_USER와 MYSQL_PASSWD 등은 자신이 설정한 user 명이나 패스워드를 입력하세요!!

mysql.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
define('MYSQL_HOST''localhost');
define('MYSQL_PORT'3306);
define('MYSQL_USER''root');
define('MYSQL_PASSWD''**********');
define('MYQL_DBNAME''acmeproducts');
 
function getMySQLConnection() {
$mysqli = new mysqli(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWD, MYQL_DBNAME, MYSQL_PORT);
 
if (mysqli_connect_error()) {
die(sprintf('Error connecting to MySQL. Error No: %d, Error: %s',mysqli_connect_errno(), mysqli_connect_error()));
}
 
return $mysqli;
}
?>
 
 
cs



dbconnection.php를 복사해와서 DBNAME의 값을 acmeproducts_mongo로 변경합니다.

1
2
3
 
        const DBNAME = 'acmeproducts_mongo';
 
cs

 






그 다음, aggregate.php를 작성합니다. $query에서 띄어쓰기 유의하세요!! mysql에서 error 날 수도 있습니다!

aggregate.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
 
<?php
require 'mysql.php';
require 'dbconnection.php';
 
// MySQL Section
$query = 'SELECT name, DATE(time_of_sales) as date_of_sales, SUM(units_sold) as total_units_sold '.
 'FROM sales s INNER JOIN products p ON(p.id = s.product_id) '.
 'GROUP BY product_id, DATE(time_of_sales)';
 
$mysql = getMySQLConnection();
$result = $mysql->query($query);
 
if ($result === False) {
die(sprintf("Error executing query %s, %d"$mysql->error, $mysql->errno));
}
 
$salesByDate = array();
 
while ($row = $result->fetch_assoc()) {
$date = $row['date_of_sales'];
$product = $row['name'];
$totalSold = $row['total_units_sold'];
$salesPerProduct = (isset($salesByDate[$date])) ? $salesByDate[$date] : array();
$salesPerProduct[$product] = $totalSold;
$salesByDate[$date] = $salesPerProduct;
}
 
$result->free();
$mysql->close();
 
 
// MongoDB Section
$mongodb = DBConnection::instantiate();
$collection = $mongodb->getCollection('daily_sales');
foreach ($salesByDate as $date => $sales) {
$document = array(
'sales_date' => new MongoDate(strtotime($date)),
'items' => array()
);
 
foreach ($sales as $product => $unitsSold) {
$document['items'][$product] = $unitsSold;
}
$collection->insert($document);
}
?>
 
cs




작성된 aggregate.php를 웹페이지에서 수행합니다. 그리고 daily_sales.php를 작성합니다.

daily_sales.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
 
<?php
require 'dbconnection.php';
 
$action = (isset($_POST['action'])) ? $_POST['action'] : 'default';
 
// 입력 날짜 검증 메소드
function validateInput() {
if (empty($_POST['year']) || empty($_POST['month']) || empty($_POST['day'])) {
return False;
}
$timestamp = strtotime($_POST['year'].'-'.$_POST['month'].'-'.$_POST['day']);
 
if (!is_numeric($timestamp)) {
return False;
}
 
return checkdate(date('m',$timestamp), date('d',$timestamp), date('Y',$timestamp));
}
 
switch ($action) {
case 'Show': {
if (validateInput() == True) {
$inputValidated = True;
 
$date = sprintf('%d-%d-%d'$_POST['year'], $_POST['month'], $_POST['day']);
$mongodate = new MongoDate(strtotime($date));
$mongodb = DBConnection::instantiate();
$collection = $mongodb->getCollection('daily_sales');
$doc = $collection->findOne(array('sales_date' => $mongodate));
else {
$inputValidated = False;
}
break;
}
 
default:
}
?>
 
<!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> Acme Corp | Daily Sales </title>
    </head>
    <body>
        <div id="contentarea">
            <div id="innercontentarea">
                <h1> Daily Sales of Acme Products </h1>
                <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST">
                    Enter Date (YYYY-MM-DD)
                    <input type="text" name="year" size=4/> -
                    <input type="text" name="month" size=2/> -
                    <input type="text" name="day" size=2/
                    <input type="submit" name="action" value="Show"/>
                </form>
                <?php if ($action === 'Show'): if ($inputValidated === True):?>
                    <h3<?php echo date('F j, Y'$mongodate->sec);?> </h3>
                <?php if (!empty($doc)):?>
                    <table class="table_list" cellspacing="0" cellpadding="0">
                        <thead>
                            <tr>
                                <th width="50%"> Item </th>
                                <th width="25%"> $nbsp; </th>
                                <th width="*"> Units Sold </th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php foreach ($doc['items'as $item => $unitsSold):?>
                                <tr>
                                    <td><?php echo $item;?></td>
                                    <td>&nbsp;</td>
                                    <td><?php echo $unitsSold;?></td>
                                </tr>
                            <?php endforeach;?>
                        </tbody>
                    </table>
                    <?php elseecho "<p> No sales record found. </p>"
endif; 
elseecho "<h3> Invalid input. Try again.</h3>";
endif;
endif;?>
            </div>
        </div>
    </body>
</html>
 
cs








브라우저에서 daily_sales.php를 수행하세요! 초기화면 입니다.








요기다 임의 날짜를 입력해서 데이터가 담긴 페이지를 볼수 있으나, 예제 데이터가 없는 관계상 아래와 같이 출력이 되네요.








 3. 몽고DB에 예전 판매 기록 저장하기

archive_sales_data.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
 
<?php
require 'mysql.php';
require 'dbconnection.php';
 
$cutoffDate = date('Y-m-d', strtotime('-30 day'));
$mysql = getMySQLConnection();
 
$query = sprintf("SELECT * FROM sales WHERE DATE(time_of_sales) < '%s'"$cutoffDate);
printf("Fetching old data from MySQL...... \n");
$result = $mysql->query($query);
 
if ($result === False) {
die(sprintf("Error executing query %s"$mysql->error));
}
 
printf("Migrating to MongoDB ..... \n");
 
$mongo = DBConnection::instantiate();
$collection = $mongo->getCollection('sales_archive');
 
while ($record = $result->fetch_assoc()) {
try {
$collection->insert($record);
} catch (MongoCursorException $e) {
die ("Migration Failed ".$e->getMessage());
}
}
 
printf("\tDone. %d records migrated. \n"$result->num_rows);
$result->free();
printf("Deleting old data from MySQL...\n");
$query = sprintf("DELETE FROM sales WHERE DATE(time_of_sales)""< '%s'"$cutoffDate);
 
$status = $mysql->query($query);
 
if ($status === False) {
die(sprintf("Error executing query %s"$mysql->error));
}
 
$mysql->close();
printf("Archiving complete.\n");
 
?>
 
cs
 



웹에서 위의 스크립트를 실행합니다. 아래와 같이 나오면 성공.




 



다음으로 mongo shell에서 query를 날립니다. (예제 데이터가 없으므로, 아무것도 안뜬다)



 





4번까지 진행하려 했으나, 예제데이터가 없는데 보여드릴게 없으므로 이만 pass 하겠습니다ㅜㅜㅜ



마지막으로 몽고DB와 RDBMS를 함께 쓰는 과정에서 일어나는 문제점만 짚고 넘어가겠습니다!
 - 데이터 일관성
 - 소프트웨어 아키텍처의 복잡도
 - 추가 구성 요소를 지원하는 비용






다른 카테고리의 글 목록

CSE/MongoDB 카테고리의 포스트를 톺아봅니다
조회수 확인
이번 포스팅은 몽고DB를 사용한 웹 분석 ​을 진행해보도록 하겠습니다!

내용을 설명하기 전, 준비해야할 것이 있습니다! 
 예전에 작성한 blog.php, blogs.php, dbconnection.php가 이번 실습하는 곳에 같은 곳에 존재해야 합니다!
 
이번 포스팅의 순서는 다음과 같습니다. 
 1. 페이지 방문 로깅
 2. 블로그 포스트당 전체 방문 수와 평균 응답 시간 찾기
 3. 실시간 페이지 방문 카운트 구현







먼저, 중요 개념을 설명하도록 하겠습니다.
 
몽고DB가 웹 분석 백엔드로 좋은 이유!!!!
 - 몽고DB는 대량의 데이터를 다루는데 아주 적합
  : 몽고DB의 확장 가능한 기능(복제, 샤딩, 레플리카 집합 등)과 데이터 크기와 연산 횟수에 따라 최적으로 수행하는 기능은 점점 확장되고 있다. 트래픽이 무척 큰 웹사이트는 사이트에서 일어나는 사용자 활동 전체를 저장하기 위해 몽고DB를 사용할 수 있으며, 배경 작업으로 데이터를 처리하고 분석 할 수 있게 된다.

 - 몽고DB는 비동기식 삽입을 지원
 ​ : 비동기식 기능은 PHP, C, 몽고 셸의 자바스크립트 인터페이스 중 무엇을 사용하든 애플리케이션 코드가 몽고 DB에 다큐먼트를 삽입한 다음에 서버의 응답을 기다리지 않고 다음 명령으로 이동하도록 요청한다. 이 특징이 훌륭한 로깅 도구로 만들어 준다. 

 - 맵리듀스!

자, 그럼 실습을 진행해보도록 하겠습니다!






 1. 페이지 방문 로깅

먼저 log.php를 작성합니다.

log.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
<?php
require_once('dbconnection.php');
define('LOGNAME''access_log');
 
 
class Logger {
private $_dbconnection;
private $_db;
 
public function __construct() {
$this->_dbconnection = DBConnection::instantiate();
$this->_collection = $this->_dbconnection->getCollection(LOGNAME);
}
 
public function logRequest($data = array()) {
$request = array();
// $_SERVER에 접근하여 HTTP 요청정보 얻음
$request['page'] = $_SERVER['SCRIPT_NAME'];
$request['viewed_at'] = new MongoDate($_SERVER['REQUEST_TIME']);
$request['ip_address'] = $_SERVER['REMOTE_ADDR'];
$request['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
 
if (!empty($_SERVER['QUERY_STRING'])) {
$params = array();
foreach (explode('&'$_SERVER['QUERY_STRING']) as $parameter) {
list($key$value) = explode('='$parameter);
$params[$key] = $value;
}
$request['query_params'] = $params;
}
 
if (!empty($data)) {
$request = array_merge($request$data);
}
$this->_collection->insert($request);
}
}
 
?>
 
cs



다음, blog.php 파일을 열어, 첫 부분에 다음 코드를 추가해주세요. (당연히 <?php ?> 안에 넣어 주시면 됩니다)
 


1
2
3
4
 
    require('log.php');
    $start = microtime();
 
cs




그리고 마지막 부분에다 이 코드를 추가하세요.(</html> 밑에다가 넣어주세요)


 

1
2
3
4
5
6
7
8
 
 
<?php
$end = microtime();
$data = array('response_time_ms' => ($end - $start) * 1000);
$logger = new Logger();
$logger->logRequest($data);
?>
cs

 






mongo 셸로 가서 

 use myblogsite
 db.createCollection('access_log', {capped: true, size: 100000}) 

이렇게 작성해주세요!




 







위 작업을 마치고 나서, blogs.php 페이지를 열어, 각 포스트를 Read More를 여러 번 클릭해주세요.
그러고 나서, 셸에다가



 db.access_log.find()


입력 합니다. 그럼 아래와 같이 출력 결과를 뿌려줍니다.












위 로그를 볼때, 가장 오래된 다큐먼트를 먼저 반환하는데, 가장 새로운 다큐먼트 순으로 반환을 원할경우
위 명령어 뒤에 .sort({$natural : -1})을 옵션을 주면 된다.  


 


 
2. 블로그 포스트당 전체 방문 수와 평균 응답 시간 찾기

지난 일주일 동안 블로그 포스트마다 방문한 숫자와 함께 화면 출력에 있어 평균 응답 시간을 파악하도록 맵과 리듀스 함수를 정의하는 프로그램을 작성해 보겠습니다! 

먼저 page_views.php를 작성합니다

page_views.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
<?php
require('dbconnection.php');
$dbConnection = DBConnection::instantiate();
$db = $dbConnection->database;
 
/* map 함수: 문서별로 count와 resp_time을 emit()으로 보냄 */
$map = "function() { emit(this.query_params.id, {count: 1, resp_time: this.response_time_ms}) }";
 
/* reduce 함수: 카운터와 응답 시간 값을 종합 */
$reduce = "function(key, values) { ".
                "var total_count = 0;".
                "var total_resp_time = 0;".               
                "values.foreach(function(doc) {".
                    "total_count += doc.count;".
                    "total_resp_time += doc.resp_time;".               
                "});".               
                "return {count: total_count, resp_time: total_resp_time};".
          "}";
 
/* finalize 함수: 카운터 총합으로 응답 시간 총합을 나누는 방법을 사용해 평균 응답 시간을 찾음 */
$finalize = "function(key, doc) {".
                 "doc.avg_resp_time = doc.resp_time / doc.count;".
                 "return doc;".
            "}";
 
$db->command(array(
                    'mapreduce' => 'access_log'
                    'map' => new MongoCode($map),
                    'reduce' => new MongoCode($reduce),
    //                'query' => array('page' => '/blog.php', 
    //                                'viewed_at' => array('$gt' => new MongoDate(strtotime('-7 days')))),
                    'finalize' => new MongoCode($finalize),
                    'out'   => 'page_views_last_week'
                )
            );
 
 
$results = $dbConnection->getCollection('page_views_last_week')
                        ->find();
 
function getArticleTitle($id) {
global $dbConnection;
$article = $dbConnection->getCollection('articles')->findOne(array('_id' => new MongoId($id)));
 
return $article['title'];
}
 
//echo json_encode($results);
?>
 
<!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" />
        <title> Most viewd articles </title>
        <link rel="stylesheet" href="style.css"/>
        <style type="text/css" media="screen">
body { font-size: 13px; }
div#contentarea { width: 680px; }
</style>
    </head>
    <body>
        <div id="contentarea">
            <div id="innercontentarea">
                <h1> Most viewed articles (Last 7 days) </h1>
                <table class="articles" cellpadding="0" cellspacing="0">
                    <thead>
                        <tr>
                            <th width="50%">Article</th>
                            <th width="25%">Page views</th>
                            <th width="*">Avg response time</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($results->sort(array('value.count' => -1)) as $result):?>
                            <tr>
                                <td><?php echo getArticleTitle($result['_id']);?></td>
                                <td><?php echo $result['value']['count'];?></td>
                                <td><?php echo sprintf('%f ms'$result['value']['avg_resp_time']);?></td>
                            </tr>
                        <?php endforeach;?>
                    </tbody>
                </table>
            </div>
        </div>
    </body>
</html>
 
 
 
cs


 



원래 7일간의 데이터를 통하여 얻어온다는 취지의 스크립트입니다만. 실행해보니 $db->command 안의 query 옵션이 이상해서 결과가 올바르게 나오지 않더군요... 그래서 저는 주석처리하고 결과를 출력했습니다.
아 그리고 블로그 포스트의 내용이 짧으면 $start와 $end 타임이 같아서 응답시간이 0이 되버리는 문제도 있습니다.

충분히 길게 포스트 내용을 긁어오시던지 작성해주세요
 




결과 화면은 이렇습니다.
음... 최신 포스팅에 뭔가 문제가 있는 것 같군요... 저것을 제외하고는 이상이 없습니다.










3. 실시간 페이지 방문 카운트 구현하기
여기서는 제가 도메인을 써서 만든 페이지가 아니라 기껏해야 제가 클릭한 것만 페이지 카운팅이 되므로 blogreader_bot을 둬서 페이지 리딩을 시키겠습니다.

먼저 log.php 파일을 열어 메소드를 추가시켜 줍니다.

log.php


1
2
3
4
5
6
7
8
9
10
11
12
public function updateVisitCounter($articleId) {
    // 방문 카운트를 담을 Collection 선택(생성)
    $articleVisitCounterDaily = $this->_dbconnection->getCollection('article_visit_counter_daily');
    $criteria = array(
        'article_id' => new MongoId($articleId),
        'request_date' => new MongoDate(strtotime('today'))
    );
        
    $newObj = array('$inc' => array('count' => 1));
    
    $articleVisitCounterDaily->update($criteria, $newObj, array('upsert' => True));
}
cs

 





다음으로 blog.php 파일을 열어 맨 끝에 아래 코드를 추가해 줍니다. ($logger->logRequest($data); 밑에 넣어주면 되겠죠?)


1
$logger->updateVisitCounter($id);
cs




blogreader_bot.php를 작성합니다.

 * 주의사항: 이 코드의 22번 라인을 보시면 localhost 뭐시껭 되있습니다. 눈치 빠르시다면 아무래도 자신의 포트설정, 디렉토리 경로 설정해주셔야 겠죠?? 저는 chap5/ 라는 디렉토리에 들어있으므로 아래와 같이 작성된겁니다.

blogreader_bot.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
<?php
require('dbconnection.php');
$mongo = DBConnection::instantiate();
$articles = $mongo->getCollection('articles');
$articleIds = array();
 
foreach ($articles->find(array(), array('_id' => TRUE)) as $article) {
array_push($articleIds, (string) $article['_id']);
}
 
function getRandomArrayItem($array) {
$length = count($array);
$randomIndex = mt_rand(0$length - 1);
return $array[$randomIndex];
}
 
echo 'Simulating blog post reading... ';
 
while(1) {
$id = getRandomArrayItem($articleIds);
 
$url = sprintf('http://localhost:88/chap5/blog.php?id=%s'$id);
$curlHandle = curl_init();
curl_setopt($curlHandle, CURLOPT_URL, $url);
curl_setopt($curlHandle, CURLOPT_HEADER, false);
curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true);
curl_exec($curlHandle);
curl_close($curlHandle);
}
?>
cs


마지막으로 작성할 코드는 realtime_pageviews.php입니다.
 
realtime_pageviews.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
<?php
require 'dbconnection.php';
$dbConnection = DBConnection::instantiate();
$collection = $dbConnection->getCollection('article_visit_counter_daily');
 
function getArticleTitle($id) {
global $dbConnection;
$article = $dbConnection->getCollection('articles')->findOne(array('_id' => new MongoId($id)));
 
return $article['title'];
}
 
$obj = $collection->find(array('request_date' => new MongoDate(strtotime('today'))));
 
?>
 
<!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> Daily Page views (in realtime) </title>
        <style type="text/css" media="screen">
body { font-size: 13px; }
div#contentarea { width: 680px; }
</style>
    </head>
    <body>
        <div id="contentarea">
            <div id="innercontentarea">
                <h1> Daily page views (in realtime) </h1>
                <table class="articles" cellspacing="0" cellpadding="0">
                    <thead>
                        <tr>
                            <th>Articles</th>
                            <th>Viewed</th>
                        </tr>
                    </thead>
                    <tbody>
                    <?php foreach($obj->sort(array('count' => -1)) as $ob):?>
                        <tr>
                            <td><?php echo getArticleTitle((string) $ob['article_id']);?></td>
                            <td><?php echo $ob['count'];?></td>
                        </tr>
                    <?php endforeach;?>
                    </tbody>
                </table>
            </div>
        </div>
    </body>
    <script type="text/javascript">
var REFRESH_PERIOD = 5000;
var t = setInterval("location.reload(true);", REFRESH_PERIOD);
</script>
</html>
 
cs





  
여기까지 했다면!  먼저 blogreader_bot.php 파일을 수행합니다!!!! 
이와 동시에 realtime_pageviews.php를 열 준비를 하셔야 합니다.
아예 2개로 분할해서 확인하세요. 브라우저를.

왜냐하면!! 오른쪽 놈이 루프를 돌면서 랜덤으로 방문을 합니다.
그러면서 실시간으로 왼쪽 페이지에서 5초간 refresh하면서 페이지 뷰한 횟수가 바뀝니다.
요래 놓고 오른쪽 실행하면 5초마다 변합니다!!!








자 이것으로 5장 몽고DB를 사용한 웹 분석을 마치겠습니다!! 






다른 카테고리의 글 목록

CSE/MongoDB 카테고리의 포스트를 톺아봅니다
조회수 확인
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 카테고리의 포스트를 톺아봅니다

[MongoDB] 3. 세션관리자 제작

2015.06.13 11:41 - Palpit
조회수 확인
3장 세션관리자 제작 시작하겠습니다!

 이번 장에서 구현할 세션관리자는

  웹사이트를 방문한 방문객의 HTTP 세션을 관리하고 세션 데이터를 저장하기 위해 몽고DB를 사용하는 모듈이다. 세션관리자는 사용자 autheticate, 활동 추적, 활동 인증, 로그 아웃 관리와 같은 기본적이지만 중요한 기능을 담당한다. 여기서는 객체지향형 프로그래밍 원칙을 사용해 모듈을 구현할 것이다.






 1. SessionManager 클래스 구현
 - 세션 저장/인출/처리를 위해 몽고DB에 만들어진 컬렉션을 사용하는 모듈인 SessionManager 클래스를 구현하겠다. 세션을 처리하기 위한 콜백 함수로 이 클래스의 인스턴스 메소드를 session_set_save_handler()로 등록할 것이다. 설계 목표는 다움과 같다.

  * 클래스가 구현한 세션은 Life Time이 1시간
  * 세션은 Timeout이 10분(이 시간동안 비활성이면 세션만료(ex: 자동 로그아웃 같은)) 
  * 세션 데이터는 세션을 표현하는 컬렉션의 다큐먼트 필드로 직렬화


 먼저, dbconnection.php라는 파일을 생성하여 아래 소스 넣는다.

dbconnection.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
<?php
class DBConnection {
const HOST = 'localhost';
const PORT = 27017;
const DBNAME = 'myblogsite';
 
private static $instance;
public $connection;
public $database;
 
private function __construct() {
$connectionString = sprintf('mongodb://%s:%d', DBConnection::HOST, DBConnection::PORT);
 
try {
$this->connection = new Mongo($connectionString);
$this->database = $this->connection->selectDB(DBConnection::DBNAME);
} catch (MongoConnectionException $e) {
throw $e;
}
}
 
static public function instantiate() {
if (!isset(self::$instance)) {
$class = __CLASS__;
self::$instance = new $class;
}
return self::$instance;
}
 
public function getCollection($name) {
return $this->database->selectCollection($name);
}
}
?>
cs


 

위의 DBConnection 클래스를 생성하여, 코드 재사용성을 증대시켰다. instantiate() 메소드를 호출하여 객체를 반환받아서, 
getClollection() 메소드를 호출하여 컬렉션을 선택한다. 
 * 위 DBConnection은 주요 Design Pattern중 Singleton Pattern을 적용하였다. 



다음으로, session.php를 생성, 아래 코드 넣는다.

session.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
<?php
require_once('dbconnection.php');
 
class SessionManager {
// Session을 저장할 Collection 이름
const COLLECTION = 'sessions';
// 10분 동안 비활성인 경우 Session 만료
const SESSION_TIMEOUT = 600;
// 1시간 후 Session 만료
const SESSION_LIFESPAN = 3600;
// Session Cookie Name
const SESSION_NAME = 'mongosessid';
const SESSION_COOKIE_PATH = '/';
/*
Web Application의 Domain Name 지정
ex) mywebapp.com
Local 환경에서 돌리지 않은 경우에는 빈 문자열 사용금지
*/
const SESSION_COOKIE_DOMAIN = '';
 
private $_mongo;
private $_collection;
// 현재 Session 표현
private $_currentSession;
 
public function __construct() {
$this->_mongo = DBConnection::instantiate();
$this->_collection = $this->_mongo->getCollection(SessionManager::COLLECTION);
session_set_save_handler(
array(&$this'open'),
array(&$this'close'),
array(&$this'read'),
array(&$this'write'),
array(&$this'destroy'),
array(&$this'gc')
);
// Session Garbage Collection 기간 설정
ini_set('session.gc_maxlifetime', SessionManager::SESSION_LIFESPAN);
// Session Cookie 구성 설정 
session_set_cookie_params(
SessionManager::SESSION_LIFESPAN,
SessionManager::SESSION_COOKIE_PATH,
SessionManager::SESSION_COOKIE_DOMAIN
);
// Session Name으로 'mongosessid'로 'PHPSESSID'를 대체
session_name(SessionManager::SESSION_NAME);
session_cache_limiter('nocache');
// Session Start
session_start();
}
 
public function open($path$name) {
return true;
}
 
public function close() {
return true;
}
 
public function read($sessionId) {
$query = array
'session_id' => $sessionId,
'timedout_at' => array('$gte' => time()),
'expired_at' => array('$gte' => time() - SessionManager::SESSION_LIFESPAN)
);
$result = $this->_collection->findOne($query);
$this->_currentSession = $result;
 
if (!isset($result['data'])) {
return '';
}
return $result['data'];
}
 
public function write($sessionId$data) {
$expired_at = time() + self::SESSION_TIMEOUT;
$new_obj = array(
'data' => $data,
'timedout_at' => time() + self::SESSION_TIMEOUT,
'expired_at' => (empty($this->_currentSession)) ? time() + SessionManager::SESSION_LIFESPAN : $this->_currentSession['expired_at']
);
 
$query = array('session_id' => $sessionId);
$this->_collection->update(
$query,
array('$set' => $new_obj),
array('upsert' => True)
);
return True;
}
 
public function destroy($sessionId) {
$this->_collection->remove(array('session_id' => $sessionId));
return True;
}
 
public function gc() {
$query = array('expired_at' =>array('$lt' => time()));
$this->_collection->remove($query);
return True;
}
 
public function __destruct() {
session_write_close();
}
}
 
$session = new SessionManager();
?>
cs






 위 코드를 작성하여 Session을 접근한다면, php문법의 require/include를 통하여 접근한다.




2. User 클래스 구현
 테스트용으로 사용자 계정을 여러개 생성한다.
 먼저 몽고DB 내에 사용자 계정을 생성하기 위한 create_users.php를 작성한다.



create_users.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
<?php
require('dbconnection.php');
$mongo = DBConnection::instantiate();
$collection = $mongo->getCollection('users');
 
$users = array(
array(
'name' => 'Luke Skywalker',
'username' => 'jedimaster23',
'password' => md5('usetheforce'),
'birthday' => new MongoDate(strtotime('1971-09-29 00:00:00')),
'address' => array(
'town' => 'Mos Eisley',
'planet' => 'Tatooine'
)
),
 
array(
'name' => 'Leia Organa',
'username' => 'princessleia',
'password' => md5('eviltween'),
'birthday' => new MongoDate(strtotime('1976-10-21 00:00:00')),
'address' => array(
'town' => 'Aldera',
'planet' => 'Alderaan'
)
),
 
array(
'name' => 'Chewbacca',
'username' => 'cewiethegreat',
'password' => md5('loudgrowl'),
'birthday' => new MongoDate(strtotime('1974-05-19 00:00:00')),
'address' => array(
'town' => 'Kachiro',
'planet' => 'Kashyyk'
)
)
);
 
foreach($users as $user) {
try {
$collection->insert($user);
} catch(MongoCursorException $e) {
die($e->getMessage());
}
}
 
echo 'Users created successfully';
?>
cs




생성 후, localhost/create_users.php를 수행하여 사용자 계정 데이터를 생성한다.
다음, user.php를 작성한다.




user.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
<?php
require_once('dbconnection.php');
require_once('session.php');
 
class User {
const COLLECTION = 'users';
// collection은 users 사용
 
private $_mongo;
private $_collection;
private $_user;
 
// User Class Construct
public function __construct() {
// DBConnection으로부터 DB를 받아와 Collection 선택
$this->_mongo = DBConnection::instantiate();
$this->_collection = $this->_mongo->getCollection(User::COLLECTION);
 
// Login 상태면 user data 불러옴
if ($this->isLoggedIn()) $this->_loadData();
}
 
// Login 상태인지 확인
public function isLoggedIn() {
return isset($_SESSION['user_id']);
}
 
// username과 password를 db에서 query하여 authenticate
public function authenticate($username$password) {
$query = array
'username' => $username,
'password' => md5($password)
);
 
$this->_user = $this->_collection->findOne($query);
if (empty($this->_user)) return False;
$_SESSION['user_id'] = (string) $this->_user['_id'];
return True;
}
 
public function logout() {
unset($_SESSION['user_id']);
}
 
public function __get($attr) {
if (empty($this->_user)) 
return Null;
 
switch ($attr) {
case 'address': {
$address = $this->_user['address'];
return sprintf("Town: %s, Planet: %s"$address['town'], $address['planet']);
}
case 'town': {
return $this->_user['address']['town'];
}
case 'planet': {
return $this->_user['address']['planet'];
}
case 'password': {
return NULL;
}
default
return (isset($this->_user[$attr])) ? $this->_user[$attr] : NULL;
}
}
 
private function _loadData() {
$id = new MongoId($_SESSION['user_id']);
$this->_user = $this->_collection->findOne(array('_id' => $id));
}
}
?>
cs



자 마지막 단계만을 남겨둔 상태이다.




3. 로그인, 로그아웃, 프로파일 페이지 구현
 로그인 폼을 생성하고, 인증을 통하여 프로파일 페이지로 넘어가게해서 사용자의 기본 정보를 보이게 한다. 프로파일 페이지 내에 로그아웃 기능을 추가하면 끝이다.




먼저 login.php를 작성하자.

login.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
<?php
$action = (!empty($_POST['login']) && ($_POST['login'] === 'Log In')) ? 'login' : 'show_form';
 
switch ($action) {
case 'login': {
require('session.php');
require('user.php');
 
$user = new User();
$username = $_POST['username'];
$password = $_POST['password'];
 
if ($user->authenticate($username$password)) {
header('location: profile.php');
exit;
else {
$errorMessage = "Username/password did not match";
break;
}
}
 
case 'show_form':
default:
$errorMessage = NULL;
}
?>
 
<!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> User Login </title>
    </head>
    <body>
        <div id="contentarea">
            <div id="innercontentarea">
                <h1> Log in here </h1>
                <div id="login_box">
                    <div class="inner">
                        <form id="login" action="login.php" method="POST" accept-charset="utf-8">
                            <ul>
                                <?php if (isset($errorMessage)):?>
                                    <li><?php echo $errorMessage;?></li>
                                <?php endif;?>
                                <li>
                                    <label> UserName </label>
                                    <input class="textbox" tabindex="1" type="text" name="username" autocomplete="off"/>
                                </li>
                                <li>
                                    <label> Password </label>
                                    <input class="textbox" tabindex="2" type="password" name="password"/>
                                </li>
                                <li>
                                    <input id="login_submit" name="login" tabindex="3" type="submit" value="Log In" />
                                </li>
                                <li class="clear"></li>
                            </ul>
                        </form<!-- End of form login -->
                    </div<!-- End of div inner -->
                </div<!-- End of div login_box -->                
            </div<!--End of div innercontentarea -->
        </div>
    </body>
</html>
cs

 





다음, profile.php

profile.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
<?php
require('session.php');
require('user.php');
$user = new User();
 
if (!$user->isLoggedIn()) {
header('location: login.php');
exit;
}
?>
 
<!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> Welcome <?php echo $user->username;?> </title>
    </head>
    <body>
        <div id="contentarea">
            <div id="innercontentarea">
            <a style="float:right;" href="logout.php"> Log out </a>
                <h1> Hello <?php echo $user->username;?> </h1>
                <ul class="profile_list">
                    <li>
                        <span class="field">Username</span>
                        <span class="value">
                            <?php echo $user->username;?>
                        </span>
                        <div class="clear"></div>
                    </li>
                    <li>
                        <span class="field">Name</span>
                        <span class="value">
                            <?php echo $user->name;?>
                        </span>
                        <div class="clear"></div>
                    </li>
                    <li>
                        <span class="field">Birthday</span>
                        <span class="value">
                            <?php echo date('j F, Y'$user->birthday->sec);?>
                        </span>
                        <div class="clear"></div>
                    </li>
                    <li>
                        <span class="field">Address</span>
                        <span class="value">
                            <?php echo $user->address;?>
                        </span>
                        <div class="clear"></div>
                    </li>
                </ul>
            </div<!--End of div innercontentarea -->
        </div>
    </body>
</html>
cs







마지막으로, logout.php

logout.php
 

 

1
2
3
4
5
6
7
8
9
<?php
require_once('session.php');
require_once('user.php');
$user = new User();
$user->logout();
header('location: login.php');
exit;
 
?>
cs



 

CSS는 첨부되어있는 파일로 적용시킨다.



이제! 웹에서 작성한 웹앱을 확인해보자! localhost/login.php로 접근해보자!
아래와 같이 나오는데, UserName과 password를 아까 생성한 계정중 한개로 입력하자! 그리고 Log In 클릭!!


 









그럼 다음처럼 나온다면 이번장은 클리어 한 것이다!



 




이것으로 세션관리자 제작에 관한 포스팅을 마치겠습니다








다른 카테고리의 글 목록

CSE/MongoDB 카테고리의 포스트를 톺아봅니다
조회수 확인
이어서 계속 가겠습니다. 




4. 블로그 편집기 구현하기



 edit.php파일 생성하여 아래 소스 넣어주세요.

edit.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
<?php
$action = (!empty($_POST['btn_submit']) && ($_POST['btn_submit'] === 'Save')) ? 'save_article' : 'show_form';
 
$id = $_REQUEST['id'];
try {
$mongodb = new Mongo();
$articleCollection = $mongodb->myblogsite->articles;
} catch(MongoConnectionException $e) {
die('Failed to connect to MongoDB '$e->getMessage());
}
 
switch ($action) {
case 'save_article':
$article = array();
$article['title'] = $_POST['title'];
$article['content'] = $_POST['content'];
$article['saved_at'] = new MongoDate();
$articleCollection->update(array('_id' => new MongoId($id)), $article);
break;
 
case 'show_form':
default:
$article = $articleCollection->findOne(array('_id' => new MongoId($id)));
}
?>
 
<!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> Blog Post Editor </title>
    </head>
    <body>
        <div id="contentarea">
            <div id="innercontentarea">
                <h1> Blog Post Creator </h1>
                <?php if ($action === 'show_form'):?>
                    <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
                        <h3> Title </h3>
                        <p><input type="text" name="title" id="title" value="<?php echo $article['title']; ?>" /></p>
                        <h3> Content </h3>
                        <textarea name="content" rows="20">
                            <?php echo $article['content'];?>
                        </textarea>
                        <input type="hidden" name="id" value="<?php echo $article['_id']; ?>" />
                        <p>
                            <input type="submit" name="btn_submit" value="Save" />
                        </p>
                    </form>
                <?php else:?>
                    <p>
                        Article saved. _id: <?php echo $id;?>.
                        <a href="blog.php?id=<?php echo $id; ?>">
                            Read it.
                        </a>
                    </p>
                <?php endif;?>
            </div<!--div innercontentarea End -->
        </div>
    </body>
</html>
cs






그 다음, 이전에 작성한 dashboard.php의 요 부분을 !!! 수정하셔야 합니다. 일단 찾으세요!
dashboard.php

 

1
2
3
4
                            <td class="url">
                                <a href="blog.php?id=<?php echo $article['_id']; ?>"> View </a>
                            </td>
 
cs




이렇게 추가해 주세요!!
dashboard.php

 

1
2
3
4
5
                            <td class="url">
                                <a href="blog.php?id=<?php echo $article['_id']; ?>"> View </a>
                                | <a href="edit.php?id=<?php echo $article['_id']; ?>"> Edit </a>
                            </td>
 
cs

 






그 다음은 웹에서 확인해야겠죠?? dashboard.php에서 보면 아마 edit 버튼이 생겼을 껍니다!
edit 버튼 눌러서 수정이 되는지 확인하세요!





여기서 잠깐!! 짚고넘어갈 연산이 있습니다!
바로 upsert 요놈은 '존재하면 update, 존재하지 않으면 insert'하는 신기방기한 연산입니다.
혹여나, 개발중에 필요한 연산일것같으므로 짚고 넘어갔습니다!

사용방법은 아래와 같습니다.
 $user->update(array('email') => 'alice@wonderland.com'), array('firstname' => 'Alice', 'lastname' => 'Liddell'), array('unsert' => True));

요롷코롷 해주시면 됩니다!!



도전과제!!

 

 blogpost.php는 작성용, edit.php는 갱신용이다. 두 모듈이 하는 작업을 하나로 합친 새 모듈을 만들자. 새 모듈의 동작은 다음과 같다.


 - 기본적으로, 페이지는 사용자가 블로그 포스트를 위한 제목과 내용을 입력하도록 HTML 폼을 보여준다.(이미 클리어)

 - 페이지가 GET 매개변수의 ID를 받으면, HTML 폼에서 아티클의 제목과 내용을 보여준다. 

 - 사용자가 폼에서 Save 버튼을 클릭할 때, 아티클은 DB에 저장된다.(힌틔 save() 나 upsert 기능 사용_

   · 새로운 아티클인 경우: 아티클 삽입

   · 그렇지 않으면: 아티클 갱신

 - 아티클이 갱신될 때, 전체 아티클 객체를 대체하는 대신 아티클의 제목과 내용만 갱신되어야 함. 또한 modified_at라는 새로운 필드에 아티클이 갱신된 마지막 시각을 기록한다. 



그럼 다음으로 넘어가겠습니다.




5. 블로그 포스트 삭제하기


dashboard.php를 열어 수정을 해야합니다!! 그냥 새로 작성하죠;; 편하게ㅋ

dashboard.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
 
<?php
// Mongo DB 연결 부
try {
$mongodb = new Mongo();
$articleCollection = $mongodb->myblogsite->articles;
} catch(MongoConnectionException $e) {
die("Failed to connect to database ".$e->getMessage());
}
 
$currentPage = (isset($_GET['page'])) ? (int$_GET['page'] : 1;
$articlesPerPage = 5;
$skip = ($currentPage - 1) * $articlesPerPage;
 
$cursor = $articleCollection->find(array(), array('title','saved_at'));
$totalArticles = $cursor->count();
$totalPages = (int) ceil($totalArticles / $articlesPerPage);
 
$cursor->sort(array('saved_at'=>-1))->skip($skip)->limit($articlesPerPage);
 
?>
 
<!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"/>
        <style type="text/css" media="screen">
body {font-size: 13px; }
div#contentarea {width: 650px;}
</style>
        <title> DashBoard </title>
        <script type="text/javascript" charset="utf-8">
function confirmDelete(articleId) {
var deleteArticle = confirm('Are you sure you want to delete this article?');
if (deleteArticle) {
window.location.href = 'delete.php?id='+articleId;
}
return;
}
</script>
    </head>
    <body>
        <div id="contentarea">
            <div id="innercontentarea">
                <h1> DashBoard </h1>
                <table class="articles" cellspacing="0" cellpadding="0">
                    <thead>
                        <tr>
                            <th width="55%"> Title </th>
                            <th width="27%"> Created at </th>
                            <th width="*"> Action </th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php
while($cursor->hasNext()):
$article = $cursor->getNext();
?>
                        <tr>
                            <td>
                                <?php echo substr($article['title'], 035). '...';?>
                            </td>
                            <td>
                                <?php print date('g:i a, F j'$article['saved_at']->sec);?>
                            </td>
                            <td class="url">
                                <a href="blog.php?id=<?php echo $article['_id']; ?>"> View </a>
                                | <a href="edit.php?id=<?php echo $article['_id']; ?>"> Edit </a>
                                | <a href="#" onclick="confirmDelete('<?php echo $article['_id']; ?>')"> Delete </a>
                            </td>
                        </tr>
                        <?php endwhile;?>
                    </tbody>
                </table>
            </div<!--div innercontentarea End -->
            <div id="navigation">
                <div class="prev">
                    <?php if($currentPage !== 1):?>
                    <a href="<?php echo $_SERVER['PHP_SELF'].'?page='.($currentPage -1); ?>"> Previous </a>
                    <?php endif;?>
                </div><!--div prev End -->
                
                <div class="page-number">
                    <?php echo $currentPage;?>
                </div<!--div page-number End-->
                
                <div class="next">
                    <?php if ($currentPage !== $totalPages):?>
                    <a href="<?php $_SERVER['PHP_SELF'].'?page='.($currentPage + 1); ?>"> Next </a>
                    <?php endif;?>
                </div<!--div Next End-->
                <br class="clear">
            </div><!--div navigation End-->
        </div>
    </body>
</html>
cs

 

 





다음으로 delete.php를 만들어주세요.

delete.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
<?php
$id = $_GET['id'];
try {
$mongodb = new Mongo();
$articleCollection = $mongodb->myblogsite->articles;
} catch(MongoConnectionException $e) {
die('Failed to connect to MongoDB '$e->getMessage());
}
 
$articleCollection->remove(array('_id' => new MongoId($id)));
?>
 
<!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> Blog Post Creator </title>
    </head>
    <body>
        <div id="contentarea">
            <div id="innercontentarea">
                <h1> Blog Post Creator </h1>
                <p> Article deleted. _id: <? echo $id;?>.
                <a href="dashboard.php"> Go Back to dashboard? </a>
            </div<!--div innercontentarea End -->
        </div>
    </body>
</html>
cs






확인 스크린 샷은 굳이 뽑지 않겠습니다ㅋ 삭제 되니까용...







6. 블로그 포스트에 댓글올리기

마지막으로 댓글입니다!!

blog.php를 아래와 같이 다시 작성해주세요

blog.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
<?php
$id = $_GET['id'];
try {
$connection = new Mongo();
$database = $connection->selectDB('myblogsite');
$collection = $database->selectCollection('articles');
} catch(MongoConnectionException $e) {
die("Failed to connect to database ".$e->getMessage());
}
$article = $collection->findOne(array('_id' => new MongoId($id)));
?>
 
<!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> My Blog Site </title>
    </head>
    <body>
        <div id="contentarea">
            <div id="innercontentarea">
                <h1> My Blogs </h1>
                <h2<?php echo $article['title'];?></h2>
                <p<?php echo $article['content'];?></p>
                <div id="comment_section">
                    <h3> Comments </h3>
                    <?php if (!empty($article['comments'])):?>
                        <h3> comment </h3>
                        <?php foreach($article['comments'as $comment):
echo $comment['name']. ' says...';?>
                        <p<?php echo $comment['comment'];?></p>
                        <span>
                            <?php echo date('g:i a, F j'$comment['posted_at']->sec);?>
                        </span><br /><br /><br />
                    <?php endforeach; endif;?>
                    
                    <h3> Post your comment </h3>
                    <form action="comment.php" method="post">
                        <span class="input-label"> Name </span>
                        <input type="text" name="commenter_name" class="comment-input"/>
                        <br/><br/>
                        <span class="input-label"> E-mail </span>
                        <input type="text" name="commenter_email" class="comment-input"/>
                        <br/><br/>
                        <textarea name="comment" rows="5"></textarea>
                        <br/><br/>
                        <input type="hidden" name="article_id" value="<?php echo $article['_id']; ?>" />
                        <input type="submit" name="btn_submit" value="Save"/>
                    </form>
                </div<!-- End of div comment_section -->
            </div>
        </div>
    </body>
</html>
cs

 




다음으로 comment.php를 작성해주세요
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
$id = $_POST['article_id'];
 
try {
$mongodb = new Mongo();
$collection = $mongodb->myblogsite->articles;
} catch(MongoConnectionException $e) {
die("failed to connect to MongoDB ."$e->getMessage());
}
 
$comments = array
'name' => $_POST['commenter_name'],
'email' => $_POST['commenter_email'],
'comment' => $_POST['comment'],
'posted_at' => new MongoDate()
);
 
$collection->update(array('_id' => new MongoId($id)), array('$push' => array('comments' => $comments)));
header('Location: blog.php?id='.$id);
?>
cs

 





확인을 해보겠습니다!
먼저, blogs.php로 접근해서 목록을 확인하고 Read more를 통해 접근하겠습니다.







아래와 같이 창이 뜨면, 임의로 작성하고 save 눌러보도록 하겠습니다.








Save를 누르니 아래와 같이 댓글이 올라가 있네요!!!








도전과제!!



 

 사용자 이름이 Bob인 사람이 올린 모든 댓글을 얻는 몽고DB 질의를 작성하자!



 






이상으로 블로그 구현 포스팅을 마무리 하겠습니다!! 

 









 


다른 카테고리의 글 목록

CSE/MongoDB 카테고리의 포스트를 톺아봅니다