MongoDB 내장 문서만 조회할 수 있을까?
결론부터 말하면, 기본적인 find() 메서드로는 배열 안의 특정 내장(embedded) 문서만 골라서 반환하는 것이 불가능합니다. 조건에 일치하는 요소가 하나뿐이더라도 MongoDB는 해당 조건을 만족하는 문서 전체를 반환하기 때문입니다.
이번 글에서는 실제 예제를 통해 이러한 동작 방식을 확인해 보겠습니다.
1. 테스트용 컬렉션 생성
먼저 내장 문서 배열을 포함하는 컬렉션을 생성합니다. 사용자 게시글 정보(PostDetails)를 배열 형태로 저장하는 예제입니다.
> db.queryToEmbeddedDocument.insertOne({
"UserName": "Larry",
"PostDetails": [
{ "UserMessage": "Hello", "UserLikes": 8 },
{ "UserMessage": "Hi", "UserLikes": 6 },
{ "UserMessage": "Good Morning", "UserLikes": 12 },
{ "UserMessage": "Awesome", "UserLikes": 4 }
]
});
{
"acknowledged" : true,
"insertedId" : ObjectId("5c988a9f330fd0aa0d2fe4bd")
}2. 컬렉션의 모든 문서 조회하기
find() 메서드를 사용하면 컬렉션에 저장된 모든 문서를 확인할 수 있습니다.
> db.queryToEmbeddedDocument.find().pretty();
위 쿼리의 실행 결과는 다음과 같습니다.
{
"_id" : ObjectId("5c988a9f330fd0aa0d2fe4bd"),
"UserName" : "Larry",
"PostDetails" : [
{
"UserMessage" : "Hello",
"UserLikes" : 8
},
{
"UserMessage" : "Hi",
"UserLikes" : 6
},
{
"UserMessage" : "Good Morning",
"UserLikes" : 12
},
{
"UserMessage" : "Awesome",
"UserLikes" : 4
}
]
}3. 조건을 적용한 쿼리 실행
이제 UserLikes 값이 8 이상인 내장 문서만 가져오고 싶다고 가정해 보겠습니다. 다음과 같이 프로젝션(projection)과 함께 쿼리를 작성할 수 있습니다.
> db.queryToEmbeddedDocument.find(
{ "PostDetails.UserLikes": { $gte: 8 } },
{ PostDetails: 1 }
).pretty();실행 결과는 다음과 같습니다.
{
"_id" : ObjectId("5c988a9f330fd0aa0d2fe4bd"),
"PostDetails" : [
{
"UserMessage" : "Hello",
"UserLikes" : 8
},
{
"UserMessage" : "Hi",
"UserLikes" : 6
},
{
"UserMessage" : "Good Morning",
"UserLikes" : 12
},
{
"UserMessage" : "Awesome",
"UserLikes" : 4
}
]
}결과 분석: 왜 전체가 반환될까?
출력 결과를 자세히 보면, 우리는 UserLikes가 8 이상인 항목(Hello: 8, Good Morning: 12)만 원했지만, 조건에 맞지 않는 Hi(6)와 Awesome(4)까지 포함된 배열 전체가 그대로 반환되었습니다.
이것이 바로 MongoDB find()의 동작 방식입니다. 프로젝션은 필드 단위로만 작동하며, 배열 내부의 개별 요소를 필터링하지 못합니다. 즉, 조건은 해당 문서를 '선택'하는 데만 사용될 뿐입니다.
참고: 배열 요소만 필터링하고 싶다면?
최신 버전의 MongoDB에서는 다음과 같은 대안을 활용할 수 있습니다.
- $elemMatch 연산자: 조건을 만족하는 첫 번째 배열 요소 하나만 반환합니다.
- 애그리게이션 파이프라인($filter): 조건에 맞는 모든 배열 요소만 남긴 새로운 배열을 만들어 반환할 수 있습니다.
예를 들어 애그리게이션을 사용하면 다음과 같이 작성합니다.
> db.queryToEmbeddedDocument.aggregate([
{ $match: {} },
{ $project: {
PostDetails: {
$filter: {
input: "$PostDetails",
as: "post",
cond: { $gte: ["$$post.UserLikes", 8] }
}
}
}}
])이처럼 find()만으로는 불가능한 배열 요소 수준의 필터링도, 상황에 맞는 도구를 선택하면 손쉽게 해결할 수 있습니다.