[MongoDB] Ch3 - 도큐먼트 생성, 갱신, 삭제
💡
컬렉션에 새 도큐먼트 추가하기
컬렉션에 새 도큐먼트 삭제하기
기존 도큐먼트 갱신하기
연산을 수행할 때 안전성과 속도 중 맞는 수준 선택하기
1. 도큐먼트 삽입
db.movies.insertOne({"title" : "test"})
insertMany
여러 도큐먼트를 컬렉션에 삽입할 때 insertMany를 사용합니다
각 도큐먼트에 대해 데이터베이스로 왕복하지 않고 도큐먼트를 대량 삽입하므로 훨씬 효율적입니다
수십, 수백, 수천 개의 도큐먼트를 한 번에 전송하면 도큐먼트 삽입이 매우 빨라집니다
삽입 유효성 검사
몽고DB는 삽입된 데이터에 최소한의 검사를 수행합니다
"_id" 필드가 존재하지 않으면 새로 추가하고, 모든 도큐먼트는 16메가바이트보다 작아야 하므로 크기를 검사합니다
(톨스토이 소설 '전쟁과 평화'는 3.14 메가바이트에 불과하다)
2. 도큐먼트 삭제
CRUD API는 deleteOne과 deleteMany를 제공합니다
3.0 이전엔 remove를 주로 사용했지만, deleteOne, deleteMany를 사용합시다
(remove는 호환성을 위해 지워지지 않음)
drop
deleteMany를 사용해 컬렉션의 모든 도큐먼트를 제거합니다
일반적으로 도큐먼트를 제거하는 작업은 꽤 빠르지만, 전체 컬렉션을 삭제하려면 다음과 같이 drop을 사용하는 편이 더 빠릅니다
db.movies.drop()
3. 도큐먼트 갱신
updateOne, updateMany, replaceOne과 같은 갱신 메서드를 사용해 변경할 수 있습니다
갱신은 원자적으로 이뤄집니다
요청 2개가 동시에 발생하면, 서버에 먼저 도착한 요청이 적용된 후 다음 요청이 적용됩니다
기본 동작을 원치 않으면 도큐먼트 버저닝 패턴을 고려해봅시다
도큐먼트 치환
replaceOne은 도큐먼트를 새로운 것으로 완전히 치환합니다
이는 스키마 마이그레이션에 유용합니다
joe = db.people.findOne({"name" : "joe", "age" : 20});
joe.age++
db.people.replaceOne({"name":"joe"}, joe);
갱신 연산자
일반적으로 도큐먼트의 특정 부분만 갱신하는 경우가 많습니다
갱신 연산자는 키를 변경, 추가, 제거하고, 심지어 배열과 내장 도큐먼트를 도작하는 복잡한 갱신 연산을 지정하는 데 사용하는 특수키입니다
연산자를 사용할 때, _id 값은 변경할 수 없습니다
(변경하려면 도큐먼트 전체를 치환합니다)
"$set" 제한자 사용하기
스키마를 갱신하거나 사용자 정의 키를 추가할 때 편리합니다
데이터형도 변경할 수 있습니다
$unset으로 키와 값을 제거할 수도 있습니다
💡 키를 추가, 변경, 삭제할 때는 항상 $ 제한자를 사용해야합니다
초보가 흔히 범하는 오류) db.blog.posts.updateOne({"author.name" : joe"}, ... {"author.name" : "joe schmoe"});
갱신하다가 키 값을 다른 값으로 바꾸는 경우입니다
이럴 때는 오류가 생깁니다
갱신 도큐먼트는 갱신 연산자를 포함해야 합니다
증가와 감소
"$inc" 연산자는 이미 존재하는 키의 값을 변경하거나 새 키를 생성하는데 사용합니다
분석, 투표 등과 같이 자주 변하는 수치 값을 갱신하는 데 매우 유용합니다
$inc는 $set과 비슷하지만 숫자를 증감하기 위해 설계됐습니다
$inc는 int, long, double, decimal 타입 값에만 사용할 수 있습니다
배열 연산자
배열을 다루는 데 갱신 연산자를 사용할 수 있습니다
리스트에 대한 인덱스를 지정할 수 있을 뿐만 아니라 셋처럼 이중으로 쓸 수 있습니다
- 요소 추가하기
- $push는 배열이 이미 존재하면 배열 끝에 요소를 추가하고, 존재하지 않으면 새로운 배열을 생성합니다
- $each는 작업 한번으로 값을 여러 개 추가할 수 있습니다
- db.stock.ticker.updateOne({"_id" : "GOOG"}, ... { "$push" : { "hourly" : { "$each" : [562, 777, 711]}}})
- 배열을 특정 길이로 늘이려면 $slice를 $push와 결합하여 사용합니다 (배열이 특정 크기 이상으로 늘어나지 않게 하고 효과적으로 'top N' 목록을 만들 수 있습니다')
- $slice는 도큐먼트 내에 큐를 생성하는 데 사용할 수 있습니다
- $sort 연산자로 배열을 정렬할 수도 있습니다</aside>
- 💡 $slice, $sort를 배열상에서 $push와 함께 쓰려면 반드시 $each도 사용해야 합니다
- 배열을 집합으로 사용하기
- $ne를 사용합니다
- 예를 들어, 인용 목록에 저자가 존재하지 않을 때만 해당 저자를 추가하려면 아래와 같이 작성합니다
-
db.papers.updateOne({"authors cited" : {"$ne" : "Richie"}}, ... {$push : {"authors cited" : "Richie"}})
- "addToSet"을 사용할 수도 있습니다
- 중복을 피해 배열에 추가할 수 있습니다
- 고유한 값을 여러 개 추가하려면 $addToSet과 $each를 결합해 사용합니다
- 요소 제거하기
- 배열을 큐나 스택처럼 사용하려면 배열의 양 끝에서 요소를 제거하는 $pop을 사용합니다
- "$pop" : {"key" : 1}은 마지막 요소부터 요소를 제거하고, -1은 처음부터 요소를 제거합니다
- $pull은 주어진 조건에 맞는 배열 요소를 제거하는데 사용합니다
- 배열의 위치 기반 환경
- 배열 인덱스 기준은 0이며, 배열 요소는 인덱스를 도큐먼트의 키처럼 사용합니다
- 배열 요소 및 요소의 위치를 알아내서 갱신하는 위치 연산자 $를 제공합니다
- 배열 필터를 이용한 갱신
- arrayFilters는 개별 배열 요소를 갱신하는 배열 필터입니다
- 특정 조건에 맞는 배열 요소를 갱신할 수 있습니다
- 예를 들어 반대표가 5표 이상인 댓글을 숨기는 코드는 아래와 같습니다
-
db.blog.updateOne( {"post" : post_id}, {"$set" : { "comments.$[elem].hidden" : true } }, { arrayFilters : [{"elem.votes" : { $lte : -5 }}] } )
갱신 입력
갱신입력은 특수한 형태를 갖는 갱신입니다
갱신 조건에 맞는 도큐먼트가 존재하지 않을 때는 쿼리 도큐먼트와 갱신 도큐먼트를 합쳐서 새로운 도큐먼트를 생성합니다
조건에 맞는 도큐먼트가 발견되면 일반적인 갱신을 수행합니다
upsert : true로 설정하면 됩니다
도큐먼트가 생성될 때 필드가 설정돼야 할 때가 종종 있는데, 이후 갱신에서는 변경되지 않아야 하면
$setOnInsert를 사용합니다
지정 셸 보조자
save는 도큐먼트가 존재하지 않으면 도큐먼트를 삽입하고, 존재하면 도큐먼트를 갱신하게 하는 셸 함수입니다
다중 도큐먼트 갱신
조건에 맞는 도큐먼트를 모두 수정하려면 updateMany를 사용합시다
갱신한 도큐먼트 반환
상태의 값을 갱신할 때, 상태에 따라 두 스레드가 경쟁할 수 있습니다
이런 상황에는 findOneAndUpdate가 적합합니다
한 번의 연산으로 항목을 반환하고 갱신합니다
이외에도 findOneAndReplace, findOneDelete 등이 있습니다