컴퓨터/database

[MongoDB] 몽고DB 후다닥 시작하기 - 2

김크리 2022. 6. 26. 17:41

몽고DB의 데이터형

몽고DB는 도큐먼트의 값으로 다양한 데이터형을 지원한다.

- 기본 데이터형

  - null

  - boolean

  - 숫자

  - 문자열

  - 날짜 : 1970년 1월 1일부터 시간을 1/1000초 단위로 나타내는 64비트 정수로 날짜를 지정한다. 표준시간대는 저장하지 않는다.

  - 정규 표현식 : 자바스크립트의 정규표현식 문법을 사용할 수 있다.

{"x" : /foobar/i}

  - 배열 : 값의 셋이나 리스트를 표현할 수 있다.

  - 내장 도큐먼트

  - 객체 ID : 객체 ID는 도큐먼트용 12바이트 ID이다.

  - 이진 데이터 : UTF-8이 아닌 문자열을 저장하는 유일한 방법이다. 임의의 바이트 문자열이다. 셸에서는 조작이 불가능하다.

  - 코드 : 임의의 자바스크립트 코드를 포함할 수 있다.

- 날짜

  - 자바스크립트의 Date 클래스 사용

- 내장 도큐먼트

  - 도큐먼트는 키에 대한 값이 될 수 있는데, 이를 내장 도큐먼트라고 한다.

  - 내장 도큐먼트를 사용하여 데이터를 키/값 쌍의 평면적인 구조에서 자연스러운 입체적 구조로 만들 수 있다.

- _id 와 ObjectId

  - 몽고 DB의 저장된 모든 도큐먼트는 _id키를 가지게 된다.

  - _id 키값는 데이터 형에 상관 없지만 기본적으로 ObjectId 데이터 형을 갖게된다.

  - _id 값은 같은 컬렉션 내의 모든 도큐먼트가 고유하게 식별되게 한다.(일종의 unique key)

  - ObjectId클래스는 가벼우면서 여러 장비에 전역적으로 고유하게(유일하게) 생성하기 쉽게 설계되었다.

  - ObjectId는 1초 내의 단일 프로세스의 유일성을 보장한다.

  - _id는 _id키를 명시하지 않으면 자동으로 추가된다 이에 대해서 몽고DB서버에서 관리할 수 있지만 일반적으로 클라이언트 쪽 드라이버에서 관리한다.

 

몽고DB의 셸 활용하기

# 다른 장비나 포트에 mongod 연결하기 - 1
> mongo some-host:30000/myDB

# 다른 장비나 포트에 mongod 연결하기 - 2
> mongo --nodb
> conn = new Mongo("some-host:30000")
> db = conn.getDB("myDB")

# 도움말
> help

# 셀에서 스크립트 실행하기
> mongo script1.js script2.js script3.js

- 셀 조보자와 대응하는 자바스크립트 용법/함수가 존재한다.

- 자주 로드하는 자바스크립트를 .mongorc.js파일에 넣어 사용할 수 있다.

- 몽고DB는 데이터베이스를 쉽게 사용하기 위해서 CRUD API를 제공한다. 

 

도큐먼트 생성

- 몽고DB에 도큐먼트를 삽입하려면 컬렉션의 insertOne, insertMany 메서드를 사용해야한다.

- insertOne은 단일 삽입, insertMany는 다건 삽입을 지원한다.

- 몽고DB 3.0 이전에는 insert 메소드를 사용하여 주로 도큐먼트를 생성하였으나, 몽고DB3.0부터는 insertOne, insertMany 가 생기며 CRUD API를 일관되게 사용하는 것을 목적으로 한다. insert 메소드는 여전히 지원하지만 지향하도록 하자.

# 데이터 1개 삽입
> db.${컬렉션명}.insertOne(${삽입 도큐먼트 내용})

# 배열값을 삽입
> db.${컬렉션명}.insertMan([${삽입 도큐먼트 내용1}, ${삽입 도튜먼트 내용2},...])

- 몽고DB는 삽입 유효성을 검사한다.

  - 하나의 도큐먼트는 16MB 보다 작아야한다.

  - 일괄 삽입에 대한 크기는 1회에 48MB만 지원하며, 이보다 큰 삽입일 경우 일괄 삽입을 여러 개로 분할하여 삽입한다.

  - "_id"필드가 존재하지 않는다면 생성한다.

  - UTF-8이 아닌 문자열을 사용하는지, 인식할 수 없는 데이터 형을 포함하는 지 확인한다.

- insertMany는 삽입의 성능을 개선하기 위해 정렬된 삽입(ordered insert), 정렬되지 않은 삽입(unordered insert)로 나뉜다.

- 기본 insertMany의 삽입 순서는 unordered insert 이다.

- insertMany 메소드를 사용하여 도큐먼트를 삽입 할 경우, "_id"값이 유니크하지 않은 경우, unordered insert 는 중복 등록된 "_id"를 가진 값만 예외적으로 삽입이 되지 않는다.

- ordered insert의 경우, 중복 등록된 "_id" 발생 시점부터 등록이 되지 않는다.

# unordered insert = default
> db.${콜렉션}.insertMany([${데이터},${데이터2},...],{"ordered":false})

# ordered insert
> db.${콜렉션}.insertMany([${데이터},${데이터2},...],{"ordered":true})

도큐먼트 삭제

- deleteOne, deleteMany를 사용하여 도큐먼트를 삭제할 수 있다. 첫번째 매개변수는 돜먼트이다.

- deleteOne은 작성한 매개변수의 값과 일치하는 첫번째 도큐먼트를 삭제한다

- deleteMany는 작성한 매개변수의 값과 일치하는 모든 도큐먼트를 삭제한다.

# 단일 삭제
> db.movies.deleteOne({"_id":1})

# 다건 삭제 - 임시 데이터 삽입
> db.movies.insertMany([{"_id":10,"title":"movie1", "year":1999}
> ,{"_id":11,"title":"movie2", "year":1998}
> ,{"_id":12,"title":"movie3","year":1998}])

# year에 맞는 모든 데이터 삭제
> db.movies.deleteMany({"year":1998})

# 컬렉션의 모든 메소드 삭제
> db.movies.drop()

- deleteOne의 필터와 일치하는 첫 번째 도큐먼트를 삭제한다는 규칙은 아래와 같은 규칙에 따라 요인이 달라진다.

  - 도큐먼트가 삽입된 순서

  - 도큐먼트에 발생한 갱신(일부 스토리지 엔진의 경우, 순서 변경의 요인이된다.)

  - 인덱스 설정

- 몽고DB 3.0 이전에서는 도큐먼트를 삭제하는데 remove를 주로 사용하였다. 이후 해당 메소드는 호환성을 위해 지원되지만, deleteOne, deleteMany의 메소드를 사용하는 것을 지향하자

 

- drop 메소드의 경우, deleteMany를 사용하여 컬렉션의 모든 도큐먼트를 삭제한다.

- drop, delete를 사용하여 삭제된 도큐먼트를 복구하는 방법은 없다.

 

도큐먼트 치환/갱신, 그리고 연산자

- replaceOne을 통해 하나의 도큐먼트를 새로운 도큐먼트로 치환할 수 있다.

- 치환시 실수로 중복된 "_id"값을 갖는 도큐먼트를 생성하게 하는 경우, 데이터베이스는 오류를 반환하고 아무것도 변경하지 않는다.

# 도큐먼트 치환
# 자바스크립트 변수에 도큐먼트 가져오기
> var movie = db.movies.findOne({"title":"E.T."})

# 가져온 도큐먼트 값 확인
> movie

# 도큐먼트 변경
> movie.comment = "Good Movie!"

# 변경된 도큐먼트 값을 기존 콜렉션의 도큐먼트에 치환
> db.movies.replaceOne({"title":"E.T."}, movie)

- updateOne을 통해 도큐먼트의 값을 치환하는 것이 아닌, 특정부분만 갱신할 수 있다.

- 특히 페이지 view count를 증가할때 갱신 연산자(update operator)를 사용할 수 있다.

  - 갱신연산자는 키를 변경,추가, 제거 등 복잡한 갱신 연산을 지정할 수 있는 특수키이다

  - "$inc" 제한자(modifier) 를 통해 update 요청이 들어올 때마다 키 값을 증가 시킬 수 있다. 해당 연산자는 숫자를 증감하기위해서 설계되었다.

# 도큐먼트 갱신, $inc 제한자 사용
# pageviews가 도큐먼트의 항목으로 생성되고, 값이 1임을 확인할 수 있다. 
> db.movies.updateOne({"title":"E.T."}, {"$inc":{"pageviews":1}})

# pageviews의 값이 +1 된 것을 확인 할 수 있다.
> db.movies.updateOne({"title":"E.T."}, {"$inc":{"pageviews":1}})

# pageviews의 값이 +2 된 것을 확인할 수 있다.
> db.movies.updateOne({"title":"E.T."}, {"$inc":{"pageviews":2}})

  - "$set" 제한자를 사용하여 필드 값을 설정할 수 있다. 해당 제한자로는 사용자 정의 키(user-defined key)를 추가할 때 편리하다.

  - "$unset" 제한자를 사용하여 필드의 키와 값을 모두 제거할 수 있다.

# 도큐먼트 갱신
# $set 연산자를 사용하여 변경, 추가
> db.${컬렉션명}.updateOne(${찾고자하는 도큐먼트 필터값}, {"$set":${해당 도큐먼트에 변경, 추가하고자 하는 값}})

# $unset 연산자를 사용하여 키와 값 삭제
> db.${컬렉션명}.updateOne(${찾고자하는 도큐먼트 필터값}, {"$unset":{${변경하고자하는 키값}:1}})

- 배열을 다루는데 갱신 연산자를 사용할 수 있다. $push, $slice, $sort, $addToSet, $ne, $each, $pull, $pop, $setInsertOn등의 연산자가 있다.

  - $push 연산자는 배열이 존재한다면 배열 끝에 요소를 추가하고 (append), 존재하지 않는다면 새로운 배열을 생성한다.

  - $each 연산자를 이용하여 단순한 반복작업을 진행 할 수 있다.

  - $slice 연산자와 $push 연산자를 결합하여 배열을 특정 크기로 늘릴 수 있다. 배열의 크기를 유지할 수 있다. 설정한 값 이상 추가될 경우, 과거 등록된 값은 사라진다. 도큐먼트 내의 큐를 생성하는데 적합하다.

  - $sort 제한자를 통해 정렬을 할 수 있다. $push 사용에서도 진행할 수 있다.

  - $ne 연산자를 사용하여 배열(array)을 집합(set)으로 사용할 수 있다.

  - $addToSet 연산자를 사용하여 $ne연산자처럼 사용할 수 있다. $ne가 작동하지 않을 때 상세히 확인할 수 있다. 또한, 중복을 피할 수 있다.

  - $pull 연산자를 사용하여 도큐먼트에서 일치하는 요소를 모두 제거할 수 있다.

  - $pop 연산자를 이용하여 배열의 양 끝의 요소를 삭제 할 수 있다.

  - $setInsertOn 연산자를 이용하여 도큐먼트가 삽입될 때 필드 값을 설정할 수 있다.(createAt 등)

MongoDB Enterprise > db.movies.updateOne({"_id":4}, {"$push":{"reader":"Park"}});
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
MongoDB Enterprise > db.movies.findOne({"_id":4})
{
	"_id" : 4,
	"title" : "1Ghost busters",
	"views" : 4,
	"reader" : [
		"John",		"Park"
	]
}
MongoDB Enterprise > 
MongoDB Enterprise > 
MongoDB Enterprise > db.movies.updateOne({"_id":4},
... {"$push":{"reader
uncaught exception: SyntaxError: "" literal not terminated before end of script :
@(shell):2:17
MongoDB Enterprise > db.movies.updateOne({"_id":4}, {"$push":{"reader":
... {"$each":["Lim","Lee","Kim"]}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
MongoDB Enterprise > 
MongoDB Enterprise > 
MongoDB Enterprise > db.movies.findOne({"_id":4})
{
	"_id" : 4,
	"title" : "1Ghost busters",
	"views" : 4,
	"reader" : [
		"John",		"Park",		"Lim",
		"Lee",		"Kim"
	]
}
MongoDB Enterprise > 
MongoDB Enterprise > db.movies.updateOne({"_id":4}
... ,{"$push":{"reader":{"$each":["a","b","c"],
... "$slice": -10}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
MongoDB Enterprise > 
MongoDB Enterprise > db.movies.findOne({"_id":4})
{
	"_id" : 4,
	"title" : "1Ghost busters",
	"views" : 4,
	"reader" : [
		"John",		"Park",		"Lim",		"Lee",		"Kim",
		"a",		"b",		"c"
	]
}
MongoDB Enterprise > db.movies.updateOne({"_id":4} ,{"$push":{"reader":{"$each":["a1","b2","c3"], "$slice": -10}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
MongoDB Enterprise > db.movies.findOne({"_id":4})
{
	"_id" : 4,
	"title" : "1Ghost busters",
	"views" : 4,
	"reader" : [
		"Park,		"Lim",		"Lee",		"Kim",		"a",		"b",		"c",
		"a1",		"b2",		"c3"
	]
}
MongoDB Enterprise > db.movies.updateOne({"_id":4},{"$pop":{"reader":-1}})

- 배열의 위치 기반으로 일부의 데이터를 조작하는 방법은 위치를 이용하거나 위치 연산자($)를 사용한다.

  - 몽고DB에서 배열의 인덱스 기준은 0 이다.

- 몽고DB 3.6에서는 개별 배열 요소를 갱신하기 위해서 배열 필터인 arrayFilters를 도입하여 특정 조건에 맞는 배열 요소를 갱신할 수 있다.

# 예시 도큐먼트
# { "_id" : 1,
#	 "array":[
#		{"name":"A1", "count":0},
#		{"name":"A2", "count":2},
#		{"name":"A3", "count":-7},
#		{"name":"C4", "count":4}
#	]
#}

# 위치 값으로 값 변경
# name: A1 이 있는 배열의 count 값이 추가된다.
> db.${컬렉션명}.updateOne({"_id":1}, {"$inc":{"array.0.count" : 1}})

# 위치 연산자($)로 값 변경
# name: A2 로 배열의 값을 찾고, 해당 배열 값을 갱신한다. A2->C2
> db.${컬렉션명}.updateOne({"array.name":"A2"}, {"$set":{"array.$.name" : "C2"}})

# 배열 필터인 arrayFilters를 이용하여 특정 조건의 배열 요소를 갱신할 수 있다.
# 각 요소에 대한 식별자로 elem을 설정하고($[elem]), votes 값이 1 이하인 값에 "hidden: true 라는 키와 값을 넣는다.
> db.${컬렉션명}.updateOne({"_id":1}
> , {$set: {"array.$[elem].hidden": true}}
> , {addFilteres:[{"elem.votes":{$lte: 2}}]
> })

- 몽고DB의 update에서 첫번째 매개변수는 변경을 위한 도큐먼트 필터, 두번째는 조작내용(연산자 사용, 자바스크립트 변수 사용 등)이다. 세번째 매개변수는 옵션 도큐먼트로 갱신 입력을 지정한다.

# 갱신입력시 특정 도큐먼트를 입력하며 동시에 갱신입력을 진행한다.
# blog에 도큐먼트 생성 및 rep값은 처음에 25로 생성된 후에, +3 이 되어 조회시 rep:28이 된다.
> db.blog.updateOne({"rep":25},{"$inc":{"rep":3}},{"upsert":true})

# 확인가능
> db.blog.findOne({"rep":28})
# 없음
> db.blog.findOne({"rep":25})

- updateMany를 통해 다중 도큐먼트를 갱신 할 수 있다.

  - 스키마가 공통적으로 변경되거나, 특정 사용자에게 정보를 추가할때 적절하다.

# 필터에 적용하는 모든 값 갱신
> db.${콜렉션 명}.updateMany(${변경하고자 하는 값 필터}, {"$set":${변경 내용}})

- 수정된 도큐먼트를 반환하는데 몽고DB 3.2 이하의 버전에서는 findAndModify를 사용하였다. 하지만 해당 메소드는 삭제, 대체, 갱신의 작업을 결합한 메소드이기 때문에 높은 이해도가 없다면 사용자 오류가 발생하기 쉽다.

- 몽고DB 3.2 이상 버전부터는 findOneAndDelete, findOneAndReplace, findOneAndUpdate를 사용하여 갱신한 도큐먼트를 반환받을 수 있다.

- 몽고 DB 4.2 집계 파이프라인을 위해 findOneAndUpdate를 확장하였다. 

  - $addFields($set), $project($unset), $replaceRoot($replaceWith)로 구성될 수 있다.

 

참고

도서 - 몽고DB 완벽 가이드 3판