こんにちは。トンペティです。
今日は、mongoDBのインデックスの複製方法とインデックス作成スクリプトの自動生成について話します。
mongoDBのインデックスを別環境にコピーしたい
既存のmongoDB環境のインデックスを複製して、別のmongoDB環境に適用したい!
そこで思いつく方法は二つ。
方法1:バックアップとリストア
mongodumpコマンドでコレクションのバックアップをとり、mongorestoreで別環境にリストア。
ただ、これはコレクションのバックアップなので、インデックスだけじゃなくデータも含んでいます。
データを入れたくないケースでは対応できません。
方法2:インデックス作成メソッドを作成し実行
getIndexesメソッドで既存DBのインデックスを参照し、その内容からcreateIndexメソッドのスクリプトを作成し実行。
複製というか同じものをゼロから構築するイメージですけど、
コレクションのデータを含まないインデックスだけを作るなら、普通はこの方法だと思います。
しかし、既存コレクションからcreateIndexを自動生成するスクリプト生成ウィザードはmongoDBには無く、
地道に1つずつ手作業でcreateIndexのスクリプトを書くしかありません。
当然、インデックスの数が増えると、時間も掛かるし、書き間違いも起こります。
過去には、作成可能なインデックスの上限数となる64個のインデックスを持つコレクションを扱ったこともあり、
そのときはインデックスの全てを作成することは諦めて、い、いつか自動化してやらあ!と思っていましたが...
いつか、、そのうち、、あしたこそ、、? あしたっていつのあしたよ?
やってやります。今こそ、createIndexの自動生成を考えてみましょう。あしたっていまさッ!
getIndexesをcreateIndexに変換する
まず、getIndexesメソッドを実行すると、次のような結果が得られます。
1 2 3 4 5 6 7 8 9 |
// shopAというコレクションのインデックスを取得する db.shopA.getIndexes() // 実行結果 ※表示上、ドキュメント内の改行は除去しています { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "sampledb.shopA" } { "v" : 2, "key" : { "price" : -1, "name" : 1 }, "name" : "price_-1_name_1", "ns" : "sampledb.shopA", "sparse" : true } { "v" : 2, "key" : { "name" : 1 }, "name" : "name_1", "ns" : "sampledb.shopA", "background" : true, "sparse" : false } { "v" : 2, "unique" : true, "key" : { "code" : 1 }, "name" : "code_1", "ns" : "sampledb.shopA" } |
sampledbというデータベースのshopAというコレクションに作られている4つのインデックスが取得できます。
ここで、参考までに、最終的に作りたいcreateIndexメソッドと比較してみましょう。
1 2 3 4 5 |
db.shopA.createIndex({ "_id" : 1 },{ "name" : "_id_" }); db.shopA.createIndex({ "price" : -1, "name" : 1 },{ "name" : "price_-1_name_1", "sparse" : true }); db.shopA.createIndex({ "name" : 1 },{ "name" : "name_1", "background" : true, "sparse" : false }); db.shopA.createIndex({ "code" : 1 },{ "unique" : true, "name" : "code_1" }); |
似て非なるもの。
そこに普遍的な法則を見つけ出してコードに起こすのが自動化の難しいところなのですが、、
ここでそんな苦労話を語る時間は無いので飛ばします。
この世には結果だけが残る!
そうして出来たのが、このcreateIndex自動生成ロジック。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// createIndexの自動生成 db.shopA.getIndexes().forEach(function(idx){ var collectionName=idx["ns"].slice(idx["ns"].indexOf(".")+1) // ns項目の値の最初のドット位置より後方をコレクション名として保持する var arg1=idx["key"] // key項目をcreateIndexの第一引数(連想配列)として保持する var arg2=idx // 一旦、すべての項目をcreateIndexの第二引数(連想配列)として保持する delete arg2["ns"] // 第二引数の連想配列に必要ない要素「ns」を削除する delete arg2["key"] // 第二引数の連想配列に必要ない要素「key」を削除する delete arg2["v"] // 第二引数の連想配列に必要ない要素「v」を削除する print("db."+collectionName+".createIndex(") // createIndexの引数前カッコまでを出力する printjsononeline(arg1) // createIndexの第一引数を出力する print(",") // createIndexの第二引数の前にカンマを出力する printjsononeline(arg2) // createIndexの第二引数を出力する print(");") // createIndexの引数後カッコを出力する }) |
もともとのgetIndexesメソッドをforEachで回しながらcreateIndex向けに変換した内容をprint出力しています。
getIndexes()手前のコレクション名(ここではshopAとしてる部分)を任意に変えれば汎用的に使用可能ッ!
ただ、インデックスのオプションは様々なケースがあるので、すべてのインデックスに対応できているかは、
正直、断言はできませんが、よく使用するオプションは可能な限り試して問題ないことは確認しているので、
大体のインデックスには対応できているはずです。
実行すると次のような結果が得られます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// createIndex自動生成ロジックの実行結果 db.shopA.createIndex( { "_id" : 1 } , { "name" : "_id_" } ); db.shopA.createIndex( { "price" : -1, "name" : 1 } , { "name" : "price_-1_name_1", "sparse" : true } ); db.shopA.createIndex( { "name" : 1 } , { "name" : "name_1", "background" : true, "sparse" : false } ); db.shopA.createIndex( { "code" : 1 } , { "unique" : true, "name" : "code_1" } ); |
ちょっと余計な改行が多いのは、printを何回も発行しているため。
printで出力する文字列部分と、printjsononelineで出力する連想配列部分を結合させると、
連想配列部分が”[object BSON]”の文字列に変換されてしまい、どうしても期待する結果が得られなかった...。
そして 一行で出力したいと思ってもできないので、 そのうちトンペティは 考えるのをやめた。
宇宙空間に放り出された絶望感から気持ちを切り替えまして、
改行多めのこのスクリプトは、そのまま使用しても問題なく動作しますが、やはり見栄えを良くしたいので、
最後はサクラエディタを使って調整します。
正規表現の置換を使って、末尾が「;」以外の行の改行を消去オオォォーッ!
置換前 | ([^;])\r\n |
置換後 | $1 |
1 2 3 4 5 6 |
// createIndex自動生成ロジックの実行結果(見栄え調整後) db.shopA.createIndex({ "_id" : 1 },{ "name" : "_id_" }); db.shopA.createIndex({ "price" : -1, "name" : 1 },{ "name" : "price_-1_name_1", "sparse" : true }); db.shopA.createIndex({ "name" : 1 },{ "name" : "name_1", "background" : true, "sparse" : false }); db.shopA.createIndex({ "code" : 1 },{ "unique" : true, "name" : "code_1" }); |
これで完成。出来上がったスクリプトを実行すればインデックスは作り放題、複製し放題です!
mongoDB環境構築の際には、一度試してみてください。
インデックスの数がどんなに多くても、もうコピーなんてしないなんて言わないよ絶対。