도큐먼트 갱신
도큐먼트 갱신은 원자적으로 이뤄진다. 갱신 요청 두 개가 동시에 발생하면 서버에 먼저 도착한 요청이 적용된 후 다음 요청이 적용된다.
따라서 여러 개의 갱신 요청이 빠르게 발생하더라도 결국 마지막 요청이 안전하게 처리된다.
기본 동작을 원치 않으면 도큐먼트 비저닝 패턴(The Document Versioning pattern)을 이용해야 한다.
도큐먼트를 데이터베이스에 저장한 후에는 updateOne, updateMany, replaceOne과 같은 갱신 메서드를 사용해 변경한다.
updateOne
updateOne은 필터 도큐먼트를 첫 번째 매개변수로, 변경 사항을 설명하는 수정자 도큐먼트를 두 번째 매개변수로 사용한다.
> db.products.find()
{ "_id" : ObjectId("628f48747a32d396cf84a7f3"), "name" : "Notebook", "price" : 1300000 }
> db.products.updateOne({name: "Notebook"}, {$set: {name: "Apple Notebook"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.products.find()
{ "_id" : ObjectId("628f48747a32d396cf84a7f3"), "name" : "Apple Notebook", "price" : 1300000 }
updateMany
updateMany의 사용법은 updateOne과 같다.
다른 점은 필터에 일치하는 복수의 도큐먼트를 한 번에 갱신한다.
> db.products.find()
{ "_id" : ObjectId("628f48747a32d396cf84a7f3"), "name" : "Apple Notebook", "price" : 1000000 }
{ "_id" : ObjectId("628f4a527a32d396cf84a7f7"), "name" : "Samsong Handphone", "price" : 1000000 }
{ "_id" : ObjectId("628f4aa57a32d396cf84a7f8"), "name" : "Smart Watch", "price" : 300000 }
> db.products.updateMany({price: 1000000}, {$set: {price: 900000}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.products.find()
{ "_id" : ObjectId("628f48747a32d396cf84a7f3"), "name" : "Apple Notebook", "price" : 900000 }
{ "_id" : ObjectId("628f4a527a32d396cf84a7f7"), "name" : "Samsong Handphone", "price" : 900000 }
{ "_id" : ObjectId("628f4aa57a32d396cf84a7f8"), "name" : "Smart Watch", "price" : 300000 }
갱신 연산자
일반적으로 도큐먼트의 특정 부분만 갱신하는 경우가 많다.
부분 갱신에는 원자적 갱신 연산자를 사용한다.
갱신 연산자는 키를 변경, 추가, 제거하고, 심지어 배열과 내장 도큐먼트를 조작하는 복잡한 갱신 연산을 지정하는 데 사용하는 특수키다.
$set 연산자
$set은 필드 값을 설정한다.
$set은 자유롭게 데이터형도 바꿀 수 있다.
필드가 존재하지 않으면 새 필드가 생성된다. 이 기능은 스키마를 갱신하거나 사용자 정의 키를 추가할 때 편리하다.
블로그 글에 타이틀을 수정한다고 가정해보자.
> db.posts.find()
{ "_id" : ObjectId("628f51047a32d396cf84a7fc"), "title" : "post1", "contents" : "contents1", "viewCount" : 0 }
> db.posts.updateOne({_id: ObjectId("628f51047a32d396cf84a7fc")}, {$set: {title: "post1-1"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.posts.find()
{ "_id" : ObjectId("628f51047a32d396cf84a7fc"), "title" : "post1-1", "contents" : "contents1", "viewCount" : 0 }
$inc 연산자
$inc는 어떠한 숫자 값을 지정한 만큼 증가시킨다.
블로그 글을 조회할 때마다 조회수를 증가시킨다고 가정해보자.
> db.posts.find()
{ "_id" : ObjectId("628f51047a32d396cf84a7fc"), "title" : "post1", "contents" : "contents1", "viewCount" : 0 }
> db.posts.updateOne({_id: ObjectId("628f51047a32d396cf84a7fc")}, {$inc: {viewCount: 1}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.posts.find()
{ "_id" : ObjectId("628f51047a32d396cf84a7fc"), "title" : "post1", "contents" : "contents1", "viewCount" : 1 }
어떻게 보면 $set와 $inc는 비슷하지만 $inc는 숫자를 증감하기 위해 설계됐다.
$inc는 int, long, double, decimal 타입 값에만 사용할 수 있다.
숫자가 아닌 값의 증감을 시도하면 Modifier "$inc" allowed for numbers only라는 오류 메시지가 뜬다.
도큐먼트 치환
도큐먼트 치환은 대대적인 스키마 마이그레이션에 유용하다.
replaceOne
replaceOne은 도큐먼트를 새로운 것으로 완전히 치환한다.
replaceOne은 필터 도큐먼트를 첫 번째 매개변수로, 교체할 도큐먼트는 두 번째 매개변수로 사용한다.
> db.products.find()
{ "_id" : ObjectId("628f48747a32d396cf84a7f3"), "name" : "Apple Notebook", "price" : 900000 }
> db.products.replaceOne({name: "Apple Notebook"}, {name: "Macbook", price: 1300000})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.products.find()
{ "_id" : ObjectId("628f48747a32d396cf84a7f3"), "name" : "Macbook", "price" : 1300000 }
여기서 두 개 이상의 도큐먼트를 치환할 때 같은 _id 값을 갖는 도큐먼트를 생성하지 않도록 주의해야 한다.
> db.products.find()
{ "_id" : ObjectId("628f4de27a32d396cf84a7f9"), "name" : "Macbook", "price" : 2000000 }
{ "_id" : ObjectId("628f4de27a32d396cf84a7fa"), "name" : "Macbook", "price" : 2200000 }
{ "_id" : ObjectId("628f4de27a32d396cf84a7fb"), "name" : "Macbook", "price" : 2300000 }
> macbook=db.products.findOne({_id: ObjectId("628f4de27a32d396cf84a7fa")})
{
"_id" : ObjectId("628f4de27a32d396cf84a7fa"),
"name" : "Macbook",
"price" : 2200000
}
> macbook.price = macbook.price + 100000
2300000
> db.products.replaceOne({name: "Macbook"}, macbook)
WriteError({
"index" : 0,
"code" : 66,
"errmsg" : "After applying the update, the (immutable) field '_id' was found to have been altered to _id: ObjectId('628f4de27a32d396cf84a7fa')",
"op" : {
"q" : {
"name" : "Macbook"
},
"u" : {
"_id" : ObjectId("628f4de27a32d396cf84a7fa"),
"name" : "Macbook",
"price" : 2300000
},
"multi" : false,
"upsert" : false
}
})
replaceOne으로 첫 번째 도큐먼트를 변수에 선언하고, 다른 도큐먼트를 해당 변수로 치환하게 되면 _id 값이 중복이 되는 것이기 때문에 오류가 발생한다.
이런 상황을 피하려면 고유한 도큐먼트를 대상으로 지정하는 것이 좋다.
_id 값이 컬렉션 기본 인덱스의 기초를 형성하므로 필터에 _id를 사용해도 효율적이다.
'MongoDB' 카테고리의 다른 글
몽고DB 배열 연산자 소개 및 사용법 (0) | 2022.06.03 |
---|---|
몽고DB 도큐먼트 삽입, 삭제하기 (0) | 2022.05.26 |
몽고DB 셸 사용해보기 (0) | 2022.05.26 |
몽고DB의 데이터형 (0) | 2022.05.25 |
몽고DB 셸 소개 (0) | 2022.05.25 |