MongoDB의 aggregate()에서 조건에 따라 중첩 배열을 필터링하려면 $filter와 $cond 연산자를 사용할 수 있습니다. $filter는 지정된 조건을 만족하는 요소만 골라서 배열의 부분 집합을 반환하는 연산자입니다.
이 글에서는 실제 예제를 통해 문서 생성부터 중첩 배열 필터링까지 단계별로 살펴보겠습니다.
1. 샘플 컬렉션 생성하기
먼저 insertOne() 메서드를 사용해 중첩 배열 구조를 가진 문서를 포함한 컬렉션을 생성합니다.
> db.demo725.insertOne(
... {
... "details": {
... "userMessages": [
... {
... "Messages": [
... { "Message": "Hello" },
... { "Message": "How" },
... { "Message": "are" }
... ]
... },
... {
... "Messages": [
... { "Message": "Good" },
... { "Message": "Bye" }
... ]
... },
... {
... "Messages": [
... { "Message": "Hello" },
... { "Message": "Bye" }
... ]
... }
... ]
... }
... }
... );
{
"acknowledged" : true,
"insertedId" : ObjectId("5eab16cd43417811278f5893")
}2. 저장된 문서 확인하기
find() 메서드를 사용하면 컬렉션의 모든 문서를 조회할 수 있습니다.
> db.demo725.find();
위 쿼리를 실행하면 다음과 같은 결과가 출력됩니다.
{ "_id" : ObjectId("5eab16cd43417811278f5893"), "details" : { "userMessages" : [ { "Messages" : [ { "Message" : "Hello" }, { "Message" : "How" }, { "Message" : "are" } ] }, { "Messages" : [ { "Message" : "Good" }, { "Message" : "Bye" } ] }, { "Messages" : [ { "Message" : "Hello" }, { "Message" : "Bye" } ] } ] } }3. 중첩 배열 필터링 쿼리 작성하기
이제 핵심인 중첩 배열 필터링 쿼리입니다. 아래 쿼리는 $addFields, $filter, $map, $anyElementTrue, $indexOfBytes를 조합하여 각 userMessages 요소 중 내부 메시지 배열에 "Hello"가 포함된 것만 남깁니다.
> db.demo725.aggregate([
... {
... $addFields: {
... "details.userMessages": {
... $filter: {
... input: "$details.userMessages",
... as: "out",
... cond: {
... $anyElementTrue: {
... $map: {
... input: "$$out.Messages",
... in: { $gte: [ { $indexOfBytes: [ "$$this.Message", "Hello" ] }, 0 ] }
... }
... }
... }
... }
... }
... }
... }
... ]).pretty()쿼리 동작 원리
- $addFields: 기존 필드
details.userMessages를 새로 계산된 값으로 대체합니다. - $filter: 외부 배열
userMessages의 각 요소($$out)에 대해 조건을 평가합니다. - $map + $indexOfBytes: 내부
Messages배열의 각 메시지에서 "Hello" 문자열의 위치를 찾습니다. 위치가 0 이상이면 해당 메시지에 "Hello"가 포함된 것입니다. - $anyElementTrue: 내부 메시지 중 하나라도 조건을 만족하면 해당 요소를 최종 결과에 유지합니다.
4. 필터링 결과 확인하기
위 쿼리를 실행하면 다음과 같은 결과가 출력됩니다.
{
"_id" : ObjectId("5eab16cd43417811278f5893"),
"details" : {
"userMessages" : [
{
"Messages" : [
{
"Message" : "Hello"
},
{
"Message" : "How"
},
{
"Message" : "are"
}
]
},
{
"Messages" : [
{
"Message" : "Hello"
},
{
"Message" : "Bye"
}
]
}
]
}
}결과를 보면 원래 3개였던 userMessages 요소 중 "Good"과 "Bye"만 포함된 두 번째 요소가 제거된 것을 확인할 수 있습니다. 즉, 내부 메시지 배열에 "Hello"가 포함된 요소만 성공적으로 필터링되어 반환되었습니다.
마무리
이처럼 MongoDB에서는 $filter와 $cond(그리고 $map, $anyElementTrue 같은 보조 연산자)를 조합하면 복잡한 중첩 배열 구조도 원하는 조건에 따라 손쉽게 필터링할 수 있습니다. 특히 배열 안에 배열이 있는 형태의 데이터를 다룰 때 매우 유용한 패턴이므로 꼭 익혀두시기 바랍니다.