Firestoreはいろいろ制限がある。それでも便利なのでいろいろ試す。
私はポイントバリューを扱うアプリをよく書きます。バリュー操作自体はトランザクション処理もできるので、Firestoreでも安心して処理できます。で、処理時にtransactionsコレクションに下記のようなスキーマのdocumentを処理の度に残すとします。
処理した時間と、どのような処理をしたかを記録するとします。
{
createdAt: 2019-10-13 09:14:11
operation: "SEND" // or "Receive"
}
問題無いケース
全体のログを降順で見たい場合、
db.collection('transactions')
.orderby('createdAt','desc').get();
というようになります。SQLで書くと、下記のような感じとなりますが、
select * from transactions order by createdAt desc;
この処理は特に問題ありません。
問題(というか操作が必要)となるケース
ただ、SEND処理だけを操作時間でソートしたい場合、(何もしないと)エラーになります。
db.collection('transactions')
.where('operation','==','SEND')
.orderby('createdAt','desc').get();
SQLで書くと、
select * from transactions where operation = 'SEND' order by craetedAt desc;
という何の変哲もないクエリです。
エラーの原因は、whereの条件で利用しているoperationとorderbyで別のフィールドを利用しているからです。
このクエリを実行すると、下記のようなエラーがでます。
{ Error: 9 FAILED_PRECONDITION: The query requires an index. You can create it here: https://console.firebase.google.com/v1/r/project/xxxxx/firestore/indexes?create_composite=ClNwcm9qZWN0cy9jd3NlcnZlci02Y2ZiMy9kYXRhYmFzZXMvKGRlZmF1bHQpL2NvbGxlY3Rpb25Hcm91cHMvdHJhbnNhY3Rpb25zL2luZGV4ZXMvXxABGg0KCW9wZXJhdGlvbhABGg0KCWNyZWF0ZWRBdBACGgwKCF9fbmFtZV9fEAI
at callErrorFromStatus (/Users/xxxx/fire_test/node_modules/@grpc/grpc-js/build/src/call.js:30:26)
at Http2CallStream.call.on (/Users/xxxx/fire_test/node_modules/@grpc/grpc-js/build/src/call.js:79:34)
at Http2CallStream.emit (events.js:203:15)
at process.nextTick (/Users/xxxx/fire_test/node_modules/@grpc/grpc-js/build/src/call-stream.js:75:22)
at process._tickCallback (internal/process/next_tick.js:61:11)
よく読むと、「このクエリにはindexがいるぞ、以下のURLに行けば作れるよ」と親切に書いてくれています。
リンクのURLに行くと、そのまま「作るか?」と聞いてくるので「作る」とすればいいようです。
下記のように適切なindexを作ってくれるようです。
作成した上で、エラーとなったクエリを実行すると、問題なくクエリが実行されます。