이번 글 에서는 multikey index에 대해 알아보겠습니다.
MongoDB는 Document 기반의 비정규화된 데이터를 저장하는 데이터베이스이기 때문에
하나의 document가
array형태의 데이터를 가지는 경우나, document안에 또 document가 있는 embeded document형태의 데이터를 갖는 경우가 많은데
이런 형태의 document를 위한 인덱스가 Multi-Key Index입니다.
multikey index
> db.user.insert({
... name:"kimdubi",
... addr:{ city : "seongnam", dong: "sampyeong" },
... db:["MongoDB","Mysql","Oracle"]})
db.user.createIndex({addr:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
db.user.createIndex({db:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1
}
> db.user.createIndex({"addr.city":1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 3,
"numIndexesAfter" : 4,
"ok" : 1
}
> db.user.createIndex({"addr.city":1,"addr.dong":1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 4,
"numIndexesAfter" : 5,
"ok" : 1
=> array, embeded document 를 field 로 갖는 인덱스, embeded document의 특정 field만으로도 인덱싱 가능
embeded document / array field 조회하는 방법
- embeded document 정의문 그대로 넣기, 하나 빼거나 순서 다르면 실패
db.user.find({addr:{city:"seongnam",dong:"sampyeong"}}).pretty()
{
"_id" : ObjectId("5daaccd874411c322449442e"),
"name" : "kimdubi",
"addr" : {
"city" : "seongnam",
"dong" : "sampyeong"
},
"db" : [
"MongoDB",
"Mysql",
"Oracle"
]
}
> db.user.find({addr:{dong:"sampyeong"}}).pretty()
>
> db.user.find({addr:{city:"seongnam"}}).pretty()
>
- embeded document의 field 로 호출
> db.user.find({"addr.city":"seongnam"})
{ "_id" : ObjectId("5daaccd874411c322449442e"), "name" : "kimdubi", "addr" : { "city" : "seongnam", "dong" : "sampyeong" }, "db" : [ "MongoDB", "Mysql", "Oracle" ] }
> db.user.find({"addr.dong":"sampyeong"})
{ "_id" : ObjectId("5daaccd874411c322449442e"), "name" : "kimdubi", "addr" : { "city" : "seongnam", "dong" : "sampyeong" }, "db" : [ "MongoDB", "Mysql", "Oracle" ] }
- array
> db.user.find({db:"MongoDB"})
{ "_id" : ObjectId("5daaccd874411c322449442e"), "name" : "kimdubi", "addr" : { "city" : "seongnam", "dong" : "sampyeong" }, "db" : [ "MongoDB", "Mysql", "Oracle" ] }
Multi-Key index 제한 사항
- Multi-Key index는 shard key 로 사용될 수 없음
=> 여러개의 인덱스 엔트리가 같은 document를 가리키기 때문에 shard key 로 사용할 수 없음
shard key 는 키 값의 범위에 따라 하나의 chunk로 매핑되어야 하기 때문
- Multi-Key index는 커버링 인덱스 처리가 불가능함
db.installed_apps.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.installed_apps"
},
{
"v" : 2,
"key" : {
"apps" : 1
},
"name" : "apps_1",
"ns" : "test.installed_apps"
}
]
db.installed_apps.explain().aggregate([ {'$unwind':'$apps'},{'$group':{_id:'$apps',sum:{'$sum':1}}},{'$match':{sum:{'$gte':2}}}])
{
"stages" : [
{
"$cursor" : {
"query" : {
},
"fields" : {
"apps" : 1,
"_id" : 0
},
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.installed_apps",
"indexFilterSet" : false,
"parsedQuery" : {
},
"queryHash" : "8B3D4AB8",
"planCacheKey" : "8B3D4AB8",
"winningPlan" : {
"stage" : "COLLSCAN",
"direction" : "forward"
},
"rejectedPlans" : [ ]
}
}
},
{
"$unwind" : {
"path" : "$apps"
}
},
{
"$group" : {
"_id" : "$apps",
"sum" : {
"$sum" : {
"$const" : 1
}
}
}
},
{
"$match" : {
"sum" : {
"$gte" : 2
}
}
}
],
"ok" : 1
}
=> app field에 인덱스가 있지만 multi-key index는 커버링 인덱스 처리가 되지 않는다.
컬렉션을 모두 읽은 다음에 apps field 의 array 데이터를 그룹핑 해서 최종 결과를 반환하게됨
- Mulki-Key index 의 검색조건 bound
> db.test.insert({i:[2,9]})
WriteResult({ "nInserted" : 1 })
> db.test.insert({i:[4,3]})
WriteResult({ "nInserted" : 1 })
>
> db.test.find({i:{$gte:3, $lte:6}})
{ "_id" : ObjectId("5dab36f674411c3224494432"), "i" : [ 2, 9 ] }
{ "_id" : ObjectId("5dab36fb74411c3224494433"), "i" : [ 4, 3 ] }
=> i:{$gte:3, $lte:6} 조건이니 [2,9] 는 조회되지 않을 것 같지만 포함되어있음 i : {$gte:3} i : {$lte:6} 의 합집합으로 조회되기 때문임
> db.test.find({i:{$elemMatch:{$gte:3, $lte:6}}})
{ "_id" : ObjectId("5dab36fb74411c3224494433"), "i" : [ 4, 3 ] }
=> Mulki-key index 에 대해 between 검색을 하려면 array의 각 element들에 대해 $elemMatch 연산자를 사용해야함
i : {$gte:3} i : {$lte:6} 의 교집합으로 조회
> db.test.insert({i:[2,4]})
WriteResult({ "nInserted" : 1 })
> db.test.find({i:{$elemMatch:{$gte:3,$lte:6}}})
{ "_id" : ObjectId("5dab36fb74411c3224494433"), "i" : [ 4, 3 ] }
{ "_id" : ObjectId("5dab3a1874411c3224494434"), "i" : [ 2, 4 ] }
=> $elemMatch연산자의 결과는 배열의 모든 요소가 조건에 만족해야 하는 게 아니라
$elemMatch의 조건을 모두 만족하는 엘리먼트 하나라도 가지고 있는 document를 반환함