Categories
복습 - Mongo DB
엘리스 커리큘럼 과정인 개인 프로젝트 주제를 고민 중이다. 오늘 처음 아이디어가 생각났는데 우선 배우는 용도로 간단하게 틀만 만들어보고, 이후에 가능성이 있으면 계속해서 이어나가볼까라는 생각도 했다. 하지만 과연 내가 생각한 그림이 현실적으로 실현 가능한지에 대해서 지속적으로 따져보고 있고, 여러 위험사항들이 생각나서 해결방법을 고민 중에 있다.
시간을 다음과 같이 투자하려고 한다.
실현 가능성 탐구 및 조정 (50%) -> 틀 계획 (30%) -> 코딩 (20%)
오늘은 여태껏 배운 과정 중에 가장 재미있게 배웠다고 생각하는 Mongo DB에 대해 포스팅하도록 하겠다.
Mongo DB
이 글은 엘리스 커리큘럼의 내용을 요약하여 정리하였고, 내용이 객관적인 사실과 다를 수 있음을 알려드립니다.
Mongo DB는 NoSQL에 속한다. NoSQL의 경우 다음 세 가지의 특징을 가진다.
- 질의 명령어가 SQL이 아님
- 정보의 형식을 미리 정하지 않음
- _id 값이 자동 저장되어 있음
질의 명령어가 SQL이 아니고 정보의 형식을 미리 정하지 않는다는 유연성 때문에 MongoDB는 덜 제한적인 일관성 모델을 제공하는 데이터베이스이다.
MongoDB 탄생 배경
SQL 문 처럼 정해진 규칙에 따라 정보를 처리하면 정보 처리량이 너무 많을 때 효율성이 떨어졌다. 예를 들어 임시 정보를 굳이 안정적으로 저장할 필요가 없었다.
또한 현대에 들어서 처리할 데이터양이 점점 많아짐에 따라 분산 컴퓨팅 기술을 쓰는 곳이 증가하였다.
Mongo DB는 위에서 작성한 니즈를 한마디로 갖췄다고 보면 된다.
즉 정해진 규칙이 가져다 주는 안정성을 포기하면서 확장성과 성능 최적화에 특화시켰다.
또한 복제와 샤딩을 지원하여 분산컴퓨팅을 기본값으로 깔았다.
분산 컴퓨팅 기본값 설정의 문제점은, 입문이 너무 어려웠다는 것인데,
Mongo DB는 JSON과 유사한 BSON 구조로 정보를 저장하게 하고, 자바스크립트 언어로 보이게 함으로써(C언어로 짜여졌다.) 기존 웹 개발자들의 유입이 쉽도록 하였다.
따라서 Mongo DB는 다음과 같은 상황에서 사용하기 적합하다.
- 프로젝트 언어를 자바스크립트로 통일하고플때
- 안정성을 포기할 만큼 높은 성능을 필요로 할 때
- 저장할 정보의 형태가 자주 변경되는 경우
- 비 정형화된 정보를 전산화할 때 (수기 작성 데이터, 로그 데이터 등)
하지만 Mongo DB를 사용할 때 MySQL과 달리 데이터가 의도치 않게 중간 상태로 저장된다는 안정성 문제가 있기 때문에, 다음과 같은 상황에서는 쓰지 않는게 좋다.
- 결제나 예약 시스템처럼 높은 성능보다 안정성, 무결성이 중요한 경우
- 복잡한 쿼리가 빈번한 경우 (연산 처리량 상대적으로 높아짐)
Mongo DB의 구조
맨 위에는 데이터베이스-, 중간에는 테이블로 이해할 수 있는 컬렉션, 맨 아래에는 하나의 행으로 볼 수 있는 도큐먼트의 3단계 구조이다.
아래 코드를 참고하여 3단계 구조의 생성과 조회하는 방법을 배워보자.
# mongoDB를 사용할 수 있게 도와주는 파이썬 모듈
import pymongo
# url (내 pc내부(localhost) + Mongo DB 기본 포트)
connection = pymongo.MongoClient("mongodb://localhost:27017/")
# 접속할 데이터베이스로 접근
# 만약 데이터베이스가 없으면 자동으로 생성한 후 접속
db = connection.get_database("testDB")
# 컬렉션에 접근
# 컬렉션이 없을 때 자동으로 생성됨
collection = db.get_collection("testCollection")
# 컬렉션에 도큐먼트 저장
collection.insert_one({"hello":"world"})
# 데이터베이스 목록 조회
print(connection.list_database_names())
# 컬렉션 목록 조회
print(db.list_collection_names())
# pprint로 도큐먼트 목록 조회
pprint(list(collection.find()))
데이터 타입으로는 bson을 사용하는데,
bson에는 기본 데이터 타입 말고도 ObjectId라는 데이터 타입이 있으며 식별값으로 사용된다.
샤딩이나 복제로 인한 데이터베이스 구별의 혼동을 막기 위한 프라이머리 키이다.
사용법은 아래 코드를 참고하자.
# python에서는 다음과 같이 BSON을 사용함
# python에서는 자료형을 하나하나 임포트 해주어야 하는 경우가 있음
from bson import ObjectId
from datetime import datetime
collection.insert_one({
"_id" : ObjectId("54c21354ef5132c54d")
"name" : {"first": "sue", "last": "Turing"},
"age" : 26,
"is_alive": True,
"ViewTime" : dateTime(2017,10,24,5.2.46)
})
도큐먼트 생성
컬렉션에 도큐먼트를 삽입하고 싶으면
컬렉션.insert_one(도큐먼트)
를 쓰거나 여러 개를 삽입하고 싶으면
컬렉션.insert_many([도큐먼트1], [도큐먼트2])
를 쓰면 된다.
이때
URL = 결과컬렉션.inserted_id
를 써서 해당 게시글 URL을 접속할 때 고유 id를 이용할 수 있다.
한편 도큐먼트를 검색하고 싶을 때는 아래 코드를 참고하자.
result = 컬렉션.find({query}})
# 리스트로 내용물을 한번에 불러오고 하나씩 출력
print(list(result))
for documnet in result:
print(document)
여기서 쿼리를 쓸 때는
query = {"_id" : False}
result = 컬렉션.find({query})
와 같이 쓴다. SQL의 SELECT와 기능이 비슷하다.
도큐먼트 수정
도큐먼트를 수정하고 싶을 때는 아래 코드를 참고하자.
#one은 하나만, many는 여러개를 수정
result = collection.update_one(
{query}, # query로 검색
{update}, # 변경할 사항을 적음
# True 시 결과가 없어도 해당 도큐먼트를 생성해서 위 값을 자동으로 설정해줌
upsert : Boolean
)
print(result.matched_count) # 찾은 도큐먼트 수
print(result.modified_count) # 변경된 도큐먼트 수
inventory.update_one(
{"item" : "canvas"} # 쿼리
{"$set" : {"qty" : 10}} # 해당 필드만 수정
{"$unset" : {"qty" : False}} # qty 필드를 없앰
)
도큐먼트 삭제
도큐먼트를 삭제하고 싶을 때는
# 쿼리로 검색하고 첫 번째 도큐먼트를 삭제
result = collection.delete_one({query})
를 쓰면 된다.
쿼리 연산자
쿼리 연산자는 다음과 같이 점 표기법, 비교 연산자, 논리 연산자, 문자열 연산자, 배열 연산자를 이용한다.
사용하게 될 쿼리와 연산자는 SQL과 Mongo DB간에 많이 다르다.
# SQL
SELECT * FROM table WHERE column = "value"
# Mongo DB
collection.find({"field":"value"}, {})
P 모든 연산자는 안쪽에서 쓰이지만, 예외적으로 $or, $and, $nor 세 개의 연산자는 가장 바깥에 쓰인다.
점 표기법
BSON 내부의 Object에 접근하기 위한 방법이다.
"name" : {"first" : "sue", "last" : "Turing"}
# 점 연산자로 내부에 접근할 수 있다.
{"name.first" : 'sue'}
"groups" : {"news", "sports"}
# 점 연산자로 배열의 요소를 찾을 수 있다.
{"groups.0": "news"}
비교 연산자
$eq (equals)
$gt (greater than)
$gte (greater than or equals)
$lt (less than)
$lte (less than or equals)
$ne (not equal)
$in
$nin
# 좋아요 수가 10초과 30미만인 도큐먼트 검색
articles.find({"likes": {"$gt":10, "$lt" : 30}})
# 수량이 5 또는 15인 아이템 도큐먼트 검색
inventory.find({"qty" : {"$in" : [5, 15]}})
논리 연산자
$or
$and
$nor : 주어진 조건 중 하나라도 false일 때 true
$not : 주어진 조건이 false일 때 true
# 게시물 중 제목이 aritlcle01이거나 작가가 Alpha인 도큐먼트
articles.find({"$or":
[{"title" : "article01"},
{"writer" : "Alpha"}]
}
)
# 좋아요 수가 11 이하가 아닌 도큐먼트
articles.find({"likes" :
{"$not" :
{"$lte" : 11}}
})
문자열 연산자
$regex : 특정 정규 표현식과 맞는 문자열을 매칭
{<field> : {"$regex" : 'pattern', "$options": '<options>'}}
# i = 대소문자 무시
# m = 정규식에서 anchor(^)사용시 값에 \n 있다면 무력화
# x = 정규식 안에 있는 whitespace를 모두 무시
# s = dot(.) 사용 할 때 \n을 포함해서 매치
$text : 문자열 검색의 기능을 수행
"$text" : {"$search" : <string>, # 검색할 내용
"$language" : <string>, # 선택적 검색하는 언어
"$caseSensitive" : <boolean>, # 선택적. False 일 경우 대소문자 무시
"$diacriticSensitive" : <boolean> # 선택wjr. p hat과 같이 위 문자를 구분할지 선택
}
이때 문자열 인덱스를 사전에 사용 해야만 text를 사용 가능하다는 점을 꼭 기억해두자.
배열 연산자
$all : 순서와 상관없이 배열 안의 요소가 모두 포함되면 선택 $elemMatch : 이 연산자에 작성된 조건과 맞는 배열 속 요소를 가진 도큐먼트를 선택
survey.find(
{"results" : {"$elemMatch" : {"product" : "xyz", "score" : {"$gte": 8}}}}
)
$size: 해당 배열의 크기가 같은 도큐먼트를 선택
scores.find({results: {"$size":3}})
다음 복습 포스팅으로는 flask에 대해 전체적으로 다뤄보도록 하겠다.
:)