バージョンは cloud_firestore: 0.8.2+1
です。
Date型のcreatedAt
というフィールドを作成し変更をリスンしようと次のようなコードを実行したところ、一瞬正しい件数が取得できたあとにdocument数0件のsnapshotが取得されるという不可解な挙動に悩まされました。
void _listenDataWithOrderBy() {
Firestore.instance
.collection("tasks")
.where("status", isEqualTo: "active")
.orderBy("createdAt", descending: true)
.snapshots()
.listen((QuerySnapshot querySnapshot) {
print("tasks.length:${querySnapshot.documents.length}");
querySnapshot.documents.forEach((d) {
final title = d.data["title"];
Timestamp timestamp = d.data["createdAt"];
DateTime date = timestamp.toDate();
print("$title,$date");
});
});
}
3件のデータが登録されていましたがログはこんな感じ。
whereで絞っているので2件取得できている事自体は正しい。
データをリスト表示にしていたので一瞬データが表示されたあと空になってしまいました。
flutter: tasks.length:2
flutter: Fuga,2018-10-29 10:06:25.858
flutter: Hoge,2018-10-29 10:06:24.854
flutter: tasks.length:0 // 何故か0件のsnapshotが!!
orderBy
を使わないと問題ないです。
flutter: tasks.length:2
flutter: Hoge,2018-10-29 10:06:24.854
flutter: Fuga,2018-10-29 10:06:25.858
調べたところどうやらwhere
とorderBy
で異なるフィールドを使用する場合には、複合インデックスを作成する必要があるもよう。
SDKによってはご丁寧にインデックス作成用のリンクがログに表示されるらしいのだが、Dartのプラグインはまだそこまで実装されていないから気づかなかった。
というわけでfirestore.index.jsonを次のようにしてデプロイしました。
{
"indexes": [
{
"collectionId": "tasks",
"fields": [
{
"fieldPath": "status",
"mode": "ASCENDING"
},
{
"fieldPath": "createdAt",
"mode": "DESCENDING"
}
]
}
]
}
すると無事にwhere
とorderBy
を同時に使用しても正常にデータが取得できるようになりました。
動作確認全体のコードはこんな感じ
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';
import 'package:flutter/material.dart';
// 実験用
class SampleApp extends StatefulWidget {
@override
State createState() => SampleAppState();
}
class SampleAppState extends State<SampleApp> {
@override
Widget build(BuildContext context) {
initFirestore();
return MaterialApp(
home: Column(
children: <Widget>[
FlatButton(
color: Colors.yellow,
child: Text("Create Data"),
onPressed: _createData,
),
FlatButton(
color: Colors.yellow,
child: Text("Listen Data"),
onPressed: _listenData,
),
FlatButton(
color: Colors.yellow,
child: Text("Listen Data with OrderBy"),
onPressed: _listenDataWithOrderBy,
),
],
),
);
}
void initFirestore() {
final firestore = Firestore.instance;
firestore.settings(timestampsInSnapshotsEnabled: true);
}
void _createData() async {
Firestore firestore = Firestore.instance;
firestore.collection("tasks").document().setData(
{"title": "Hoge", "status": "active", "createdAt": DateTime.now()});
await new Future.delayed(new Duration(seconds: 1));
firestore.collection("tasks").document().setData(
{"title": "Fuga", "status": "active", "createdAt": DateTime.now()});
await new Future.delayed(new Duration(seconds: 1));
firestore.collection("tasks").document().setData(
{"title": "Piyo", "status": "completed", "createdAt": DateTime.now()});
}
void _listenData() {
Firestore.instance
.collection("tasks")
.where("status", isEqualTo: "active")
.snapshots()
.listen((QuerySnapshot querySnapshot) {
print("tasks.length:${querySnapshot.documents.length}");
querySnapshot.documents.forEach((d) {
final title = d.data["title"];
Timestamp timestamp = d.data["createdAt"];
DateTime date = timestamp.toDate();
print("$title,$date");
});
});
}
void _listenDataWithOrderBy() {
Firestore.instance
.collection("tasks")
.where("status", isEqualTo: "active")
.orderBy("createdAt", descending: true)
.snapshots()
.listen((QuerySnapshot querySnapshot) {
print("tasks.length:${querySnapshot.documents.length}");
querySnapshot.documents.forEach((d) {
final title = d.data["title"];
Timestamp timestamp = d.data["createdAt"];
DateTime date = timestamp.toDate();
print("$title,$date");
});
});
}
}