[aggregate] 서로 다른 collection의 child element 조건으로 lookup하기

2021. 3. 3. 18:39프로그래밍-Web/MongoDB

콜렉션 A(CBS)

 

{

_id: ObjectId('아이디')

itemList: [ { _id: ObjectId('아이디'), itemId:ObjectId('아이디'), quantity: 0} ....]

}....

 

콜렉션 B(Zone)

 

{

_id: ObjectId('아이디')

itemList: [ { _id: ObjectId('아이디'), itemId:ObjectId('아이디'), quantity: number }.... ]

}....

 

 

약간 까다로운 lookup상황을 맞이했다.

위와 같은 두개의 콜렉션이 존재할때, A 컬렉션의 모든 itemList의 itemId를 조회하고

그것과 일치하는 itemId를 가진 콜렉션B의 itemList들을 조회한다.

콜렉션 A의 itemId는 unique하지만, 콜렉션B의 itemId는 제한이 없다.

이 상황에서 콜렉션A의 quantity를, 콜렉션 B의 일치하는 모든 element의 quantity 합으로 업데이트 해줘야하는 상황이다.

 

그림으로 표현하면 이러한 상황이다.

즉, nested한 Array를 순회하면서 다른 콜렉션에 위치한 일치하는 element를 찾고, 해당 데이터들을 연산하여 업데이트 해주는 것이다.

 

lookup구문을 사용하다보면 느끼는게, 최대한 unwind를 많이 해주는것이 초심자에겐 이해하기 편하다는 것이다.

match operator를 이요해 해당하는 CBS 컬렉션을 찾았고, 이를 Target이 되는 zone 컬렉션과 lookup한다.

이때 itemId가 일치해야 하므로, 해당하는 필드를 각각 local, foreign 필드로 설정한다.

 

이대로 실행하면 도큐먼트는 itemList와 result에 엄청 많은 object를 리턴할것이다.

이때 무턱대로 모든 list를 unwind하면 안된다.

나는 CBS의 itemList를 중심으로 loop를 돌릴것이므로, itemList만 unwind..

하려했으나, 아무래도 배열을 모두 풀어 각각의 document로 표현하는게 편하겠다.

 

이후 addFields라는 항목을 통해 test 필드를 추가한다.

이는 filter를 통해 원하는 데이터만 건져내기 위함이다.

filter의 input은 필터링의 Target이 되는 List이다.

cond는 실행 커맨드이고, this는 Target List를 뜻한다.

따라서 Target인 zone의 itemList 배열의 itemId가, CBS의 itemId와 일치하는 document를 찾아낼것이다.

 

 project로 디버깅하기 편하게 만들어준다

(aggregate는 디버깅 어떻게 해야 편할지 모르겠다 ㅠㅠ)

다시한번 test를 unwind해줄건데, 이때 itemId가 일치하는 document가 없는 애들은 모두 없애기위해 옵션을 꺼준다.

 

이후 group 커맨드를 통해 quantity들의 합을 모두 묶어준다.

당연히 unwind를 했기 때문에 sum이 자동으로 순회되는 것이다.