네, 가능합니다. MongoDB에서 $or 연산자가 포함된 쿼리도 각 조건에 해당하는 필드에 인덱스가 미리 생성되어 있다면 인덱스를 활용할 수 있습니다. 핵심은 $or 절 안의 각 필드마다 개별적인 인덱스를 만들어 두는 것입니다.
이번 글에서는 실제 예제를 통해 인덱스를 생성하고, explain() 메서드로 실행 계획을 확인하며 인덱스가 실제로 사용되는지 검증해 보겠습니다.
1단계: 인덱스 생성하기
먼저 $or 조건에서 사용될 첫 번째 필드인 First에 오름차순(1) 인덱스를 생성합니다.
> db.indexOrQueryDemo.createIndex({"First":1});위 명령을 실행하면 다음과 같은 결과가 출력됩니다.
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1
}numIndexesBefore는 인덱스 생성 전 개수, numIndexesAfter는 생성 후 개수를 의미합니다. 위 결과를 보면 인덱스가 하나 추가되어 2개에서 3개로 늘어난 것을 확인할 수 있습니다.
이어서 두 번째 필드인 Second에도 동일한 방식으로 인덱스를 생성합니다.
> db.indexOrQueryDemo.createIndex({"Second":1});실행 결과는 다음과 같습니다.
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 3,
"numIndexesAfter" : 4,
"ok" : 1
}이제 First와 Second 필드 각각에 대한 인덱스가 모두 준비되었습니다.
2단계: $or 연산자 쿼리에 explain() 적용하기
인덱스가 실제로 사용되는지 확인하려면 $or 연산자가 포함된 쿼리 뒤에 explain() 메서드를 붙여 실행 계획(plan)을 조회하면 됩니다.
> db.indexOrQueryDemo.find({$or:[{First:1}, {Second:2}]}).explain();실행 결과의 주요 부분은 다음과 같습니다.
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.indexOrQueryDemo",
"indexFilterSet" : false,
"parsedQuery" : {
"$or" : [
{
"First" : {
"$eq" : 1
}
},
{
"Second" : {
"$eq" : 2
}
}
]
},
"winningPlan" : {
"stage" : "SUBPLAN",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "OR",
"inputStages" : [
{
"stage" : "IXSCAN",
"keyPattern" : {
"First" : 1
},
"indexName" : "First_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"First" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"First" : [
"[1.0, 1.0]"
]
}
},
{
"stage" : "IXSCAN",
"keyPattern" : {
"Second" : 1
},
"indexName" : "Second_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"Second" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"Second" : [
"[2.0, 2.0]"
]
}
}
]
}
}
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"host" : "DESKTOP-QN2RB3H",
"port" : 27017,
"version" : "4.0.5",
"gitVersion" : "3739429dd92b92d1b0ab120911a23d50bf03c412"
},
"ok" : 1
}실행 계획 결과 분석하기
출력 결과에서 가장 중요하게 살펴볼 부분은 winningPlan, 즉 MongoDB 쿼리 플래너가 최종적으로 선택한 실행 계획입니다.
- SUBPLAN 단계: $or 연산자가 포함된 쿼리를 처리하기 위해 플래너가 하위 계획(subplan)을 구성했음을 의미합니다.
- OR 단계: 두 개의 조건을 각각 처리한 후 결과를 병합하는 역할을 담당합니다.
- IXSCAN 단계: 인덱스 스캔(Index Scan)을 나타냅니다. 이 단계가 보인다는 것은 컬렉션 전체를 훑는 COLLSCAN(전체 컬렉션 스캔)이 아니라 인덱스를 활용해 쿼리가 실행되었음을 의미합니다.
실제로 출력 결과를 보면 첫 번째 IXSCAN에서는 indexName: "First_1"이, 두 번째 IXSCAN에서는 indexName: "Second_1"이 사용된 것을 확인할 수 있습니다. 즉, $or 절의 각 조건이 서로 다른 인덱스로 효율적으로 처리된 것입니다.
정리 및 참고 사항
MongoDB에서 $or 쿼리가 인덱스를 제대로 활용하려면 다음 사항을 기억하세요.
- $or 안의 모든 조건 필드에 각각 인덱스가 존재해야 인덱스가 사용됩니다. 하나라도 인덱스가 없으면 전체 쿼리가 컬렉션 스캔으로 fallback될 수 있습니다.
explain()결과에서IXSCAN스테이지가 포함되어 있는지 확인하면 인덱스 사용 여부를 바로 판단할 수 있습니다.- 예제에 사용된
ensureIndex()는 현재 deprecated(사용 중단 권고)된 메서드이므로, 최신 버전의 MongoDB에서는createIndex()를 사용하는 것이 좋습니다.