이 글은 몽고의 특징과 쉘 사용법에 대해서 작성할 글입니다. 2개의 글로 작성되있습니다.
다음글 : MongoDB 특징과 쉘 사용에 대해서 알아보자 - 2
글의 내용이 많으므로 찾으시는 목차를 검색하여 글에서 검색하면 빠르게 찾으실 수 있습니다.
목차
- find
- find(), findOne()
- findOneAndUpdate()
- update
- updateOne(), updateMany()
- $set 제한자
- 갱신 연산자($inc)
- 배열 연산자($push)
- $push에 $each 제한자
- $push에 $slice
- $sort 제한자를 $push 작업에 적용
- 배열을 집합으로 사용하기($ne)
- 요소 제거
- 배열의 위치 기반 변경
- 배열 필터를 이용한 갱신
- 갱신 입력
- $setOnInsert
- 다중 도큐먼트 갱신
- 갱신한 도큐먼트 반환
- insert
- insertMany()
- $addToSet
- 저장 셀 보조자
- delete
- deleteOne(), deleteMany()
- replace
- replaceOne()
사용
MongoDB는 도큐먼트 지향 데이터베이스입니다. MongoDB는 아래와 같은 특징을 가지고 있습니다.
- 관계형 모델을 사용하지 않는 이유는 분산 확장을 쉽게 하기 위함
- 도큐먼트 지향 데이터베이스에서는 행 개념 대신에 보다 유연한 모델인 도큐먼트 사용
- 내장 도큐먼트와 배열을 허용함으로써 도큐먼트 지향 모델은 복잡한 계층 관계를 하나의 로코드로 표현
- 키와 값을 미리 정의하지 않아 고정된 스키마가 없다.
다양한 기능
인덱싱
- 몽고는 일반적인 보조 인덱스를 지원하며 고유(unique), 복합(compund), 공간 정보, 전문 인덱싱 기능도 제공.
- 중첩된 도큐먼트 및 배열과 같은 계층 구조의 보조 인덱스도 지원
집계
- 데이터 처리 파이프라인 개념을 기반으로 한 집계 프레임워크를 제공
- 집계 파이프라인은 데이터베이스 최적화를 최대한 활용해, 서버 측에서 비교적 간단한 일련의 단계로 데이터를 처리함으로써 복잡한 분석 엔진을 구축
특수한 컬렉션 유형
- 로그와 같은 최신 데이터를 유지하고자 세션이나 고정 크기 컬렉션과 같이 특정 시간에 만료해야 하는 데이터에 대해 유효 시간 컬렌셕을 지원
- 기준 필터와 일치하는 도큐먼트에 한정된 부분 인덱스를 지원
파일 스토리지
- 큰 파일과 파일 메타데이터를 편리하게 저장하는 프로토콜을 지원
기본 개념
- 데이터의 기본 단위는 도큐먼트다.
- 컬렉션은 동적 스키마가 없는 테이블과 같다
- 단일 인스턴스는 자체적인 컬렉션을 갖는 여러 개의 독립적인 데이터베이스를 호스팅한다.
- 모든 도큐먼트는 컬렉션 내에서 고유한 특수키인 "_id"를 가진다.
- 몽고 셀이라는 간단하지만 강력한 도구와 함께 배포된다. 몽고 셀은 모고디비 인스턴스를 관리하고 몽고디비 쿼리 언어로 데이터를 조작하기 위한 내장 지원을 제공한다.
도큐먼트
- 정렬된 키와 연결된 값의 집합으로 이뤄진 도큐먼트.
// 타입 구분
{"count" : 5}
{"count" : "5"}
// 대소문자 구분
{"count" : 5}
{"Count" : 5}
// 같은 키는 불가
{"greeting" : "Hello", "greeting" : "World"}
컬렉션
- 도큐먼트들의 키, 키의 개수, 데이터형의 값은 모두 다르다.
{"greeting" : "Hello", "view" : 3}
{"signoff" : "Good"}
그렇다면 왜 컬렉션이 필요할까?
- 같은 컬렉션에 다른 종류의 도큐먼트를 저장하면 개발자와 관리자에게 번거로운 일이 발생한다.
- 컬렉션별로 목록을 뽑으면 한 컬렉션 내 특정 데이터형별로 쿼리해서 목록을 뽑을 때보다 훨씬 빠르다.
- 같은 종류의 데이터를 하나의 컬렉션에 모다우면 데이터 지역성에 좋다.
- 인덱스를 만들면 도큐먼트는 특정 구조를 가져야한다. 이러한 인덱스는 컬렉션별로 정의한다.
셀
- 셀은 관리 기능이나, 실행 중인 인스턴스를 점검하거나 간단한 기능을 시험하는 데 매우 유용.
insertOne()
컬렉션에 도큐먼트를 추가합니다.
# movie 지역변수 생성
movie =
{
"title" : "Star Wars: kkkkkk Movie",
"director" : "Gerod laus",
"year" : 1977
}
# moviews 컬렉션에 저장할 수 있다.
db.movies.insertOne(movie)
# 조회
db.movies.find().pretty()
{
"_id" : ObjectId("606d5809852386250330a6fe"),
"title" : "Star Wars: kkkkkk Movie",
"director" : "Gerod laus",
"year" : 1977
}
아래와 같이 중복된 _id를 넣으려 하면 error가 발생합니다.
/error
db.movies.insertMany(
[
{"_id": 0, "title" : "Diejdlsdd"},
{"_id": 1, "title" : "E.T."},
{"_id": 1, "title" : "E.T.2"},
{"_id": 2, "title" : "Runner"}
]
);
find(), findOne()
find와 findOne은 컬렉션을 쿼리하는 데 사용
# 단건 조회
db.movies.findOne()
{
"_id" : ObjectId("606d5809852386250330a6fe"),
"title" : "Star Wars: kkkkkk Movie",
"director" : "Gerod laus",
"year" : 1977
}
updateOne(), updateMany()
컬렉션을 갱신할 때 사용
# update
> db.movies.updateOne( {"title" : "Star Wars: kkkkkk Movie"},
... {$set : {reviews: []}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
# 확인
db.movies.find().pretty()
{
"_id" : ObjectId("606d59c263f269c1ddc2505a"),
"title" : "Star Wars: kkkkkk Movie",
"director" : "Gerod laus",
"year" : 1977,
"reviews" : [ ]
}
Document Drop
deleteMany보다는 drop이 빠르다
db.movies.deleteMany({}) <<< db.movies.drop()
insertMany()
여러 도큐먼트를 컬렉션에 삽입하려면 insertMany()를 사용한다.
# insert
db.movies.insertMany(
[
{"title" : "Diejdlsdd"},
{"title" : "E.T."},
{"title" : "Runner"}
]
);
# find
db.movies.find()
{ "_id" : ObjectId("606d5ce467fe246be00de3b7"), "title" : "Diejdlsdd" }
{ "_id" : ObjectId("606d5ce467fe246be00de3b8"), "title" : "E.T." }
{ "_id" : ObjectId("606d5ce467fe246be00de3b9"), "title" : "Runner" }
deleteOne(), deleteMany()
deleteOne() : 검색 조건에 여러개가 걸려도 1개만 삭제된다.
deleteMany() : 검색 조건에 여러개가 걸리면 전체 삭제
# find
db.movies.find()
{ "_id" : ObjectId("606d59c263f269c1ddc2505a"), "title" : "Star Wars: kkkkkk Movie", "director" : "Gerod laus", "year" : 1977, "reviews" : [ ] }
{ "_id" : ObjectId("606d5ce467fe246be00de3b7"), "title" : "Diejdlsdd" }
{ "_id" : ObjectId("606d5ce467fe246be00de3b8"), "title" : "E.T." }
{ "_id" : ObjectId("606d5ce467fe246be00de3b9"), "title" : "Runner" }
{ "_id" : 0, "title" : "Diejdlsdd" }
{ "_id" : 1, "title" : "E.T." }
# delete
db.movies.deleteOne({"_id" : 1})
{ "acknowledged" : true, "deletedCount" : 1 }
# find
db.movies.find()
{ "_id" : ObjectId("606d59c263f269c1ddc2505a"), "title" : "Star Wars: kkkkkk Movie", "director" : "Gerod laus", "year" : 1977, "reviews" : [ ] }
{ "_id" : ObjectId("606d5ce467fe246be00de3b7"), "title" : "Diejdlsdd" }
{ "_id" : ObjectId("606d5ce467fe246be00de3b8"), "title" : "E.T." }
{ "_id" : ObjectId("606d5ce467fe246be00de3b9"), "title" : "Runner" }
{ "_id" : 0, "title" : "Diejdlsdd" }
replaceOne()
도큐먼트를 치환한다. 가능하다면 _id키를 사용하자!
# insert default data
db.users.insertOne({"name" : "joe", "friends" : 32, "enemies" : 2 })
# select
db.users.findOne()
{
"_id" : ObjectId("606d61ac67fe246be00de3ba"),
"name" : "joe",
"friends" : 32,
"enemies" : 2
}
var joe = db.users.findOne({"name" : "joe"})
> joe
{
"_id" : ObjectId("606d61ac67fe246be00de3ba"),
"name" : "joe",
"friends" : 32,
"enemies" : 2
}
joe.relationships = {"friends" : joe.friends, "enemies" : joe.enemies}
joe.username = joe.name
delete joe.friends
delete joe.friends
delete joe.name
db.users.replaceOne({"name" : "joe"}, joe);
db.users.findOne()
{
"_id" : ObjectId("606d61ac67fe246be00de3ba"),
"enemies" : 2,
"relationships" : {
"friends" : 32,
"enemies" : 2
},
"username" : "joe"
}
$set 제한자
$set은 필드 값을 설정한다. 필드가 없으면 새 필드를 생성하고 스키마를 갱신하거나 사용자 정의 키를 추가할 때 편리하다.
# save
db.users.insertOne({
"name" : "joe",
"age" : 30,
"sex" : "male",
"location" : "asdf"
})
{
"acknowledged" : true,
"insertedId" : ObjectId("606d666b290897f1f912c161")
}
# update set
db.users.updateOne({"_id" : ObjectId("606d65d6290897f1f912c160")},
{"$set" : {"favorite book" : "War"}})
db.users.findOne()
> db.users.find()
{ "_id" : ObjectId("606d61ac67fe246be00de3ba"), "enemies" : 2, "relationships" : { "friends" : 32, "enemies" : 2 }, "username" : "joe" }
{ "_id" : ObjectId("606d6295290897f1f912c15e"), "name" : "joe", "friends" : 32, "enemies" : 2, "favorite book" : "War" }
{ "_id" : ObjectId("606d65d6290897f1f912c160"), "name" : "joe", "age" : 30, "sex" : "male", "location" : "asdf", "favorite book" : "War" }
{ "_id" : ObjectId("606d666b290897f1f912c161"), "name" : "joe", "age" : 30, "sex" : "male", "location" : "asdf" }
# update set
db.users.updateOne({"name" : "joe"},
{"$set" : {"favorite book" : ["Cat", "Dogs", "KKKK"] }})
> db.users.find()
{ "_id" : ObjectId("606d61ac67fe246be00de3ba"), "enemies" : 2, "relationships" : { "friends" : 32, "enemies" : 2 }, "username" : "joe" }
{ "_id" : ObjectId("606d6295290897f1f912c15e"), "name" : "joe", "friends" : 32, "enemies" : 2, "favorite book" : [ "Cat", "Dogs", "KKKK" ] }
{ "_id" : ObjectId("606d65d6290897f1f912c160"), "name" : "joe", "age" : 30, "sex" : "male", "location" : "asdf", "favorite book" : "War" }
{ "_id" : ObjectId("606d666b290897f1f912c161"), "name" : "joe", "age" : 30, "sex" : "male", "location" : "asdf" }
# update unset
db.users.updateOne({"name" : "joe"},
{"$unset" : {"favorite book" : 1 }})
> db.users.find()
{ "_id" : ObjectId("606d61ac67fe246be00de3ba"), "enemies" : 2, "relationships" : { "friends" : 32, "enemies" : 2 }, "username" : "joe" }
{ "_id" : ObjectId("606d6295290897f1f912c15e"), "name" : "joe", "friends" : 32, "enemies" : 2 }
{ "_id" : ObjectId("606d65d6290897f1f912c160"), "name" : "joe", "age" : 30, "sex" : "male", "location" : "asdf", "favorite book" : "War" }
{ "_id" : ObjectId("606d666b290897f1f912c161"), "name" : "joe", "age" : 30, "sex" : "male", "location" : "asdf" }
갱신 연산자($inc)
일반적으로 도큐먼트의 특정 부분만 갱신하는 경우가 많다. 부분 갱신에는 원자적 갱신 연산자를 사용한다. 갱신 연산자는 키를 변경, 추가, 제거하고 배열과 내장 도큐먼트를 조작하는 복잡한 갱신 연산을 지정하는 데 사용하는 특수키이다.
$set와 비슷하지만 숫자를 증감하기 위해 설계되었다. int, long, double, decimal 타입에만 사용
# save
db.analytics.insertOne({"url" : "www.example.com", "pageviews" : 52})
# update
db.analytics.updateOne({"url" : "www.example.com"},
{"$inc" : {"pageviews" : 3}})
# pageviews 3증가
db.analytics.findOne()
{
"_id" : ObjectId("606d64c3290897f1f912c15f"),
"url" : "www.example.com",
"pageviews" : 53
}
배열 연산자($push)
배열을 다루는 데 갱신 연산자를 사용할 수 있다. 배열은 일반적이고 강력한 데이터 구조이고 연산자는 리스트에 대한 인덱스를 지정할 수 있을 뿐 아니라 셋처럼 이중으로 쓸수 있다.
- 요소 추가
- $push는 배열이 이미 존재하면 배열 끝에 요소를 추가, 존재하지 않으면 새로운 배열을 생성!
db.blog.posts.insertOne({"title" : "A Blog", "content" : "..."})
db.blog.posts.findOne()
{
"_id" : ObjectId("606d6b07290897f1f912c162"),
"title" : "A Blog",
"content" : "..."
}
db.blog.posts.updateOne({"title" : "A Blog"},
{"$push" : {"comment" :
{"name" : "joe", "email" : "joe@example.com", "content" : "nice!"}}})
db.blog.posts.findOne()
{
"_id" : ObjectId("606d6b07290897f1f912c162"),
"title" : "A Blog",
"content" : "...",
"comment" : [
{
"name" : "joe",
"email" : "joe@example.com",
"content" : "nice!"
}
]
}
db.blog.posts.updateOne({"title" : "A Blog"},
{"$push" : {"comment" :
{"name" : "bob", "email" : "bob@example.com", "content" : "goog!"}}})
db.blog.posts.findOne()
{
"_id" : ObjectId("606d6b07290897f1f912c162"),
"title" : "A Blog",
"content" : "...",
"comment" : [
{
"name" : "joe",
"email" : "joe@example.com",
"content" : "nice!"
},
{
"name" : "bob",
"email" : "bob@example.com",
"content" : "goog!"
}
]
}
$push에 $each 제한자
$push에 $each 제한자를 사용하면 작업 한번으로 값을 여러 개 추가할 수 있다.
db.stock.ticker.insertOne({"_id" : "GOOG"})
db.stock.ticker.updateOne({"_id" : "GOOG"},
{"$push" : {"hourly" : {"$each" : [562.776, 562.790, 559.123]}}})
db.stock.ticker.find()
{ "_id" : "GOOG", "hourly" : [ 562.776, 562.79, 559.123 ] }
$push에 $slice
$push에 $slice를 결합하면 배열을 특정 길이로 늘릴 수 있다. slice로 배열의 최대 길이를 조정할 수 있다.
db.temp.insertOne({"genre" : "horror"})
# 100번 반복
db.temp.updateOne({"genre" : "horror"},
{"$push" : {"top10" : {"$each" : ["Night", "Saw"],
"$slice" : -10}}})
db.temp.find()
{ "_id" : ObjectId("606d75b0eff0581991d9a372"), "genre" : "horror", "top10" : [ "Night", "Saw", "Night", "Saw", "Night", "Saw", "Night", "Saw", "Night", "Saw" ] }
$sort 제한자를 $push 작업에 적용
db.animals.insertOne({"kind" : "cat"})
# 100번 반복
db.animals.updateOne({"kind" : "cat"},
{"$push" : {"top10" : {"$each" : [{"name" : "navy", "age" : 10}, {"name" : "mongi", "age" : 20}],
"$slice" : -10,
"$sort" : {"age" : -1}}}})
db.animals.find()
{ "_id" : ObjectId("606d77b5eff0581991d9a373"), "kind" : "cat", "top10" : [ { "name" : "navy", "age" : 10 }, { "name" : "navy", "age" : 10 }, { "name" : "navy", "age" : 10 }, { "name" : "navy", "age" : 10 }, { "name" : "navy", "age" : 10 }, { "name" : "navy", "age" : 10 }, { "name" : "navy", "age" : 10 }, { "name" : "navy", "age" : 10 }, { "name" : "navy", "age" : 10 }, { "name" : "navy", "age" : 10 } ] }
배열을 집합으로 사용하기($ne)
특정값이 배열에 존재하지 않을 때 해당 값을 추가하면서, 배열을 집합처럼 처리하려면 쿼리 도큐먼트에 "$ne"를 사용한다.
db.papers.updateOne({"authors cited" : {"$ne" : "Richie"}}, {"$push" : {"authors cited" : "Richie"}})
$addToSet
$addToSet을 사용할 수도 있다. $addToSet은 $ne가 작동하지 않을 때나 $addToSet을 사용하면 무슨 일이 일어났는지 더 잘 알 수 있을 때 유용하다.
db.people.insertOne({"username" : "joe", "emails" : ["joe1@example.com", "joe2@example.com","joe3@example.com"]})
# update same data
db.people.updateOne({"_id" : ObjectId("606d7ac6eff0581991d9a374")},
{"$addToSet" : {"emails" : "joe1@example.com"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
# update difference data
db.people.updateOne({"_id" : ObjectId("606d7ac6eff0581991d9a374")},
{"$addToSet" : {"emails" : "joe4@example.com"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
db.people.find()
{ "_id" : ObjectId("606d7ac6eff0581991d9a374"), "username" : "joe", "emails" : [ "joe1@example.com", "joe2@example.com", "joe3@example.com", "joe4@example.com" ] }
고유한 값을 여러 개 추가하려면 $addToSet과 $each를 결합해 사용한다. $ne/$push 조합으로는 할 수 없는 작업니다.
db.people.updateOne({"_id" : ObjectId("606d7ac6eff0581991d9a374")},
{"$addToSet" : {"emails" : {"$each" :
["joe5@example.com", "joe6@example.com", "joe7@example.com"]}}})
{ "_id" : ObjectId("606d7ac6eff0581991d9a374"), "username" : "joe", "emails" : [ "joe1@example.com", "joe2@example.com", "joe3@example.com", "joe4@example.com", "joe5@example.com", "joe6@example.com", "joe7@example.com" ] }
요소 제거
- $pop
- 배열을 큐나 스택처럼 사용하려면 배열의 양쪽 끝에서 요소를 제거한다.
- {"$pop" : {"key" : 1}} -> 마지막부터 요소 제거
- {"$pop" : {"key" : 1}} -> 처음부터 요소 제거
- $pull : 주어진 조건에 맞는 배열 요소를 제거
db.lists.insertOne({"todo" : ["dishes", "laundry", "dry cleaning"]})
db.lists.updateOne({}, {"$pull" : {"todo" : "laundry"}})
db.lists.find()
{ "_id" : ObjectId("606e8c5c7b290c0589af6c9b"), "todo" : [ "dishes", "dry cleaning" ] }
배열의 위치 기반 변경
db.blogs.posts.insertOne(
{
"content" : "...",
"comments" : [
{"comment" : "good!", "author" : "John", "votes" : 0},
{"comment" : "too short!", "author" : "Claire", "votes" : 3},
{"comment" : "free", "author" : "Alice", "votes" : -5},
{"comment" : "vacation", "author" : "Lynn", "votes" : -7},
]
}
)
db.blogs.posts.updateOne({"content" : "..."}, {"$inc" : {"comments.0.votes" : 1}})
{
"_id" : ObjectId("606e90a97b290c0589af6c9d"),
"content" : "...",
"comments" : [
{
"comment" : "good!",
"author" : "John",
"votes" : 1
},
{
"comment" : "too short!",
"author" : "Claire",
"votes" : 3
},
{
"comment" : "free",
"author" : "Alice",
"votes" : -5
},
{
"comment" : "vacation",
"author" : "Lynn",
"votes" : -7
}
]
}
db.blogs.posts.updateOne({"comments.author" : "John"}, {"$set" : {"comments.$.author" : "Jim"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
db.blogs.posts.find().pretty()
{
"_id" : ObjectId("606e90a97b290c0589af6c9d"),
"content" : "...",
"comments" : [
{
"comment" : "good!",
"author" : "Jim",
"votes" : 1
},
{
"comment" : "too short!",
"author" : "Claire",
"votes" : 3
},
{
"comment" : "free",
"author" : "Alice",
"votes" : -5
},
{
"comment" : "vacation",
"author" : "Lynn",
"votes" : -7
}
]
}
배열 필터를 이용한 갱신
db.blogs.posts.updateOne(
... {"content" : "..."},
... {"$set" : {"comments.$[elem].hidden" : true}},
... {
... arrayFilters: [
... {"elem.votes" : { $lte : -5 } }
... ]
... }
... )
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.blogs.posts.find().pretty()
{
"_id" : ObjectId("606e90a97b290c0589af6c9d"),
"content" : "...",
"comments" : [
{
"comment" : "good!",
"author" : "Jim",
"votes" : 1
},
{
"comment" : "too short!",
"author" : "Claire",
"votes" : 3
},
{
"comment" : "free",
"author" : "Alice",
"votes" : -5,
"hidden" : true
},
{
"comment" : "vacation",
"author" : "Lynn",
"votes" : -7,
"hidden" : true
}
]
}
갱신 입력
갱신 입력은 특수한 형태를 갖는 갱신이다. 갱신 조건에 맞는 도큐먼트가 존재하지 않을 때는 쿼리 ㅋ도큐먼트와 갱신 도큐먼트를 합쳐서 새로운 도큐먼트를 생성한다. 조건에 맞는 도큐먼트가 발견되면 일반적인 갱신을 수행한다.
# code...
# 이 페이지에 대한 항목이 있는지 확인
blog = db.analytics.findOne({"url" : "/blog"})
# 항목이 있으면 조회 수에 1을 더하고 저장
if (blog) {
blog.pageviews++
db.analytics.save(blog)
}
# 항목이 없으면 이 페이지에 대한 새로운 도큐먼트 생성
else {
db.analytics.insertOne({"url" : "/blog", "pageviews" : 1})
}
# 갱신 입력 사용
# 첫방문
db.analytics.updateOne({"url" : "/blog"}, {"$inc" : {"pageviews" : 1}}, {"upsert" : true})
db.analytics.find()
{ "_id" : ObjectId("606ea1c10409afa4cebe978c"), "url" : "/blog", "pageviews" : 1 }
# 2회 방문
db.analytics.updateOne({"url" : "/blog"}, {"$inc" : {"pageviews" : 1}}, {"upsert" : true})
db.analytics.find()
{ "_id" : ObjectId("606d64c3290897f1f912c15f"), "url" : "www.example.com", "pageviews" : 56 }
{ "_id" : ObjectId("606ea1c10409afa4cebe978c"), "url" : "/blog", "pageviews" : 2 }
$setOnInsert
"$setOnInsert" insert 할때만 세팅
> db.userss.updateOne({}, {"$setOnInsert" : {"createAt" : new Date()}}, {"upsert" : true})
{
"acknowledged" : true,
"matchedCount" : 0,
"modifiedCount" : 0,
"upsertedId" : ObjectId("606ea3040409afa4cebe97a1")
}
> db.userss.find()
{ "_id" : ObjectId("606ea3040409afa4cebe97a1"), "createAt" : ISODate("2021-04-08T06:30:28.631Z") }
> db.userss.updateOne({}, {"$setOnInsert" : {"createAt" : new Date()}}, {"upsert" : true})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
> db.userss.find()
{ "_id" : ObjectId("606ea3040409afa4cebe97a1"), "createAt" : ISODate("2021-04-08T06:30:28.631Z") }
저장 셀 보조자
- save는 도큐먼트가 존재하지 않으면 도큐먼트를 삽입하고, 존재하면 도큐먼트를 갱신하게 하는 셀 함수!
- 셀 함수는 매개변수가 하나이고 도큐먼트를 넘겨받는다.
- 도큐먼트가 "_id" 키를 포함하면 save는 갱신 입력을 실행하고 포함하지 않으면 삽입을 실행.
> db.testcalls.insertOne({"aaaa" : "..."})
{
"acknowledged" : true,
"insertedId" : ObjectId("606ea810db3450d20a0cb7a2")
}
> db.testcalls.findOne()
{ "_id" : ObjectId("606ea810db3450d20a0cb7a2"), "aaaa" : "..." }
> var x = db.testcalls.findOne()
> x.num = 42
42
> db.testcalls.save(x)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.testcalls.find()
{ "_id" : ObjectId("606ea810db3450d20a0cb7a2"), "aaaa" : "...", "num" : 42 }
다중 도큐먼트 갱신
다중 스키마를 변경하거나 특정 타겟에게 새로운 정보를 추가할 때는 updateMany를 사용하자!
db.testc.insertMany(
[
{"birthday": "10/13/1978"},
{"birthday": "10/13/1978"},
{"birthday": "10/13/1978"}
]
)
> db.testc.updateMany({"birthday" : "10/13/1978"}, {"$set" : {"gift" : "Happy Bir.."}})
{ "acknowledged" : true, "matchedCount" : 3, "modifiedCount" : 3 }
> db.testc.find()
{ "_id" : ObjectId("606ea9a0db3450d20a0cb7a3"), "birthday" : "10/13/1978", "gift" : "Happy Bir.." }
{ "_id" : ObjectId("606ea9a0db3450d20a0cb7a4"), "birthday" : "10/13/1978", "gift" : "Happy Bir.." }
{ "_id" : ObjectId("606ea9a0db3450d20a0cb7a5"), "birthday" : "10/13/1978", "gift" : "Happy Bir.." }
갱신한 도큐먼트 반환
예시)
status가 READY, RUNNING, DONE이 있다. 우선순위가 가장 높은 Status = READY를 찾아 프로세스를 실행하고 RUNNING을 걸쳐 DONE으로 가야한다.
한 Step씩 밟아보자
var cursor = db.processes.find({"status" : "READY"});
ps = cursor.sort({"priority" : -1}).limit(1).next(); // 1)
db.processes.updateOne({"_id" : ps.id}, ("$set" : {"status" : "RUNNING"}});
do_something(ps)
db.processes.updateOne({"_id" : ps._id}, {"$set" : {"status" : "DONE"}})
결론적으로 좋치 못한 소스이다.
find로 여러개를 조회하면 여러개의 스레드가 생성된다.
1) 에서 1번 스레드가 먼저 도큐먼트를 얻고 status를 RUNNING으로 갱신하기 전에 다시 1)에서 2번 스레드가 같은 도큐먼트를 받으면 받으면 두 스레드는 같은 프로세스를 실행하게 된다.
var cursor = db.processes.find({"status" : "READY"})
cursor.sort({"priority" : -1}).limit(1)
while((ps = sursor.next()) != null) {
var result = db.processes.updateOne({"_id" : ps._id, "status" : "READY"}, {"$set" : {status" : "RUNNING"}})
if (result.modifiedCount === 1) {
do_somethind(ps)
db.processes.updateOne({"_id": ps._id}, {"$set" : {"status" : "DONE"}})
break;
}
cursor = db.processes.find({"status" : "READY"})
cursor.sort({"priority" : -1}).limit(1)
}
이 코드는 동일한 프로세스를 처리하진 않지만 한 스레드가 모든 일을 하고 다른 스레드는 노는 경우가 발생할 수 있다.
findOneAndUpdate()
db.processes.findOneAndUpdate({"status" : "READY"},
{"$set" : {"status" : "RUNNING"}},
{"$sort" : {"priority" : -1}})
# Result
{
"_id" : ObjectId("....ddafd"),
"priority" : 1,
"status" : "READY"
}
findOneAndUpdate 메서드는 도큐먼트의 상태를 수정하기 전에 반환한다. 만약 상태값이 변경된 이후의 값을 얻고 싶다면 returnDocument를 사용하자
db.processes.findOneAndUpdate({"status" : "READY"},
{"$set" : {"status" : "RUNNING"}},
{"$sort" : {"priority" : -1}, "returnNewDocument" : true})
// Result
{
"_id" : ObjectId("....ddafd"),
"priority" : 1,
"status" : "RUNNING"
}
최종 코드는 아래와 같다
db.processes.findOneAndUpdate({"status" : "READY"},
{"$set" : {"status" : "RUNNING"}},
{"$sort" : {"priority" : -1}, "returnNewDocument" : true})
do_something(ps)
db.processes.updateOne({"_id": ps._id}, {"$set" : {"status" : "DONE"}})
이외에도 findOneAndReplace는 동일한 매개변수를 사용하며 returnNewDocument의 값에 따라 교체 전이나 후에 필터와 일치하는 도큐먼트를 반환한다.
findOneAndDelete도 유사하지만 갱신 도큐먼트를 매개변수로 사용하지 않으며 다른 두 메서드의 옵션을 부분적으로 가진다. findOneAndDelete는 삭제된 도큐먼트를 반환한다.
'Study' 카테고리의 다른 글
[데이터 중심 어플리케이션 설계] 트랜잭션 (1) | 2022.05.12 |
---|---|
대규모 시스템 설계 - 채팅 시스템 설계 (1) | 2021.12.07 |