12
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DartのFirestoreでorderByしたらなぜか0件のsnapshotが取得される

Last updated at Posted at 2018-10-29

バージョンは 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

調べたところどうやらwhereorderByで異なるフィールドを使用する場合には、複合インデックスを作成する必要があるもよう。
SDKによってはご丁寧にインデックス作成用のリンクがログに表示されるらしいのだが、Dartのプラグインはまだそこまで実装されていないから気づかなかった。

というわけでfirestore.index.jsonを次のようにしてデプロイしました。

{
  "indexes": [
    {
      "collectionId": "tasks",
      "fields": [
        {
          "fieldPath": "status",
          "mode": "ASCENDING"
        },
        {
          "fieldPath": "createdAt",
          "mode": "DESCENDING"
        }
      ]
    }
  ]
}

すると無事にwhereorderByを同時に使用しても正常にデータが取得できるようになりました。

動作確認全体のコードはこんな感じ

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");
      });
    });
  }
}
12
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?