简体   繁体   English

如何使用 Flutter 从 Firestore 中的 collectionGroups 查询数据

[英]How to query data from collectionGroups in Firestore with Flutter

For an app I'm building in Flutter I'm trying to retrieve a list of reserved timeslots by the logged in user across different days from Firestore.对于我在 Flutter 中构建的应用程序,我试图从 Firestore 中检索登录用户在不同日期的保留时间段列表。 I get to a point where I can retrieve the reserved timeslots on a given day (by hardcoding the day and passing it on to a futurebuilder), but when I try to retrieve data from a collectionGroup the listview stays empty (snapshot doesn't have data).我达到了可以在给定日期检索保留时间段的地步(通过对这一天进行硬编码并将其传递给 futurebuilder),但是当我尝试从 collectionGroup 检索数据时,列表视图保持为空(快照没有数据)。 Now, as a Flutter beginner I find understanding how to handle collectionGroup queries quite difficult and therefore any help would be appreciated!现在,作为 Flutter 初学者,我发现理解如何处理 collectionGroup 查询非常困难,因此我们将不胜感激!

For a quick overview of how my database is structured I have included 2 pictures on github:为了快速了解我的数据库的结构,我在 github 上包含了 2 张图片:

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

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

Below I have included my futurebuilder and listview builder下面我包括了我的 futurebuilder 和 listview builder

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'),
          );
        }
      },
    );
  }
}

And this is how I get the data from firestore.这就是我从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();
  }
}

I have created an index with collection id 'hours' and with collection range 'collectionGroup', my security rules right now are the following:我创建了一个集合 ID 为“小时”且集合范围为“collectionGroup”的索引,我现在的安全规则如下:

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

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

I've spend hours and hours trying to find this out already, so really anything useful would greatly be appreciated!我已经花了好几个小时试图找到这个,所以任何有用的东西都会非常感激!

I found the solution to my problem;我找到了解决问题的方法; First my snapshot could retrieve ALL the data from 'hours' When I removed the .where('reserved', isEqualTo: reserved) , but it didn't work when I included it, so the code worked but only the query didn't.首先,当我删除.where('reserved', isEqualTo: reserved)时,我的快照可以从 'hours' 中检索所有数据,但是当我包含它时它不起作用,所以代码有效,但只有查询没有. This was due to the index settings of my Firestore database.这是由于我的 Firestore 数据库的索引设置。 I read the documentation on indexes for Firestore here: https://firebase.google.com/docs/firestore/query-data/index-overview?authuser=0#automatic_indexing我在这里阅读了有关 Firestore 索引的文档: https://firebase.google.com/docs/firestore/query-data/index-overview?authuser=0#automatic_indexing

After I realized I had only made a composite index while I didn't look at the single-field index exemptions .在我意识到我只做了一个综合索引而我没有看单字段索引豁免之后。 I added two exemptions:我添加了两个豁免:

One for the field 'hour' in collectionGroup 'hours' while checking the boxes ascending and descending and one for the field 'reserved' in collectionGroup 'hours' while checking the boxes ascending and descending again.一个用于collectionGroup 'hours' 中的字段'hour',同时检查升序和降序框,一个用于collectionGroup 'hours' 中的'reserved' 字段,同时再次检查升序和降序框。 Now it does work!现在它确实有效!

After I realized I had only made a composite index while I didn't look at the single-field index exemptions在我意识到我只做了一个复合索引而我没有看单字段索引豁免之后

If you are looking for a way of doing this using firebase cli you can do it in this way:如果您正在寻找一种使用firebase cli执行此操作的方法,您可以这样做:

  1. create a file called firestore.indexes.json创建一个名为firestore.indexes.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"
        }
      ]
    }
  ]
}

In this way, you are preserving the default for the collection scope and enabling the ASCENDING one for the collection group scope instead.这样,您将保留集合 scope 的默认值,并改为为集合组 scope 启用ASCENDING选项。

  1. In your firebase.json :在您的firebase.json
  "firestore": {
    "indexes": "indexes/firestore.indexes.json"
  },
  1. run firebase deploy --only firestore:indexes运行firebase deploy --only firestore:indexes

This will do the job and finally, your collectionGroup query will work as expected.这将完成这项工作,最后,您的collectionGroup查询将按预期工作。

Hope it helps to save some time.希望它有助于节省一些时间。

In your example, you are not using a collectionGroup query.在您的示例中,您没有使用 collectionGroup 查询。 You need to do something like Firestore.instance.collectionGroup('hours').where('reserved', isEqualTo: '').getDocuments();你需要做类似Firestore.instance.collectionGroup('hours').where('reserved', isEqualTo: '').getDocuments(); for non-reserved slots.对于非预留插槽。

So, what do you store in your 002 document when that slot ís reserved?那么,当该插槽被保留时,您在 002 文档中存储什么? And why don't you use an explicit type like a boolean for the status?为什么不使用像 boolean 这样的显式类型作为状态? That makes it easier too.这也使它变得更容易。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM