[Ch3] 도큐먼트 생성, 갱신, 삭제 (갱신 2)
2022. 7. 13. 13:02ㆍ책/몽고DB 완벽 가이드
3.2.2. Array Operator
◎ $push
{ $push: { <field1>: <value1>, ... } } |
- 배열에 요소를 추가한다.
- 배열이 이미 존재하면 배열 끝에 요소를 추가하고, 존재하지 않으면 새로운 배열을 생성한다.
- Array.prototype.push랑 비슷한 느낌으로 돌아간다고 보면 됨.
블로그 게시물에 누가 댓글을 달면 DB에 반영해보자
let firstPost = db.blog.posts.findOne({ "title": "첫 번째 게시글" });
firstPost;
{
"_id" : ObjectId("62cd55dae195d477e64034df"),
"title" : "첫 번째 게시글",
"content" : "야한 생각 중입니다"
}
db.blog.posts.updateOne(firstPost, {
"$push": { "comments" : { "name" : "joe", "content": "ㅁㅊ놈아ㅋㅋㅋㅋ" }}}
);
db.blog.posts.find().pretty();
{
"_id" : ObjectId("62cd55dae195d477e64034df"),
"title" : "첫 번째 게시글",
"content" : "야한 생각 중입니다",
"comments" : [ { "name" : "joe", "content" : "ㅁㅊ놈아ㅋㅋㅋㅋ"} ]
}
위 예시에선 단순히 element를 추가하는 형태지만 더 복잡한 배열 기능에도 사용 가능하며, 이때 제한자 Modifier를 사용한다.
◎ $addToSet
{ $addToSet: { <field1>: <value1>, ... } } |
- 배열에 값을 넣을 때, 해당 값이 이미 배열에 없는 경우에만 삽입해준다
- 아래 $push + $ne 조합보다 더 많은 일을 할 수 있다
db.people
{ "name": "김씨", "friends": [ "나씨", "박씨", "이씨" ] }
db.people.updateOne({ name: "김씨" }, {
$addToSet : { friends :
{ $each : [ "박씨", "이씨", "연씨", "운씨", "서씨" ] }
}
});
// 기존에 있던 "박씨", "이씨"는 추가되지 않는다
{ "name": "김씨", "friends": [ "나씨", "박씨", "이씨", "연씨", "운씨", "서씨" ] }
◎ $pop
{ $pop: { <field>: <-1 | 1>, ... } } |
- 배열의 첫 번째 or 마지막 요소를 제거한다
- 배열을 스택이나 큐로 사용할 때 쓰면 좋음
- -1이면 첫 번째, 1이면 마지막 요소를 제거
db.students
{ { name: "김", score: [ 80, 70, 66, 96 ] }
맨 앞의 80점을 지워보자
db.students.updateOne({ name : "김", {
$pop : { score : -1 }
});
{ { name: "김", score: [ 70, 66, 96 ] }
◎ $pull
{ $pull: { <field1>: <value | condition>, <field2>: <value | condition>, ... } } |
- 조건에 맞는 모든 요소를 제거한다
- 특정 값 제거뿐만 아니라 특정 조건에 맞는 도큐먼트도 제거할 수 있다(in nested document)
db.lists
{ date: "2022-07-13", todo: [ "빨래", "청소", "설거지", "공부", "데이트" ] }
데이트를 한다니 괘씸하니 없애보자
db.lists.updateOne({ date: "2022-07-13", {
$pull : { todo : "데이트" }
});
{ date: "2022-07-13", todo: [ "빨래", "청소", "설거지", "공부" ] }
착해졌다
◎ $ (위치 연산자positional operator)
{ "<array>.$" : value } |
- 쿼리 도큐먼트와 일치하는 배열 요소 및 요소의 위치를 알아낸다
- 배열의 인덱스를 명시적으로 입력하지 않아도 해당 요소를 찾아내기 위해 쓴다
- 대충 Array.prototype.findIndex 를 끼얹은 느낌이라고 봐도 된다
- 만약 일치하는 요소가 여러개면 첫 번째 요소만 반환한다
db.blog.posts
{ id: 288, content : "...", comments : [
{ author: "ㅇㅇ", comment: "ㅄ", vote: 0 }
{ author: "ㄴㄴ", comment: "어휴", vote: 3 }
{ author: "ㅂㅂ", comment: "뭐함?", vote: -5 }
{ author: "ㅅㅅ", comment: "ㅉㅉ", vote: -9 }
]}
여기서 "ㅂㅂ"가 쓴 댓글을 수정하고 싶다면 어떻게 할까?
JS관점으로 보면 아래처럼 수정할 수 있을 것이다.
posts[2].comment = "뭐함 ㅄ아"
그러나 만약 요소가 100만개쯤 된다고 치면
DB에서 일일이 쿼리해서 "ㄴㄴ"가 몇 번째 위치에 있는지 찾을거임?
이런 불상사를 막기 위해 $ 가 존재한다.
db.blog.updateOne({ "comments.author" : "ㄴㄴ" }, {
$set : { "comment.$.comment" : "뭐함 ㅄ아" }
});
◎ $[ <identifier > ]
- arrayFilter 조건에 맞는 요소들에 일시적으로 이름을 달아 식별하기 위해 쓴다
- 아래 표현식에서 배열의 필터 결괏값에 lessThanFive라는 변수명을 달아주는 역할과 비슷하다고 보면 된다.
const lessThanFive = Array.filter( val => val <= 5 );
◎ arrayFilters
- 개별 배열 요소를 갱신하기 위한 필터
- 특정 조건에 맞는 모든 배열 요소를 갱신할 수 있다
db.blog.posts
{ id: 288, content : "...", comments : [
{ author: "ㅇㅇ", comment: "ㅄ", vote: 0 }
{ author: "ㄴㄴ", comment: "어휴", vote: 3 }
{ author: "ㅂㅂ", comment: "뭐함?", vote: -5 }
{ author: "ㅅㅅ", comment: "ㅉㅉ", vote: -9 }
]}
여기서 vote ≤ -5 인 댓글은 경고를 줘보자
db.blog.posts.updateOne({ id: 288 },
{ $set : { "comments.$[elem].warning" : true } },
{ arrayFilters : [{ "elem.votes" : { $lte : -5 }] },
);
JS로 표현하자면 대충 아래와 비슷한 구문이다
Array
.filter( comment => comment.vote <= -5)
.forEach( comment => comment.warning = true )
3.2.3. Modifiers
◎ $each
{ $push: { <field>: { $each: [ <value1>, <value2> ... ] } } } |
- $addToSet 연산자, $push 연산자와 함께 사용 가능한 제한자
- 요소 여러개를 한번에 추가할 때 사용한다.
- 한 번에 여러개의 제한자를 동시 사용 가능하다.
예를 들어, 날짜별로 지각생들을 관리하는 컬렉션이 있다고 치자.
2022-07-19 날짜에 "지각생 번호" : [18, 28, 38] 라는 필드를 추가하려면?
db.students.late.insertOne({ "date" : "2022-07-19" });
db.students.late.updateOne({ "date" : "2022-07-19"}, {
"$push" : { "지각생 번호" : { "$each" : [18, 28, 38] } }
});
db.students.late.find().pretty()
{ "_id" : ObjectId("62cd67a0e195d477e64034e5"),
"date" : "2022-07-19",
"지각생 번호" : [ 18, 28, 38 ] }
◎ $slice
{ $push: { <field> : { $each: [ <value1>, <value2>, ... ], $slice: <num> } } } |
- $push 연산자를 사용하며 배열의 요소 갯수를 제한하는 용도로 사용된다
- $slice 단독으론 사용할 수 없고, 반드시 $each 연산자와 함께 호출되어야 한다
- 만약 $each에 할당할 게 따로 없다면 [ ]로 빈 배열을 전달하면 된다
- <num> 는 ± 부호를 구분하여 작성한다
ex) -10 : 20개 추가시 뒷 쪽부터 10개만 추려서 배열에 추가한다
eX) +10 : 20개 추가시 앞 쪽부터 10개만 추려서 배열에 추가한다
db.students
{ "_id": 1, "scores": [ 40, 50, 60 ] }
db.students.updateOne({ _id: 1 }, {
$push : { scores: { $each: [ 80, 78, 86 ], $slice: -5 } }
})
{ "_id": 1, "scores" : [ 50, 60, 80, 78, 86 ] }
// slice가 -5로 설정돼있고,
// 배열의 요소가 5개를 초과하는 경우
// 뒷 부분부터 추려서 남기므로
// 맨 앞에 있던 40이 제외됐다
◎ $sort
{ $push: { <field>: { $each: [ <value1>, <value2>, ... ], $sort: <sort specification> } } } |
- $push 연산자와 사용하며, 배열의 요소를 정렬하는데 사용한다
- <sort specification> 에서 정렬할 기준이 되는 필드와 오름차순(1)/내림차순(-1)을 지정한다
- $each 연산자와 반드시 함께 호출되어야 한다
- 만약 $each에 할당할 게 따로 없다면 [ ]로 빈 배열을 전달하면 된다
예를 들어 아래와 같은 컬렉션을 score 오름차순 정리하고 싶다면...
db.students
{ "_id" : 1, "quiz" : [
{ "id" : 1, "score" : 6 },
{ "id" : 2, "score" : 9 },
{ "id" : 3, "score" : 8 },
{ "id" : 4, "score" : 7 },
] }
딱히 추가할 건 없고 배열만 정렬하면 되니까
$each에 빈 배열[]을 넣어주고 $sort를 호출하면 된다
db.students.updateOne({ "_id": 1}, {
$push : { quiz: { $each: [], $sort: { score: 1 } } }
});
{ "_id" : 1, "quiz" : [
{ "id" : 1, "score" : 6 },
{ "id" : 4, "score" : 7 },
{ "id" : 3, "score" : 8 },
{ "id" : 2, "score" : 9 }
]}
3.2.4. Comparison Query Operators
◎ $ne
{ field: { $ne: value } } |
- 필드 값이 주어진 값과 비교하여 일치하지 않을 때(not equal) 선택한다
- 배열 연산자와 함께 쓰면 배열을 집합(Set)처럼 다룰 수 있다
db.people
{ "name": "김씨", "friends": [ "나씨", "박씨", "이씨" ] }
{ "name": "나씨", "friends": [ "김씨", "박씨" ] }
{ "name": "박씨", "friends": [ "김씨", "나씨" ] }
"이씨"랑 친구가 아닌 사람들을 찾아서
"이씨"를 친구목록에 추가해주자
db.people.updateMany(
{ friends : { $ne : "이씨" } },
{ $push : { friends : "이씨" } }
);
'책 > 몽고DB 완벽 가이드' 카테고리의 다른 글
[4.2] 쿼리 / 쿼리조건(쿼리 연산자) (0) | 2022.07.18 |
---|---|
[4.1] 쿼리 / find (0) | 2022.07.18 |
[Ch3] 도큐먼트 생성, 갱신, 삭제 (갱신 1) (0) | 2022.07.12 |
[Ch3] 도큐먼트 생성, 갱신, 삭제 (생성&삭제) (0) | 2022.07.12 |
[Ch2] 몽고DB 기본(2/3) (0) | 2022.07.11 |