この記事はheyアドベントカレンダー2020の5日目の記事です。
STORESでECサービスのSREをしている@wanijiです。
STORESではメインとなるDBにMongoDBを採用しており、RDBと同様にインデックスを使ったパフォーマンス改善をします。その際に、インデックスを使用したクエリの中でも効率の良いCovered Queryにすることを検討するのですが、 null
が条件に入ってはならないという制約があるため、使用出来ない事がよくあります。
今回は null
以外のデータを取得したい時に、どうやってCovered Queryにするかをご紹介します。
Covered Queryとは
Covered Queryは、クエリ条件と返却されるフィールドがすべて同一のインデックスに含まれているため、ドキュメントの取得がなく非常に高速なクエリです。Covered Queryではない場合、インデックスに含まれていないフィールドが必要なため、ドキュメントを取得する必要があります。
クエリをCovered Queryにする条件は以下のとおりです。
- クエリで使用するフィールドと返却するフィールドが同一のインデックスに含まれている
null
がクエリ条件に含まれない
しかし現実には、null
以外のデータを取得するために foo: { "$ne": nil }
のような条件を使いたいケースが多々あります。これを null
を条件に入れないで実現する方法を説明します。
どのように実現するか
フィールドに含まれるデータ型や値によって変わるため、それぞれ説明します。
フィールドに数値とnullが含まれる場合
数値の場合は $gt
や $lt
を使います。例えば foo
フィールドには0以上のデータと null
しか入らない場合、 foo: { "$gte": 0 }
とすることで null
を排除しつつCovered Queryに出来ます。実装当時は0以上しか入らなかったが、仕様変更で0未満も入るようになったということがあり得るため、その点は注意が必要です。
フィールドに文字列とnullが含まれる場合
文字列の場合は { "$gte": " " }
とすることでCovered Queryに出来ます。MongoDBで文字列に対し $gt
を使うと、先頭から一文字ずつ比較します。MongoDBは文字列をUTF-8で扱っており、その中で半角スペースは制御文字列を除くと最小の値(0x20
)であるため、すべての文字列を取得することが出来ます。また、MongoDB 2.6以降から $gt
や $lt
を使うと null
が取得されないようになっているため、 null
が排除されます。
まとめ
Covered Queryにしたことで効率的なクエリになりました。Sparse IndexやPartial Index を併用することで、インデックス自体のサイズを削減することも出来るので、そちらもおすすめです。
明日は@daitasuによる "フルリモート環境で行う大喜利式チームランチ" です。