DEV Community

DocumentDBでマルチバイト検索を実現する場合の留意事項について

Amazon DocumentDBは使っていますか?

Amazon DocumentDBはMongoDBの互換のドキュメント指向データベースであり、サービスの概要がわかるBlackBeltの資料は2019年のものが最新となっている。

MongoDBの互換となっているもののサポートされている MongoDB API、オペレーション、およびデータ型を見ると様々な面で機能差異があることがわかる。

特にマルチバイトを取り扱い場合においてはcollationに対応していないなどの問題があるため、検索要件が一致しているかどうかを検討する必要がある。

私が検証した中で、ドキュメントに記載がされていない部分において、マルチバイト検索においてインデックス適切に利用されず検索性能が大幅に低下する問題に遭遇したので、回避方法ともに紹介する。

今回の検証は、DocumentDB 4.0で行っているが、5.0でも事象が変わらない。

key に対して単一キーのインデックスを事前に作成する

rs0 [direct: primary] db1> db.col.createIndex({scope:1, key: 1})
scope_1_key_1
Enter fullscreen mode Exit fullscreen mode

その上で、半角文字で開始するコレクションを検索すると4133レコードの結果がある場合でも134 msと高速に応答する。

rs0:PRIMARY> db.col.find({ scope:"xxxxx", key: /^A/ }).hint({scope:1,key:1}).explain("executionStats");
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "db1.col",
                "winningPlan" : {
                        "stage" : "SUBSCAN",
                        "inputStage" : {
                                "stage" : "SORT",
                                "sortPattern" : {
                                        "key" : 1,
                                        "scope" : 1
                                },
                                "inputStage" : {
                                        "stage" : "FETCH",
                                        "inputStage" : {
                                                "stage" : "IXOR",
                                                "inputStages" : [
                                                        {
                                                                "stage" : "IXSCAN",
                                                                "indexName" : "scope_1_key_1"
                                                        },
                                                        {
                                                                "stage" : "IXSCAN",
                                                                "indexName" : "scope_1_key_1"
                                                        }
                                                ]
                                        }
                                }
                        }
                }
        },
        "executionStats" : {
                "executionSuccess" : true,
                "executionTimeMillis" : "134.866",
                "planningTimeMillis" : "0.632",
                "executionStages" : {
                        "stage" : "SUBSCAN",
                        "nReturned" : "4133",
                        "executionTimeMillisEstimate" : "133.853",
                        "inputStage" : {
                                "stage" : "SORT",
                                "nReturned" : "4133",
                                "executionTimeMillisEstimate" : "132.697",
                                "sortPattern" : {
                                        "key" : 1,
                                        "scope" : 1
                                },
                                "inputStage" : {
                                        "stage" : "FETCH",
                                        "nReturned" : "4133",
                                        "executionTimeMillisEstimate" : "105.506",
                                        "inputStage" : {
                                                "stage" : "IXOR",
                                                "nReturned" : "0",
                                                "executionTimeMillisEstimate" : "34.238",
                                                "inputStages" : [
                                                        {
                                                                "stage" : "IXSCAN",
                                                                "nReturned" : "4133",
                                                                "executionTimeMillisEstimate" : "33.333",
                                                                "indexName" : "scope_1_key_1"
                                                        },
                                                        {
                                                                "stage" : "IXSCAN",
                                                                "nReturned" : "0",
                                                                "executionTimeMillisEstimate" : "0.902",
                                                                "indexName" : "scope_1_key_1"
                                                        }
                                                ]
                                        }
                                }
                        }
                }
        },
        "serverInfo" : {
                "host" : "xxxxxxx",
                "port" : 27017,
                "version" : "4.0.0"
        },
        "ok" : 1,
        "operationTime" : Timestamp(1703472746, 1)
}
Enter fullscreen mode Exit fullscreen mode

しかしながら、全角文字で開始するコレクションの検索を行った場合、先ほどの条件に対して2.72倍の11266レコードの結果がある場合に、応答時間が153倍の 20518 msもかかる。

rs0:PRIMARY> db.col.find({ scope:"xxxxx", key: /^あ/ }).hint({scope:1,key:1}).explain("executionStats");
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "db1.col",
                "winningPlan" : {
                        "stage" : "SUBSCAN",
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "indexName" : "scope_1_key_1",
                                "direction" : "forward"
                        }
                }
        },
        "executionStats" : {
                "executionSuccess" : true,
                "executionTimeMillis" : "20518.276",
                "planningTimeMillis" : "0.303",
                "executionStages" : {
                        "stage" : "SUBSCAN",
                        "nReturned" : "11266",
                        "executionTimeMillisEstimate" : "20517.382",
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "nReturned" : "11266",
                                "executionTimeMillisEstimate" : "20514.086",
                                "indexName" : "scope_1_key_1",
                                "direction" : "forward"
                        }
                }
        },
        "serverInfo" : {
                "host" : "xxxxxxx",
                "port" : 27017,
                "version" : "4.0.0"
        },
        "ok" : 1,
        "operationTime" : Timestamp(1703472782, 1)
}
Enter fullscreen mode Exit fullscreen mode

対応方法としては、正規表現を利用せずに文字コード範囲の検索によって実現することで応答時間を早めることができる。

db.col.find({ scope:"xxxxx", key: {$gte: "あ", $lt: String.fromCharCode("あ".charCodeAt(0)+1) } })

rs0:PRIMARY> db.col.find({ scope:"xxxxx", key: {$gte: "あ", $lt: "ぃ"}}).hint({scope:1,key:1}).explain("executionStats");
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "db1.col",
                "winningPlan" : {
                        "stage" : "SUBSCAN",
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "indexName" : "scope_1_key_1",
                                "direction" : "forward"
                        }
                }
        },
        "executionStats" : {
                "executionSuccess" : true,
                "executionTimeMillis" : "367.233",
                "planningTimeMillis" : "0.499",
                "executionStages" : {
                        "stage" : "SUBSCAN",
                        "nReturned" : "11266",
                        "executionTimeMillisEstimate" : "366.099",
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "nReturned" : "11266",
                                "executionTimeMillisEstimate" : "362.641",
                                "indexName" : "scope_1_key_1",
                                "direction" : "forward"
                        }
                }
        },
        "serverInfo" : {
                "host" : "xxxxxxx",
                "port" : 27017,
                "version" : "4.0.0"
        },
        "ok" : 1,
        "operationTime" : Timestamp(1703472821, 1)
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)