2
votes

Comment interroger les données de collectionGroups dans Firestore avec Flutter

Pour une application que je crée dans Flutter, j'essaie de récupérer une liste de plages horaires réservées par l'utilisateur connecté à différents jours de Firestore. J'arrive à un point où je peux récupérer les plages horaires réservées un jour donné (en codant le jour en dur et en le transmettant à un futur constructeur), mais lorsque j'essaye de récupérer les données d'une collectionGroupe, la liste reste vide (l'instantané n'a pas Les données). Maintenant, en tant que débutant Flutter, je trouve assez difficile de comprendre comment gérer les requêtes de collectionGroup et donc toute aide serait appréciée!

Pour un aperçu rapide de la structure de ma base de données, j'ai inclus 2 images sur github:

Image 1: https://github.com/winckles/rooster/blob/master/Schermafbeelding%202019-11 -10% 20om% 2020.08.20.png? Raw = true

Image 2: https://github.com/winckles/rooster/blob/master/Schermafbeelding%202019-11-10% 20om% 2020.08.43.png? Raw = true

Ci-dessous, j'ai inclus mon futur constructeur et mon constructeur de listview

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }

    match /{path=**}/hours/{document}{
    allow read, write;
    }
  }
}

Et voici comment Je reçois les données de Firestore.

import 'package:cloud_firestore/cloud_firestore.dart';

class MyTimes {
  Future getMyTimes(String reserved) async {
    return Firestore.instance
        .collectionGroup('hours')
        .where('reserved', isEqualTo: reserved)
        .getDocuments();
  }
}

J'ai créé un index avec l'ID de collection 'heures' et avec la plage de collection 'collectionGroup', mes règles de sécurité pour le moment sont les suivantes:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';

class TimesTest extends StatefulWidget {
  @override
  _TimesTestState createState() => _TimesTestState();
}

class _TimesTestState extends State<TimesTest> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: MyFutureBuilder(),
      ),
    );
  }
}

class MyFutureBuilder extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: MyTimes().getMyTimes('${loggedInUser.email}'),
      builder: (context, AsyncSnapshot snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return Center(
            child: CircularProgressIndicator(
              backgroundColor: Colors.lightBlueAccent,
            ),
          );
        } else if (snapshot.hasData) {
          return ListView.builder(
              padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
              itemCount: snapshot.data.documents.length,
              itemBuilder: (BuildContext context, int index) {
                DocumentSnapshot user = snapshot.data.documents[index];
                return Padding(
                  padding: EdgeInsets.symmetric(
                    horizontal: 7.0,
                    vertical: 3.0,
                  ),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Card(
                        color: Colors.white,
                        child: Container(
                          child: ListTile(
                            onTap: () {},
                            title: Text(
                              user.data['hour'],
                              style: TextStyle(
                                color: Colors.grey[900],
                              ),
                            ),
                            subtitle: Text(
                              user.data['reserved'],
                              style: TextStyle(
                                color: Colors.grey[900],
                              ),
                            ),
                            leading: Icon(
                              Icons.access_time,
                              color: Colors.grey[900],
                            ),
                          ),
                        ),
                      ),
                    ],
                  ),
                );
              });
        } else if (snapshot.connectionState == ConnectionState.done &&
            !snapshot.hasData) {
          return Center(
            child: Text('No times found'),
          );
        } else {
          return Center(
            child: Text('Something else is wrong'),
          );
        }
      },
    );
  }
}

J'ai déjà passé des heures et des heures à essayer de le découvrir, donc vraiment tout ce qui est utile serait grandement apprécié!


0 commentaires

3 Réponses :


0
votes

Dans votre exemple, vous n'utilisez pas de requête collectionGroup. Vous devez faire quelque chose comme Firestore.instance.collectionGroup ('hours'). Where ('reserved', isEqualTo: '') .getDocuments (); pour les emplacements non réservés.

Alors, que stockez-vous dans votre document 002 lorsque cet emplacement est réservé? Et pourquoi n'utilisez-vous pas un type explicite comme un booléen pour l'état? Cela facilite également les choses.


1 commentaires

Merci pour votre réponse! Cependant, j'ai mis à jour mon code selon ce que vous avez suggéré, mais maintenant mon instantané ne récupère aucune donnée (j'obtiens 'No times found'). Dans le document 002, je stocke le créneau horaire suivant donc 'heure: 09: 00-10: 00' et à nouveau 'réservé: ""'. Je les ai nommés pour les mettre en ordre. Lorsque l'utilisateur connecté réserve un créneau horaire, le champ réservé est mis à jour en «réservé: e-mail des utilisateurs connectés». Je comprends la chose booléenne, mais j'aimerais montrer aux autres utilisateurs qui ont réservé le créneau horaire si ce n'est pas gratuit. Et est-ce que je n'aurais alors pas de problème avec l'affichage des heures réservées par l'utilisateur connecté?



2
votes

J'ai trouvé la solution à mon problème; D'abord, mon instantané pouvait récupérer TOUTES les données des 'heures' Lorsque j'ai supprimé le .where ('reserved', isEqualTo: reserved) , mais cela ne fonctionnait pas lorsque je l'ai inclus, donc le code fonctionnait mais seule la requête ne l'a pas fait. Cela était dû aux paramètres d'index de ma base de données Firestore. J'ai lu la documentation sur les index pour Firestore ici: https://firebase.google.com/ docs / firestore / query-data / index-overview? authuser = 0 # automatic_indexing

Après avoir réalisé que je n'avais créé qu'un index composite, je n'ai pas examiné les exemptions d'index à un seul champ . J'ai ajouté deux exemptions:

Un pour le champ 'heure' dans collectionGroup 'heures' en cochant les cases croissant et décroissant et un pour le champ 'réservé' dans collectionGroup 'heures' en cochant à nouveau les cases ascendantes et descendantes. Maintenant ça marche!


0 commentaires

1
votes

Après avoir réalisé que je n'avais créé qu'un index composite sans regarder les exemptions d'index à un seul champ

Si vous cherchez un moyen de faire cela en utilisant firebase cli , vous pouvez le faire de cette manière:

  1. créer un fichier appelé firestore.indexes.json
  "firestore": {
    "indexes": "indexes/firestore.indexes.json"
  },

De cette façon, vous conservez la valeur par défaut pour la portée de la collection et activez celle ASCENDING pour la portée du groupe de collection à la place. p >

  1. Dans votre firebase.json :
{
  "indexes": [],
  "fieldOverrides": [
    {
      "collectionGroup": "your_sub_collection",
      "fieldPath": "the_field",
      "indexes": [
        {
          "order": "ASCENDING",
          "queryScope": "COLLECTION"
        },
        {
          "order": "DESCENDING",
          "queryScope": "COLLECTION"
        },
        {
          "arrayConfig": "CONTAINS",
          "queryScope": "COLLECTION"
        },
        {
          "order": "ASCENDING",
          "queryScope": "COLLECTION_GROUP"
        }
      ]
    }
  ]
}

  1. exécuter firebase deploy --only firestore: indexes

Cela fera l'affaire et enfin, votre requête collectionGroup fonctionnera comme prévu.

J'espère que cela vous aidera à gagner du temps.


1 commentaires

Merci, c'est très utile!