Back-End/MongoDB

[MongoDB] MQL 기초 5 - 배열과 내장 Document를 다루는 방법 2 (Update)

유자맛바나나 2024. 5. 31. 02:12

❑ 공통 데이터 Insert

db.students.insertMany([
	{_id: 1, grades: [85, 80, 80]},
	{_id: 2, grades: [88, 90, 92]},
	{_id: 3, grades: [85, 100, 90]}
])

 

❑ 배열의 값 변경하기 : $set

[참고] $ : Positioning Operator

 

1. Array.$ : 배열 내 조건과 일치하는 첫 번째 element에 적용

1.1. 배열 Element가 Value일 때 Update

AS-IS)

{
	_id: 1,
	grades: [85, 80, 80]
}

MQL)

db.students.updateOne(
	{ _id: 1, grades: 80 }, 
	{ $set: { "grades.$": 82 } }
)
  • _id:1 이고, grades 배열에 값이 80인 첫 번째 element를 82로 변경

TO-BE)

{
	_id: 1,
	grades: [85, 82, 80]
}
  • _id:1, grades:80 인 element는 두 개 가 있었지만 첫 번째 element에만 적용됨

 

1.2. 배열 Element가 내장 Document 일 때 Update

AS-IS)

{
	_id: 1,
	grades: [
			{ grade: 80, mean: 75, std: 8 },
			{ grade: 85, mean: 90, std: 5 },
			{ grade: 85, mean: 85, std: 8 }
		]
}

MQL)

db.students.updateOne(
	{ _id: 1, "grades.grade": 85 }, 
	{ $set: { "grades.$.std": 6 } }
)
  • _id:1 이고, grades 배열의 내장 Document 중 grade 값이 85인 첫 번째 Document를 찾아 std field를 6으로 변경

TO-BE)

{
	_id: 1,
	grades: [
			{ grade: 80, mean: 75, std: 8 },
			{ grade: 85, mean: 90, std: 6 }, # grade: 85 인 첫 번째 내장 Document
			{ grade: 85, mean: 85, std: 8 }
		]
}

 

2. Array.$[] : 배열 내 조건과 일치하는 모든 element에 적용

2.1. 배열 Element가 Value일 때 Update

AS-IS)

{
	_id: 1,
	grades: [85, 80, 80]
}
{
	_id: 2,
	grades: [88, 90, 92]
}

MQL)

db.students.updateMany(
	{},
	{ $inc: { "grades.$[]": 3} }
)
  • 모든 document의 grades 배열의 element를 3씩 증가(inc)시키겠다

TO-BE)

{
	_id: 1,
	grades: [88, 83, 83]
}
{
	_id: 2,
	grades: [91, 93, 95]
}

 

2.2. 배열 Element가 내장 Document 일 때 전체 Document Update

AS-IS)

{
	_id: 1,
	grades: [
			{ grade: 80, mean: 75, std: 8 },
			{ grade: 85, mean: 90, std: 5 },
			{ grade: 85, mean: 85, std: 8 }
		]
}

MQL)

db.students.updateOne(
	{ _id: 1, grades: {$elemMatch: {grade: {$gte: 85}}} }, 
	{ $set: { "grades.$[].grade": 100 } }
)
  • _id:1 이고, grades 배열의 내장 Document 중 grade 값이 85와 같거나 큰 Document가 있다면, 배열의 모든($[]) Document의 grade를 100으로 변경
  • $gte : greater than or eqaul (크거나 같다)

TO-BE)

{
	_id: 1,
	grades: [
			{ grade: 100, mean: 75, std: 8 }, 
			{ grade: 100, mean: 90, std: 5 }, # as-is의 grade 값이 85 이므로 $gte: 85를 만족
			{ grade: 100, mean: 85, std: 8 }  # as-is의 grade 값이 85 이므로 $gte: 85를 만족
		]
}

 

2.3. 배열 Element가 내장 Document 일 때 특정 Document Update - arrayFilters 사용

AS-IS)

{
	_id: 1,
	grades: [
			{ grade: 80, mean: 75, std: 8 },
			{ grade: 85, mean: 90, std: 5 },
			{ grade: 85, mean: 85, std: 8 }
		]
}

MQL)

db.students.updateMany(
	{ _id: 1 }, 
	{ $set: { "grades.$[element].grade": 100 } },
	{ arrayFilters: [{"element.grade": {$gte: 83} }] }
)
  • $[element]에서 element를 identifier라고 함. 일종의 변수로 지정한 것이고, arrayFilters에서 사용하게 됨. element라는 이름 대신 다른 것을 사용해도 상관없음
  • _id:1 이고, arrayFilters에 따라 grades의 내장 Document를 지칭하는 element의 grade field가 83보다 크거나 같은 값을 찾아 100으로 변경한다

TO-BE)

{
	_id: 1,
	grades: [
			{ grade: 80, mean: 75, std: 8 }, 
			{ grade: 100, mean: 90, std: 5 }, # as-is의 grade 값이 85 이므로 $gte: 83를 만족
			{ grade: 100, mean: 85, std: 8 }  # as-is의 grade 값이 85 이므로 $gte: 83를 만족
		]
}

 

 

❑ 배열에 element 추가하기 : $addToSet, $Push

0. $addToSet과 $Push의 차이

$addToSet

  • $addToSet 연산자는 배열에 값을 추가할 때, 해당 값이 배열에 이미 존재하는지 여부를 확인하고 중복을 허용하지 않는다.
  • 만약 배열에 추가하려는 값이 이미 존재한다면, 추가를 하지 않고 기존 값들을 그대로 유지.
  • 배열에 중복된 값을 추가하지 않으며, 값의 유일성을 보장한다.
  • addToSet은 배열 전체를 탐색하므로 시간 복잡도가 높다. 배열의 element가 많을 경우 부하가 클 수 있다.

$push

  • $push 연산자는 배열에 값을 추가할 때, 중복 여부를 확인하지 않고 항상 값을 배열에 추가한다.
  • 새로운 값이 배열에 추가되면 중복되어도 무조건 추가됨.
  • push는 배열의 마지막 index만 보기 때문에 시간 복잡도가 낮다. 따라서 부하가 작다.

 

1. $addToSet

AS-IS 데이터

{
	_id: 1,
	cart: ['banana', 'cheeze', 'milk']
}

 

1.1. 값 타입 element 한 개 추가

MQL)

db.shopping.updateOne(
	{ _id: 1 },
	{ $addToSet: {cart: 'beer'} }
)
  • cart 배열에 ‘beer’ 추가

TOBE)

{
	_id: 1,
	cart: ['banana', 'cheeze', 'milk', 'beer']
}

 

1.2. 배열 타입 element 한 개 추가

MQL)

db.shopping.updateOne(
	{ _id: 1 },
	{ $addToSet: {cart: ['beer', 'candy']} }
)

TOBE)

{
	_id: 1,
	cart: ['banana', 'cheeze', 'milk', ['beer', 'candy']]
}

 

1.3. 값 타입 element 여러 개 추가 - $each 사용

MQL)

db.shopping.updateOne(
	{ _id: 1 },
	{ $addToSet: { cart: { $each: ['beer', 'candy'] }} }
)
  • $each 오퍼레이터를 사용한다

TOBE)

{
	_id: 1,
	cart: ['banana', 'cheeze', 'milk', 'beer', 'candy']
}
  • beer, candy가 값 타입으로 들어감

 

2. $push

AS-IS 데이터

{
	_id: 1,
	cart: ['banana', 'cheeze', 'milk']
}

 

1.1. element 한 개를 마지막에 추가

MQL)

db.shopping.updateOne(
	{ _id: 1 },
	{ $push: {cart: 'beer'} }
)

TOBE)

{
	_id: 1,
	cart: ['banana', 'cheeze', 'milk', 'beer']
}

 

1.2. element 여러 개 마지막에 추가 - $each 사용

MQL)

db.shopping.updateOne(
	{ _id: 1 },
	{ $push: {cart: {$each : ['beer', 'candy']}} }
)

TOBE)

{
	_id: 1,
	cart: ['banana', 'cheeze', 'milk', 'beer', 'candy']
}

 

1.3. element 여러 개를 특정 index에 추가 - $position 사용

MQL)

db.shopping.updateOne(
	{}, # 모든 document에 적용
	{ 
		$push: {
			cart: {
				$each : ['beer', 'candy'],
				$position: 0 # index = 0 번째에 element 추가
			}
		} 
	}
)
  • position: -1은 가장 끝이다

TOBE)

{
	_id: 1,
	cart: ['beer', 'candy', 'banana', 'cheeze', 'milk']
}
  • beer, candy가 0번째 index에 추가됨

 

1.4. 배열의 크기를 유지하며 element를 추가 - $slice 사용

MQL)

db.shopping.updateOne(
	{}, # 모든 document에 적용
	{ 
		$push: {
			cart: {
				$each: ['beer', 'candy'],
				$position: 0,
				$slice: 4 # 배열의 크기를 4개로 유지
			}
		} 
	}
)

TOBE)

{
	_id: 1,
	cart: ['beer', 'candy', 'banana', 'cheeze']
}
  • beer, candy가 0번째 index에 추가됨
  • 배열의 크기를 4개로 유지해야 하므로 가장 마지막 element였던 milk가 제거됨

 

❑  배열에서 element 제거하기 : $pull, $pop

0. $pull과 $pop의 차이

  • $pull은 $addToSet과 같이 배열 전체를 탐색하므로 시간 복잡도가 높고, 배열의 element가 많을 경우 부하가 높을 수 있다.
  • $pop은 $push와 같이 index의 가장 마지막만 보기 때문에 시간 복잡도나 낮다.

 

1. $pull

AS-IS 데이터

{
	_id: 1,
	cart: ['banana', 'cheeze', 'milk', ['beer', 'candy']]
}

 

1.1. element 한 개 제거

MQL)

db.shopping.updateOne(
	{ _id: 1 },
	{ $pull: {cart: 'banana'} }
)

TOBE)

{
	_id: 1,
	cart: ['cheeze', 'milk', ['beer', 'candy']]
}

 

1.2. element 여러 개 제거 - $in 사용

MQL)

db.shopping.updateOne(
	{ _id: 1 },
	{ $pull: {cart: {$in: [['beer', 'candy'], 'banana'] }} }
}

TOBE)

{
	_id: 1,
	cart: ['cheeze', 'milk']
}

 

2. $pop

AS-IS 데이터

{
	_id: 1,
	cart: ['banana', 'cheeze', 'milk'],
	coupon: ['10%', '20%', '30%']
}

 

2.1. 한 개의 배열에서 element 삭제

MQL)

db.shopping.updateOne(
	{ _id: 1 },
	{ $pop: {cart: -1} }
}
  • -1 : 배열의 첫 번째 element 삭제
  • cart 배열의 첫 번째 element를 삭제한다

TOBE)

{
	_id: 1,
	cart: ['cheeze', 'milk'], # 'banana' 삭제
	coupon: ['10%', '20%', '30%']
}

 

2.2. 두 개의 배열에서 element 삭제

MQL)

db.shopping.updateOne(
	{ _id: 1 },
	{ $pop: {cart: -1, coupon: 1} }
}
  • -1 : 배열의 첫 번째 element 삭제
  • 1: 배열의 마지막 element 삭제
  • cart 배열의 첫 번째, coupon 배열의 마지막 element를 삭제한다

TOBE)

{
	_id: 1,
	cart: ['cheeze', 'milk'], # 'banana' 삭제
	coupon: ['10%', '20%'] # '30%' 삭제
}

-1 : 배열의 첫 번째 element 삭제

1: 배열의 마지막 element 삭제