mongodbのsparse indexと `{$ne: null}` 的なquery
疎なfield(ほとんどがnullでごく少数のdocumentだけ値が入る)があったとして。 これに対するindexはsparse indexで十分じゃないかなと思っていたのだけれど。
試してみたらだめなqueryだったという話。
explainの見方
その前にexplainの見方をメモ
db.<collection>.find(q).explain("executionStats")
- winningPlanが採用されたもの(rejectedPlanが非採用)
- 使われているindexはinpuStageがIXSCANの時のindexName(IXSCAN=index scan, COLLSCAN=collection scan)
- totalDocsExaminedはindexで絞り込まれたdocument数
- nReturnedは返されたdocument数
要はIXSCANになっていればOK。
sparse index
部分関数みたいないもの。sparse indexとdense indexで対応している。通常のindexがdense index。 値が入っていない部分に関してはindexを作成しないというもの。indexのサイズを小さくできることが利点(逆に言うとsparse indexにしたからといって早くなることはない。メモリーに乗り切らなくなるみたいなすごいきわどい状況の話でなければ)。
例えばKというfieldがありこれがnull等の場合に貼るindexを考えてみたときに以下の様な形になる。
value | dense index | sparse index |
---|---|---|
{K: 1} | true | true |
{K: null} | true | true |
{} | true | false |
fieldが存在しない場合にはindexは生成されないけれど、nullの場合は作成される。 (ちなみにunique:trueを付けたときにsparse:trueを付けると、該当のfieldを持たないdocumentはunique制約の対象外にできるみたいな話もあったりする)
{$ne: null}
もしくは {$exists: true}
sparse index
ある値が存在するのならばというqueryは {$exists: true}
という形になるし。これはindexがあるかで判定ができる。
一方で {$exists: false}
はindexの対象外の値を見ているのでindexだけをみて判断できない。
sparse indexが賢いなら、全部nullのfieldになっていた場合に、 {$ne: null}
でもなんか良い感じにindex使われないかなと思ったけれど。そんなことはなかった。
考えてみたら当たり前で null以外は値を持たないも含んでしまうのでsparse indexではまかないきれない範囲になっている。
代わりに {$exists: true}
とのandを取ってあげるとindexを使ってくれる。
dense index
一方dense indexというか通常のindexは全部の情報を持っているので {$ne: null}
だけでindexが効く。
考えてみれば当たり前の話ではあるけれど。
実験
entriesというidとnameとmarkedAtだけで作られたdocumentを格納するcollectionを作成。 markedAtは1つだけに値を入れる(それ以外はnullかunset)。 markedAtにindexを貼ってみて上手くindexが使われるか調べる。
db.createCollection("entries"); db.entries.insert([ {"_id": ObjectId(), "name": "foo"}, {"_id": ObjectId(), "name": "bar"}, {"_id": ObjectId(), "name": "boo"}, ]); // create sparse index db.entries.createIndex({"markedAt": 1}, {sparse: true}); // markedAt 1 and other items don't have markedAt db.entries.updateOne({}, {$set: {"markedAt": ISODate()}}) db.entries.find({markedAt: {$exists: true}}).explain("executionStats") // IXSCAN // markedAt 1 and other items markedAt are null db.entries.updateMany({}, {$set: {"markedAt": null}}); db.entries.updateOne({}, {$set: {"markedAt": ISODate()}}) db.entries.find({markedAt: {$ne: null}}).explain("executionStats") // COLLSCAN db.entries.find({$and: [{markedAt: {$ne: null}}, {markedAt: {$exists: true}}]}).explain("executionStats") // IXSCAN // create dense index db.entries.dropIndex("markedAt_1"); db.entries.createIndex({"markedAt": 1}); // markedAt 1 and other items markedAt are null db.entries.find({markedAt: {$ne: null}}).explain("executionStats") // IXSCAN // markedAt 1 and other items don't have markedAt db.entries.updateMany({}, {$unset: {"markedAt": 1}}); db.entries.updateOne({}, {$set: {"markedAt": ISODate()}}) db.entries.find({markedAt: {$exists: false}}).explain("executionStats") // IXSCAN
sparse indexにする時には、$exists: true
を忘れずにという話
matplotlibで描画できるグラフがわからないので自分用のギャラリーを作っている
matplotlibには色々なメソッドがいっぱい
なんか色々あるけれど。どういうものがあるのか把握していない。分かっているのは以下2つだけの様な状況
- plot
- hist
たぶんそれ以外にも色々あるかんじ。
matplotlibの例は多すぎるし複雑過ぎる
matplotlibのexampleやgalleryは以下のところにある。
一個一個見ていくには多すぎるし。見栄え重視でコードが複雑過ぎる例もあったりする。つらい。
自分用のギャラリーを作ってみる
自分用のギャラリーというかsandbox+利用例の様なものを作ってみることにした。
やり方は単純で
- https://matplotlib.org/api/axes_api.html#plotting のPlotting部分の要素を選ぶ
<number>plot_<name>.py
のようなファイルを作る- ここに簡単な利用例のコードを書く
そして、Makefile経由で出力結果の画像とそれらをまとめたギャラリー的なmarkdown(readme.md)を生成している。
https://github.com/podhmo/utatane/tree/master/examples/02plot
現在は以下が終わっている
- plot
- errorbar
- scatter
あと、どうせなのでnumpyの方をあえてつかっている。