업데이트, 원자적연산, 삭제
도큐먼트 업데이트
MongoDB 업데이트 방식
– 도큐먼트 전체 대치
– 도큐먼트 내의 특정 필드를 수정하기 위해 업데이트 연산자 사용
{
_id: ObjectId("4c4b1476238d3b4dd5000001"),
username: "kbanker",
email: "kylebanker@gmail.com",
first_name: "Kyle",
last_name: "Banker",
hashed_password: "bd1cfa194c3a603e7186780824b04419",
addresses: [
{
name: "work",
street: "1 E. 23rd Street",
city: "New York",
state: "NY",
zip: 10010
}
]
}
doc = db.users.findOne({username: "kbanker"})
user_id = doc._id
대치에 의한 수정
user_id = ObjectId("4c4b1476238d3b4dd5003981")
doc = db.users.findOne({_id: user_id}) //사용자 _id로 도큐먼트 질의
doc['email'] = 'mongodb-user@mongodb.com' //email 속성 수정
print('updating ' + user_id)
db.users.update({_id: user_id}, doc) //update 메서드의 매개변수로 넘겨줌으로써 전체 도큐먼트를 대치시킴
연산자에 의한 수정
user_id = ObjectId("4c4b1476238d3b4dd5000001")
db.users.update({_id: user_id},
{$set: {email: 'mongodb-user2@mongodb.com'}}) //$set 연산자를 사용하여 email 필드를 수정
두 방법의 비교
//대치를 이용한 상품 리뷰수 증가 : 서버로부터 도큐먼트를 가져와 수정 후 서버로 전송
product_id = ObjectId("4c4b1476238d3b4dd5003982")
doc = db.products.findOne({_id: product_id})
doc['total_reviews'] += 1 // total_reviews의 값에 1을 더함
db.products.update({_id: product_id}, doc)
//타깃을 이용한 상품 리뷰수 증가 : $inc 연산자를 사용하여 업데이트
db.products.update({_id: product_id}, {$inc: {total_reviews: 1}})
대치 Vs 연산자
– 사용자의 어떤 속성을 수정하든지 상관없이 업데이트를 수행하는 코드는 동일하다.
업데이트를 일반화하는 MongoDB 객체 매퍼를 작성한다면 대치 방식이 합리적
– 타킷방식은 좀 더 나은 성능을 갖는다. 수정할 도큐먼트를 요청할 필요도 없으며 도큐먼트의 크기가 일반적으로 작다.
도큐먼트를 원자적으로 업데이트할 때 효율적
전자상거래 업데이트
상품의 평점 평균 내보기
product_id = ObjectId("4c4b1476238d3b4dd5003981")
// product_id = db.products.findOne({sku: '9092'}, {'_id': 1})
count = 0
total = 0
db.reviews.find({product_id: product_id}, {rating: 4}).forEach( //해당 상품Id에 대한
function(review) {
total += review.rating //리뷰평점 합산
count++ //리뷰수 카운트
})
average = total / count
db.products.update({_id: product_id},
{$set: {total_reviews: count, average_review: average}})
db.products.update({_id: product_id},
{
$set: {
average_review: average,
ratings_total: total //리뷰 평점의 총합을 보관하는 필드 추가
},
$inc: {
total_reviews: 1
}
})
카테고리 계층 구조
주어진 카테고리에 대해 조상 리스트 업데이트하기
var generate_ancestors = function(_id, parent_id) {
ancestor_list = []
var cursor = db.categories.find({_id: parent_id})
//루트 노드에 도달할 때까지 가 노드의 parent_id 속성을 질의
while(cursor.size() > 0) {
parent = cursor.next()
// 조상들의 순서가 있는 리스트를 구하고 ancestor_list에 저장
ancestor_list.push(parent)
parent_id = parent.parent_id
cursor = db.categories.find({_id: parent_id})
}
//$set을 사용해 카테고리의 ancestor 속성에 저장
db.categories.update({_id: _id}, {$set: {ancestors: ancestor_list}})
}

Home 최상위 카테고리에 Gardening 카테고리 추가하기 (아래)

parent_id = ObjectId("8b87fb1476238d3b4dd50003")
category = {
parent_id: parent_id,
slug: "gardening",
name: "Gardening",
description: "All gardening implements, tools, seeds, and soil."
}
db.categories.save(category) //save를 이용하여 도큐먼트 저장
generate_ancestors(category._id, parent_id) // generate_ancestors 호출
//Outdoors 카테고리를 Gardening 카테고리 아래로 옮기기
//Outdoors의 parent_id를 gardening_id로 변경
db.categories.update({_id: outdoors_id}, {$set: {parent_id: gardening_id}})
//Outdoors의 하위 카테고리를 조회하여 조상 리스트 수정
db.categories.find({'ancestors.id': outdoors_id}).forEach(
function(category) {
generate_ancestors(category._id, outdoors_id)
}
)
//Outdoors 카테고리명 수정 -> The Great Outdoors
doc = db.categories.findOne({_id: outdoors_id}) //Outdoors 도큐먼트를 찾음
doc.name = "The Great Outdoors" //name 속성 변경
db.categories.update({_id: outdoors_id}, doc) //대치방식으로 업데이트
db.categories.update(
{'ancestors._id': outdoors_id},
{$set: {'ancestors.$': doc}}, //모든 조상 리스트에 나타나는 Outdoors를 변경
{multi: true}) //셀렉터와 일치하는 모든 도큐먼트에 업데이트 적용 (다중 업데이트 가능)
ancestor.$ : $ 위치 연산자 : 쿼리 셀렉터와 일치하는 배열 인덱스를 그 자신으로 대치에 업데이트 시킴
db.users.update({
_id: ObjectId("4c4b1476238d3b4dd5000001"),
'addresses.name': 'work'},
{$set: {'addresses.$.street': '155 E 31st St.'}}) //사용자 주소 필드 변경 시 사용되는 위치 연산자
리뷰
{
helpful_votes: 3,
voter_ids: [
ObjectId("4c4b1476238d3b4dd5000041"),
ObjectId("7a4f0376238d3b4dd5000003"),
ObjectId("92c21476238d3b4dd5000032")
]
}
db.reviews.update({_id: ObjectId("4c4b1476238d3b4dd5000041")}, {
//$push 연산자를 사용해 추천자의 ID를 리스트에 추가
$push: {
voter_ids: ObjectId("4c4b1476238d3b4dd5000001")
},
//$inc 연산자를 사용해 추천수 하나씩 증가
$inc: {
helpful_votes: 1
}
})
query_selector = {
_id: ObjectId("4c4b1476238d3b4dd5000041"),
voter_ids: {
$ne: ObjectId("4c4b1476238d3b4dd5000001")
}
}
db.reviews.update(query_selector, {
$push: {
voter_ids: ObjectId("4c4b1476238d3b4dd5000001")
},
$inc : {
helpful_votes: 1
}
})
레이스 컨디션 (race condition)
타깃 방식과 대치 방식으로 동시에 업데이트될 때 데이터가 손실 될 수 있다.
주문
업서트(upsert) : 업데이트할 도큐먼트가 존재하지 않을 경우 새로운 도큐먼트를 인서트
cart_item = {
_id: ObjectId("4c4b1476238d3b4dd5003981"),
slug: "wheel-barrow-9092",
sku: "9092",
name: "Extra Large Wheel Barrow",
pricing: {
retail: 5897,
sale: 4897
}
}
selector = {
user_id: ObjectId("4c4b1476238d3b4dd5000001"),
state: 'CART'
}
update = {
$inc: {
sub_total: cart_item['pricing']['sale']
}
}
db.orders.update(selector, update, {upsert: true})
{
user_id: ObjectId("4c4b1476238d3b4dd5000001"),
state: 'CART',
subtotal: 9794
}
selector = {user_id: ObjectId("4c4b1476238d3b4dd5000001"),
state: 'CART',
'line_items._id':
{'$ne': cart_item._id}
}
update = {'$push': {'line_items': cart_item}}
db.orders.update(selector, update)
{
user_id: ObjectId("4c4b1476238d3b4dd5000001"),
state: 'CART',
line_items: [
{
_id: ObjectId("4c4b1476238d3b4dd5003981"),
quantity: 2,
slug: "wheel-barrow-9092",
sku: "9092",
name: "Extra Large Wheel Barrow",
pricing: {
retail: 5897,
sale: 4897
}
}
],
subtotal: 9794
}