aggregate()로 조건 일치 객체 필터링하기
중첩 배열의 모든 요소가 특정 조건과 일치하는 객체를 필터링하려면 MongoDB의 aggregate() 메서드를 사용해야 합니다. 먼저 예제에 사용할 문서들이 담긴 컬렉션을 생성해 보겠습니다.
> db.demo418.insertOne(
... {
... "details":[
... {
... "CountryName":"US",
... "Marks":45
... },
... {
... "CountryName":"US",
... "Marks":56
... },
...
... ],
... "Name":"John"
... }
... );
{
"acknowledged" : true,
"insertedId" : ObjectId("5e724324b912067e57771ae6")
}
> db.demo418.insertOne(
... {
... "details":[
... {
... "CountryName":"US",
... "Marks":78
... },
... {
... "CountryName":"UK",
... "Marks":97
... },
...
... ],
... "Name":"Mike"
... }
... );
{
"acknowledged" : true,
"insertedId" : ObjectId("5e724325b912067e57771ae7")
}
위 코드는 두 개의 문서를 생성합니다. 첫 번째 문서(John)의 details 배열에는 CountryName이 모두 "US"인 요소 두 개가 있고, 두 번째 문서(Mike)의 배열에는 "US"와 "UK"가 섞여 있어 이후 필터링 결과를 비교하기 좋은 구조입니다.
find()로 전체 문서 확인하기
find() 메서드를 사용하면 컬렉션에 저장된 모든 문서를 확인할 수 있습니다.
> db.demo418.find();
실행 결과는 다음과 같습니다.
{ "_id" : ObjectId("5e724324b912067e57771ae6"), "details" : [ { "CountryName" : "US", "Marks" : 45 }, { "CountryName" : "US", "Marks" : 56 } ], "Name" : "John" }
{ "_id" : ObjectId("5e724325b912067e57771ae7"), "details" : [ { "CountryName" : "US", "Marks" : 78 }, { "CountryName" : "UK", "Marks" : 97 } ], "Name" : "Mike" }
배열의 모든 요소가 조건과 일치하는 문서 필터링 쿼리
다음은 중첩 배열(details)의 모든 요소가 조건(CountryName이 "US")과 일치하는 문서만 걸러내는 집계 쿼리입니다.
> db.demo418.aggregate([
... {$unwind:"$details"},
... { $group: {
... _id: '$_id',
... details : { $first: '$details' },
... alldetails: { $sum: 1 },
... alldetailsmatch: { $sum: { $cond: [ { $eq: [ '$details.CountryName', "US" ] }, 1, 0 ] } },
... Name: { $first: '$Name' }
... }},
... { $project: {
... _id: 1,
... Name: 1,
... details: 1,
... arrayValue: { $cond: [ { $eq: [ '$alldetails', '$alldetailsmatch' ] }, 1, 0 ] }
... }},
... { $match: { 'arrayValue' : 1 } }
... ])
쿼리를 실행하면 다음과 같은 결과가 출력됩니다.
{ "_id" : ObjectId("5e724324b912067e57771ae6"), "details" : { "CountryName" : "US", "Marks" : 45 }, "Name" : "John", "arrayValue" : 1 }
쿼리 동작 원리
이 집계 파이프라인은 다음 단계를 거쳐 동작합니다.
- $unwind:
details배열을 개별 문서 단위로 분해합니다. - $group:
_id를 기준으로 재그룹화하면서 전체 배열 요소 수(alldetails)와CountryName이 "US"인 요소 수(alldetailsmatch)를 각각 집계합니다. - $project: 두 값이 서로 같으면
arrayValue를 1로, 하나라도 다르면 0으로 설정합니다. - $match:
arrayValue가 1인 문서, 즉 배열의 모든 요소가 조건을 충족하는 문서만 최종 결과로 출력합니다.
결과적으로 "UK"인 요소를 하나라도 포함하고 있는 Mike의 문서는 제외되고, 배열 내 모든 국가명이 "US"인 John의 문서만 반환됩니다. 이처럼 $unwind, $group, $cond를 조합하면 중첩 배열 전체가 특정 조건을 만족하는지 손쉽게 검증할 수 있습니다.