MongoDB 업데이트와 삭제
[업데이트 타입과 옵션]
MongoDB 업데이트 방식
– 타깃방식
– 대치방식
db.products.update_one({}, {name: "Pitchfork", $addToSet: {tags: 'cheap'}})
//도큐먼트 이름을 바꾸려는 목적이면 $set 연산자 사용
db.products.update_one({},
{$set: {name: "Pitchfork"}, $addToSet: {tags: 'cheap'}})
다중 도큐먼트 업데이트
– 기본적으로 쿼리 셀렉터와 일치하는 첫 번째 도큐먼트만을 업데이트 함 => 여러 개를 하려면 다중 업데이트 사용
// 모든 도큐먼트에 cheap 태그 추가해보자.
db.products.update({}, {$addToSet: {tags: 'cheap'}}, {multi: true}) // multi : true 매개변수 값 추가
//TODO : 업데이트는 원자적으로 작동되므로 실패에 대한 처리도 같이 되어야한다.
업서트
– 아이템이 없을 경우 새로 추가하고 이미 있다면 업데이트 하는것 (=merge 와 같은..)
업서트는 대치 방식의 업데이트에서는 수행될 수 없다.
db.products.update({slug: 'hammer'},
{$addToSet: {tags: 'cheap'}}, {upsert: true}) // upsert : true 매개변수 값 추가
[업데이트 연산자]
표준적인 업데이트 연산자
- $inc : 수치 증가 또는 감소할 때 사용, 수의 타입이 변경되지 않는 한 디스크에 해당 부분만 적용이 되어 지정된 값만 영향을 받음.
db.products.update({slug: "shovel"}, {$inc: {review_count: 1}})
db.users.update({username: "moe"}, {$inc: {password_retries: -1}})
//수치를 더하거나 뺄 때
db.readings.update({_id: 324}, {$inc: {temp: 2.7435}})
//아이디가 324가 없을 경우 새로 생성
db.readings.update({_id: 324}, {$inc: {temp: 2.7435}}, {upsert: true})
- $set, $unset : $set – 특정 키의 값을 정해줄 때, $unset – 도큐먼트에 해당 키 삭제
// 키에 대한 값이 이미 존재하면 그 값을 덮어쓰고 그렇지 않을 경우 새로운 키 생성
db.readings.update({_id: 324}, {$set: {temp: 97.6}})
db.readings.update({_id: 325}, {$set: {temp: {f: 212, c: 100}}})
db.readings.update({_id: 326}, {$set: {temps: [97.6, 98.4, 99.1]}})
// readings라는 컬렉션 내에 324 아이디의 temp 키 삭제
db.readings.update({_id: 324}, {$unset: {temp: 1}})
{_id: 325, 'temp': {f: 212, c: 100}}
db.readings.update({_id: 325}, {$unset: {'temp.f': 1}}) // f(화씨) 삭제
{_id: 326, temps: [97.6, 98.4, 99.1]}
db.readings.update({_id: 326}, {$pop: {temps: -1}}) //0번째 배열 값 삭제
// $unset의 배열처리는 삭제가 아니라 null처리가 됨. 삭제하기 위해서는 $pull, $pop 사용
db.readings.update({_id: 325}, {$unset: {'temp.f': 1}})
db.readings.update({_id: 326}, {$unset: {'temps.0': 1}})
- $rename : 키의 이름을 바꿀 때 사용
db.readings.update({_id: 324}, {$rename: {'temp': 'temperature'}})
db.readings.update({_id: 325}, {$rename: {'temp.f': 'temp.fahrenheit'}})
- $setOnInsert : 도큐먼트가 새 것이고 삽입을 수행할 때만 필드를 수정하도록 지정 (업서트 중에 관리하는 데이터를 업데이트 되지 않도록)
// state 값의 변동 없이 재고 특정 항목의 수량을 증가
db.products.update({slug: 'hammer'}, {
$inc: {
quantity: 1
},
$setOnInsert: {
state: 'AVAILABLE'
}
}, {upsert: true})
배열 업데이트 연산자
- $push, $pushAll, $each :
$push – 배열의 값을 추가. 배열 끝에 단일 요소 추가
$pushAll – 여러 개 값을 배열에 추가 (deprecated 가능성 있음)
$each – $push와 같이 여러 개 추가할 때 사용
//shovel 상품에 새로운 태그 추가
db.products.update({slug: 'shovel'}, {$push: {tags: 'tools'}})
//태그를 여러 개 추가
db.products.update({slug: 'shovel'},
{$push: {tags: {$each: ['tools', 'dirt', 'garden']}}})
//태그를 여러 개 추가
db.products.update({slug: 'shovel'},
{$pushAll: {'tags': ['tools', 'dirt', 'garden']}})
- $slice : 값을 배열로 넣고 너무 크게 늘리지 않을 때 사용
$push 와 $each 연산자와 함께 사용하며 결과 배열을 특정 크기로 잘라 이전 버전을 먼저 제거할 수 있다.
인수는 0보다 작거나 같아야 한다.
{
_id: 326,
temps: [92, 93, 94]
}
// 위 도큐먼트 업데이트
db.temps.update({_id: 326}, {
$push: {
temps: {
$each: [95, 96],
$slice: -4
}
}
})
// 결과 값
// 값을 배열로 밀어넣은 후 값이 네 개만 남을 때까지 제거
// -1 일때는 96, 0일때는 [], 4일 때는 [92,93,94,95]
{
_id: 326,
temps: [93, 94, 95, 96]
}
- $sort : $push, $slice, $each 연산자와 정렬
{
_id: 300,
temps: [
{ day: 6, temp: 90 },
{ day: 5, temp: 95 }
]
}
//day 기준으로 정렬한 후 배열 추가
db.temps.update({_id: 300}, {
$push: {
temps: {
$each: [
{ day: 7, temp: 92 }
],
$slice: -2,
$sort: {
day: 1
}
}
}
})
//결과 값
{
_id: 300,
temps: [
{ day: 6, temp: 90 },
{ day: 7, temp: 92 }
]
}
- $addToSet, $each :
$addToSet – 배열에 존재하지 않을 경우에만 추가
$each는 $addToSet 및 $push 연산자에서만 사용될 수 있다.
//shovel 상품에 tool이라는 태그 값을 가지고 있으면 수행하지 않음
db.products.update({slug: 'shovel'}, {$addToSet: {'tags': 'tools'}})
//$each에 지정된 값 중에 tags에 존재하지 않는 값만 추가
db.products.update({slug: 'shovel'},
{$addToSet: {tags: {$each: ['tools', 'dirt', 'steel']}}})
- $pop : 배열에서 아이템을 삭제하는 가장 기본적인 방법 ($push와 반대)
마지막으로 첨가된 아이템 삭제. 삭제된 값을 반환하지 않음
// ['tools', 'dirt', 'garden', 'steel'] -> ['tools', 'dirt', 'garden']
db.products.update({slug: 'shovel'}, {$pop: {'tags': 1}})
// ['tools', 'dirt', 'garden'] -> ['dirt', 'garden']
db.products.update({slug: 'shovel'}, {$pop: {'tags': -1}})
- $bit : 업데이트상에서 비트단위 OR 연산과 AND 연산을 하게 해줌
{
_id: 16,
permissions: 4 //r-- , 이진수 100
}
//비트단위 OR 연산을 사용하여 쓰기 권한 추가
//number에 double을 사용하므로 NumberInt() 사용
db.permissions.update({_id: 16}, {$bit: {permissions: {or: NumberInt(2)}}})
{
_id: 16,
permissions: 6 //rw-, 이진수110
}
- $pull, $pullAll :
$pull – 배열에서 원소의 위치 대신 값을 지정해서 아이템 삭제
$pullAll – 여러 개의 값을 삭제
db.products.update({slug: 'shovel'}, {$pull: {tags: 'dirt'}})
db.products.update({slug: 'shovel'},
{$pullAll: {'tags': ['dirt', 'garden']}})
{_id: 326, temps: [97.6, 98.4, 100.5, 99.1, 101.2]}
// 100보다 높은 온도 삭제
db.readings.update({_id: 326}, {$pull: {temps: {$gt: 100}}})
//결과 값
{_id: 326, temps: [97.6, 98.4, 99.1]}
위치 업데이트
{
_id: ObjectId("6a5b1476238d3b4dd5000048"),
line_items: [
{
_id: ObjectId("4c4b1476238d3b4dd5003981"),
sku: "9092",
name: "Extra Large Wheelbarrow",
quantity: 1,
pricing: {
retail: 5897,
sale: 4897
}
},
{
_id: ObjectId("4c4b1476238d3b4dd5003982"),
sku: "10027",
name: "Rubberized Work Glove, Black",
quantity: 2,
pricing: {
retail: 1499,
sale: 1299
}
}
]
}
// SKU 10027인 주문 아이템의 수량을 5로 수정하길 원할 때
query = {
_id: ObjectId("6a5b1476238d3b4dd5000048"),
'line_items.sku': "10027"
}
update = {
$set: {
'line_items.$.quantity': 5 //$가 위치 연산자
}
}
db.orders.update(query, update)
[findAndModify 명령]
doc = db.orders.findAndModify({
query: {
user_id: ObjectId("4c4b1476238d3b4dd5000001"),
},
update: {
$set: {
state: "AUTHORIZING"
}
}
})
- query : 도큐먼트 쿼리 셀렉터이고 디폴트 값은 {}
- update : 업데이트할 내용을 지정하는 도큐먼트이고 디폴트 값은 {}
- remove : true면 객체 삭제, 디폴트 값은 false
- new : true면 업데이트 수행한 후 업데이트 적용된 도큐먼트 반환, 디폴트 값은 false
- sort : 정렬 방향을 결정하는 도큐먼트, 일치하는 도큐먼트 중 어떤 것을 처리해야하는지 정하는데 사용됨, 최근 생성된 도큐먼트 처리 시 {created_at:-1} 형식을 사용 가능
- fields : 일부의 필드만 필요하다면 이 옵션으로 필요한 필드 지정.
- upsert : true이면 findAndModify를 업서트처럼 수행. 새로 생성한 도큐먼트 반환 시 {new:ture} 사용
[삭제]
//모든 리뷰 삭제
db.reviews.remove({})
//특정 사용자가 작성한 리뷰 삭제
db.reviews.remove({user_id: ObjectId('4c4b1476238d3b4dd5000001')})
[동시성, 원자성, 고립]
[업데이트성능]