지난 1강에 이어 두 번째 강의를 정리해본다. 이번 주제는 관계형 데이터베이스에서 흔한 작업들, 즉 CRUD라고 하는 작업을 MongoDB에서는 어떻게 처리하는가에 대한 내용이다. 내용이 길어 1, 2로 나눈다.

CRUD

MongoDB에서는 CRUD를 다음과 같이 말한다. 이러한 기능은 관계형 DB의 SQL 같은 별도 언어가 아니라 자바스크립트나 자바와 같은 개발 언어의 구문 형태로 API가 제공된다.

Mongo Shell

Mongo Shell은 자바스크립트를 인터랙티브하게 실행할 수 있는 인터프리터다. 윈도에서는 mongo.exe를 실행하면 되며 DB를 지정하지 않으면 test DB에 연결된다. 단축키는 Bash나 Emacs와 비슷하여 예를 들어 위, 아래 화살표로 이전 입력을 불러올 수 있고 ctrl-a(또는 home)키를 입력하면 줄 처음으로 가며 몇 글자 입력 후 탭키를 누르면 자바스크립트 키워드가 자동 완성된다. "help"라고 입력하면 도움말을 볼 수 있다.

BSON

문자열, 부동 소숫점 소수, 배열, 객체, 시간을 표현할 수 있는 이진 형태의 자료 구조다. 사용자는 JSON만을 볼 수 있지만 MongoDB 내부적으로는 DB와의 통신에서 이 형식이 사용된다.

문서 삽입

문서(JavaScript 객체)를 삽입하려면 다음 구문을 사용한다. 이때 변경 불가한(immutable) ObjectId 객체가 _id 필드로 생성되어 추가된다.

db.컬렉션명.insert(문서)

등록한 문서를 확인할 때 다음과 같이 하면 컬렉션 안의 모든 문서를 찾게 된다.

db.컬렉션명.find()

findOne

db.컬렉션명.findOne(조건객체, 조회할필드객체)와 같이 하여 원하는 문서를 찾을 수 있다.

db.people.findOne({name: "Jones"}, {Name: true, _id: false})

이때 필드 객체에서 _id 필드는 기본적으로 결과에 포함시키기 때문에 제외하려면 명시적으로 false 값을 줘야 한다. 다른 필드는 명시적으로 포함해야만 결과에 나타난다.

find

db.컬렉션명.find()는 전체 문서를 찾아내는 메서드다. 인자를 주면 해당 인자를 조건으로 그에 맞는 문서를 찾아낸다.

db.scores.find({type: 'essay', score: 50}, {student: true, _id: false})

그런데 문서가 많은 경우를 대비해 한번에 20개씩만 출력하게 돼 있으며 it라는 명령을 내리면 다음 20개를 볼 수 있다. 또한 find().pretty()와 같이 하면 문서 내의 필드를 가로로 나열해 표시하던 방식에서 세로로 표시하는 방식으로 바뀐다.

$gt, $lt

범위를 지정하는 조건을 줘야 하는 경우, 예를 들어 점수가 50 이상 60 이하인 경우를 찾으려면 다음과 같이 할 수 있다. (조건 순서는 무관)

db.scores.find({ score : { $gte : 50 , $lte : 60 } } );

이러한 조건은 문자열에 대해서도 적용할 수 있는데 자료형이 섞여 있는 경우 원하는 결과를 찾을 수 없을 수 있으므로 스키마 구성시 주의해야 한다. MongoDB는 자바스크립트 환경이므로 UTF-8 로캘로 정렬하고 대소문자를 구별한다.

$regex, $exists, $type

계속해서 find 메서드의 조건으로 정규식이나 존재 여부, 자료형 등을 지정하려면 다음 예시와 같이 한다.

{name: {$regex: 'e$'}} // 정규식 조건
{name: {$exists: true}} // name 값이 존재하는지 여부
{name: {$type: 2}} // 2는 BSON에서 문자열 자료형을 가리키는 값임

$or

여러 조건을 or로 묶으려면 $or: [조건 배열]과 같이 한다. 조건 배열에 괄호가 많이 나타나 익숙해지는 데 시간이 걸릴 수 있는데 mongo 셸에서는 괄호 짝을 강조 표시해주고 구문에 문제가 있으면 결과 대신 …을 표시하므로 도움이 된다.

db.scores.find({$or: [{score: {$lt: 50}}, {score: {$gt: 90}}]})

$and도 있지만 조건 객체의 기본이 and 조건이므로 많이 사용되지는 않는다.

주의) 아래는 JSON 구문상 뒤의 score가 앞의 score를 덮어쓰므로 $lt 조건만 유효하다.

db.scores.find( { score : { $gt : 50 }, score : { $lt : 60 } } );

내부 배열에 대한 질의

MongoDB는 polymorphic하기 때문에 객체 내 배열은 단순 값을 질의할 때와 마찬가지로 검색한다. 아래 예는 tags라는 필드가 배열일 경우다.

db.products.find( { tags : "shiny" } ); // 이 질의는 tags 배열 중 shiny가 있으면 찾아낸다

// 아래 예시와 같은 결과를 찾아낼 수 있다
{ _id : 42 , name : "Whizzy Wiz-o-matic", tags : [ "awesome", "shiny" , "green" ] }
{ _id : 1040 , name : "Snappy Snap-o-lux", tags : "shiny" }

$in, $all

$in: [...] - 지정한 배열 요소 중 하나라도 일치하는 게 있는 문서를 찾는다 $all: [...] - 지정한 배열 요소 모두를 가지고 있는 문서를 찾는다

점 표기법 질의

문서의 필드가 예를 들어 {email: {personal: 'aaa@bbb.com', work: 'ccc@ddd.com'}}와 같은 객체인 경우도 find 메서드에 조건을 지정해 찾을 수 있는데 필드명은 자바스크립트의 점 표기법 규칙에 따라 지정할 수 있다. 예를 들어 {'email.work': 'ccc@ddd.com'}와 같이 필드의 값 객체에 대해 하위 필드를 찾아들어갈 수 있다.

객체의 일부 값을 찾는 경우가 아니라 객체의 값을 통으로 지정해서 찾을 수도 있다. 질의 조건을 객체 값 그대로 지정하는 것이다. 다만 의미상은 똑같은 JSON 객체더라도 필드 순서가 바뀌면 BSON에서 달라지기 때문에 못찾으므로 필드 순서도 그대로 객체 값과 같이 지정해야 한다.

커서

Mongo 셸에서 find() 메서드는 사실 "커서"를 반환하는 것이며 셸은 그 커서를 사용해 한번에 20개까지 화면에 출력한다. 따라서 예를 들어 cur = db.컬렉션.find()와 같이 해서 커서를 받을 수 있으며 (셸에서 이렇게 입력하고 엔터를 누르면 값을 출력하느라 커서를 사용하게 되므로 ; null을 붙여 커서를 사용해버리지 않도록 한다.) hasNext(), next(), sort({필드: 1 또는 -1}), limit(숫자), skip(숫자)와 같은 메서드를 사용할 수 있다. 이때 이러한 메서드는 서버측의 질의를 수정하는 것이지 클라이언트에 반환된 결과를 필터링하는 게 아니므로 서버로부터 클라이언트로 넘어오는 데이터가 달라지는 것이다.

결과 계수

find 메서드 대신 count를 사용해 결과 개수를 셀 수 있다.

데이터베이스 갱신은 통(wholesale) 갱신이다

데이터 갱신은 update 메서드를 사용한다. 인자 두 가지가 필요하다.

  1. 첫번째 인자는 SQL의 where 절과 같은 조건 객체다.
  2. 두번째 인자는 원래의 데이터를 대체할 데이터 객체다.

update는 원래의 문서를 완전히 대체하는 통 갱신 개념이므로 관계형 DB에서 특정 컬럼들을 지정해 갱신하는 것과 다르다. (단, _id 필드는 갱신 안되고 유지됨)

$set, $inc, $unset

update 메서드가 통으로 갱신하지 않게 하려면 $set 연산자를 사용한다.

db.people.update({name: 'Alice'}, {$set: {age: 31}})

위 예시는 name이 Alice인 문서에서 age 값만 변경한다. 한편 $inc를 사용하면 특정 필드 값을 증가시킬 수 있다.

db.people.update({name: 'Bob'}, {$inc: {age: 1}})

$unset을 사용하면 특정 필드를 없앨 수 있다. {$unset: {profession: 1}} 여기서 1은 아무 값이나 와도 상관 없다.