프로젝트형 IoT 서비스 개발 4회차/3. 게이트웨이 디바이스 제어

[Day49] 2022-04-07(목) MongoDB4 - Aggregation, PyMongo Setting - 김서연 강사님

powerstone 2022. 4. 7. 18:13
728x90

[1] Aggregation

  - Aggregation framework와 mapreduce가 제공

  - RDBMS의 group by의 개념

  - 집계작업이 단순한 경우에 mapreduce보다는 aggregation framework를 사용

  - input으로 사용된 document를 처리하고 처리 결과로 ouput document를 생성하고

    생성된 ouput document를 또 다른 명령어의 input document로 활용

    => 이를 파이프라인(Pipeline)이라 한다.

  - 파이프라인으로 집계를 처리하며 이 처리 명령을 배열로 나타낸다.

  1. Aggregation Operator

    - $match : where절, having절과 같은 개념

    - $group : group by

    - $sort : order by

    - $sum : 총합(총합을 계산할 필드 명시), 총 갯수(1을 정의)

    - $avg : 평균

    - $max : 최댓값

    - $min : 최솟값

  2. 형식

    - db.collection.aggregate( aggregation operator 이용한 명령어 )

    - 여러 개를 사용하는 경우 배열로 표현

    - ([ 

          {$match: {조건...}},

          {$group: { _id: "$집계할 기준이 되는 필드명", 출력될 필드명: {집계함수: "$집계할 필드명이나 값"}}}

      })

  3. Aggregation 기본 예제

    1) 조건 없는 예제

      ① addr별 인원수

        - db.score.aggregate( [ { $group: { myaddr: "$addr", num: {$sum: 1} } } ] )  - X

        - db.score.aggregate( [ { $group: { _id: "$addr", num: {$sum: 1} } } ] )  - O

          => SQL : SELECT addr _id, count(*) num FROM score GROUP BY addr

      ② dept 별 인원수

        - db.score.aggregate( [ { $group: { _id: "$dept", num: { $sum: 1 } } } ] )

      ③ dept 별 java 점수의 평균

        - db.score.aggregate( [ { $group: { _id: "$dept", java_avg: { $avg: "$java" } } } ] )

      ④ addr 별 servlet 점수의 합계

        - db.score.aggregate( [ { $group: { _id: "$addr", servlet_sum: { $sum: "$servlet" } } } ] )

 

    2) 조건 적용 예제 ($match 사용)

      - java가 80점 이상인 사람들이 부서별로 몇 명인지 구하기

        - db.score.aggregate( [

                                         { $match: { java: { $gte: 80 } } },

                                         { $group: { _id: "$dept", num: { $sum: 1 } } }

                                     ] )

        => SQL : SELECT dept _id, count(*) num FROM score WHERE java >= 80 GROUP BY dept

      

      3) 정렬 적용 예제 ($sort 사용)

        - 2)의 예제를 인원수에 대해서 오름차순으로 정렬

          - db.score.aggregate( [

                                           { $match: { java: { $gte: 80 } } },

                                           { $group: { _id: "$dept", num: { $sum: 1 } } },

                                           { $sort: { num: 1 } }

                                       ] )

        => SQL : SELECT dept _id, count(*) num FROM score WHERE java >= 80 GROUP BY dept ORDER BY num ASC

 

    ※ 배열 필드로 조건 사용 QUERY

- 조건 중에 배열 필드가 있는 경우 QUERY 예제

1) favorites 의 city 배열에 서울 또는 인천이 존재하는 모든 document 조회

	db.test.find( { "favorites.city": { $in: [ "서울", "인천" ] } } )

2) favorites 의 city 배열에 서울과 인천이 존재하고, java점수가 90점 이상인 데이터 조회

	db.test.find( { $and: [ { "favorites.city": { $in: [ " 서울", "인천" ] } }, { java: { $gte: 90 } } ] } )

3) 쉬리 영화를 보거나 부서가 인사인 document

	db.test.find( { $or: [ { "favorites.movie": "쉬리" }, { dept: "인사" } ] } )

4) city가 부산 또는 울산이면서 헬로카봇을 본 document 조회

	db.test.find( { $and : [ { "favorites.city" : { $in: [ "부산", "울산" ] } }, { "favorites.movie": "헬로카봇" } ] } )

 

    4) 배열 필드 조건 사용 예제

test 컬렉션을 이용해서 작업하세요

1. dept가 인사인 document의 servlet 평균 구하기

	db.test.aggregate( [
		{ $match: { dept: "인사" } },
		{ $group: { _id: "$dept", servlet_avg: { $avg: "$servlet" } } }
	] )

2. java가 80점이 넘는 사람들의 부서별로 몇 명인지 구하기

	db.test.aggregate( [
		{ $match: { java: { $gte: 80 } } },
		{ $group: { _id: "$dept", num: { $sum: 1 } } }
	] )

3. 2번 결과를 인원수 데이터를 내림차순으로 정렬해 보세요.
  - java점수의 평균도 함께 출력하기

	db.test.aggregate( [
		{ $match: { java: { $gte: 80 } } },
		{ $group: { _id: "$dept", num: { $sum: 1 }, java_avg: { $avg: "$java" } } },
		{ $sort: { num: -1 } }
	] )

4. 앞에서 작업한 결과에 null인 document를 제외하세요
  - 앞에서의 작업 : db.score.aggregate( [ { $group: { _id: "$addr", num: {$sum: 1} } } ] )
  
	db.score.aggregate( [
		{ $match: { addr: { $exists: true } } },
		{ $group: { _id: "$addr", num: {$sum: 1} } }
	] )

5. 다음과 같은 조건을 만족하는 document의 부서별 인원수를 구하세요
    - 서울, 울산에 거주한 경험이 있고 헬로카봇을 본 적이 있다.
    - java 점수가 80점 이상이다.
    - 위 두 개의 조건을 모두 만족해야 한다.

	db.test.aggregate( [
		{ $match: { $and: [
			{ "favorites.city": { $in: [ "서울", "울산" ] } },
			{ "favorites.movie": "헬로카봇" },
			{ java: { $gte: 80 } } ]
		} },
		{ $group : { _id: "$dept", num: { $sum: 1 } } }
	] )

6. 다음과 같은 조건을 만족하는 document 들의 부서별로 java의 평균을 구하세요
   - 인천에 거주한 경험이 있거나 겨울왕국을 본 경험이 있다.
   - java 점수가 90점 이상이다.
   - 위 두 개의 조건은 둘 중에 하나만 만족하면 됩니다.

	db.test.aggregate( [
		{ $match: { $or: [
			{ "favorites.city": "인천" },
			{ "favorites.movie": "겨울왕국" },
			{ java: { $gte: 90 } }
		] } },
		{ $group: { _id: "$dept", java_avg: { $avg: "$java"} } }
	] )

 

 

[2] PyMongo - Python 과 MongoDB 연동

  1. Setting

    1) PyMongo 라이브러리 설치

    2) import pymongo

import pymongo

    3) local host 로 접속을 위한 객체 생성

      - 객체만 생성하는 것으로 접속이 된 상태는 아님

      - 매번 객체를 활용할 때 접속되는 것

# mongodb 서버에 접속해서 작업할 수 있도록 MongoClient 객체 생성
client = pymongo.MongoClient("mongodb://127.0.0.1:27017")
# client = pymongo.MongoClient("mongodb://localhost:27017")

    4) 접속 확인 및 데이터베이스 리스트 확인

# database 접속
print(client.list_database_names())     # db 목록 확인

    5) 외부 접속 허용

      - 문제는 로컬 호스트 말고 외부에서 IP를 통해 들어오는 것이 불가능하므로 이에 대한 셋팅 필요

      ① 기존의 dbpath로 설정된 경로의 폴더를 별도의 폴더(mongodb) 로 이동

      ② mongodb 폴더에 log 폴더 생성

      ③ mongodb 폴더에 텍스트 파일 생성하고 파일명을 .txt까지 지우고 mongod.cfg 로 변경 (cfg는 설정 파일)

mongodb 폴더에는 data폴더, log폴더, mongod.cfg 가 있다.

      ④ mongod.cfg 를 텍스트 파일로 열어서 아래 내용을 입력

        - 여기서 bind_ip=0.0.0.0 이 바로 외부 접속을 허용하는 부분 (기본 셋팅은 127.0.0.1 로 되어 있었을 것)

dbpath=C:\2022_iot\mongodb\data
logpath=C:\2022_iot\mongodb\log\mongod.log
logappend=true
bind_ip=0.0.0.0
port=27017

 

      ⑤ cmd창 > mongod --config C\2022_iot\mongodb\mongod.cfg

이제는 접속할 때, dbpath 명령어가 아닌, config 명렁어를 이용해 설정 파일(cfg파일)로 실행
액세스 허용

      ⑥ log 폴더에 mongod.log 가 생성되며, 실행시켜 보면, 로그가 기록되어 있는 것을 확인할 수 있다.

mongod.log 파일 생성

      ⑥ 이제 python에서 IP를 직접 입력해도 접속 되는 것을 확인할 수 있다. (XXX.XXX.XXX.XXX 에 IP 입력)

import pymongo


# mongodb 서버에 접속해서 작업할 수 있도록 MongoClient 객체 생성
client = pymongo.MongoClient("mongodb://XXX.XXX.XXX.XXX:27017")

# database 접속
print(client.list_database_names())     # db 목록 확인

 

  2. Mongo Shell 에서 사용하던 기능들 사용하기

    1) show dbs

      - client객체.list_database_names( )

print(client.list_database_names())     # db 목록 출력

    2) show collections

      - db객체.list_collection_names( )

db = client.get_database("sample")      # sample db 저장
print(db.list_collection_names())       # sample db 내의 collection 출력

    3) collection에서 document 하나 가져와서 출력

      - 방법1 : db객체.collection명.find_one( )  => 자동완성 기능X

      - 방법2 : collection객체.find_one( )         => 자동완성 기능O

myscore = db["score"]       # sample db 내의 score collection 저장
# sample 데이터베이스의 score 컬렉션에서 데이터 조회하기 -1
result = db.score.find_one()
print(result)
# sample 데이터베이스의 score 컬렉션에서 데이터 조회하기 -2
result2 = myscore.find_one()
print(result2)

    4) 그 외 기능들은 내일 이어서...

  

  3. 번거로운 서버 접속 기능을 class로 정의

MyMongo.py

import pymongo

# MyMongo 클래스를 조건에 맞게 작성하세요
# - MyMongo 생성자 정의하기 : 매개변수는 ip와 port를 전달받아 클래스변수를 초기화할 수 있도록 처리
# - connect 메소드를 정의
#   db명과 collection명을 매개변수로
#   mongodb 서버에 접속
#   db 데이터베이스에 접속
#   collection 컬렉션을 리턴하도록 작성


class MyMongo:
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port

    def connect(self, db_name, collection_name):
        client = pymongo.MongoClient("mongodb://"+self.ip+":"+self.port)
        db = client.get_database(db_name)
        # collection = db[collection_name]
        collection = db.get_collection(collection_name)
        return collection

MyMongoTest.py

from MyMongo import MyMongo

# MyMongoTest python파일을 작성하고 MyMongo 클래스의 connect 메소드를 활용하여
# document 한 개 조회해서 결과를 출력할 수 있도록 작성


my_mongo = MyMongo("XXX.XXX.XXX.XXX", "27017")
score = my_mongo.connect("sample", "score")
result = score.find_one()
print(result)

- 끝 -

 

728x90