【问题标题】:Using StreamBuilder instead of FeatureBuilder to avoid whereIn 10 Limit in Firestore使用 StreamBuilder 而不是 FeatureBuilder 来避免 Firestore 中的 whereIn 10 限制
【发布时间】:2021-12-05 18:08:13
【问题描述】:

我想获取特定用户喜欢的帖子并在交错网格视图中显示它们。我可以在 FutureBuilder 中做到这一点。但是在 FutureBuilder 中,由于 whereIn 的限制为 10,我无法获取更多。我知道使用StreamBuilder可以实现,但是我很无奈,不知道怎么做。

这是我的 Firebase 收藏。

下面是获取数据的代码:


  final FirebaseFirestore firestore = FirebaseFirestore.instance;
  getData() async {
    SharedPreferences sp = await SharedPreferences.getInstance();
    String _uid = sp.getString('uid');

    final DocumentReference ref = firestore.collection('users').doc(_uid);
    DocumentSnapshot snap = await ref.get();
    List d = snap['loved items'];
    List filteredData = [];
    if (d.isNotEmpty) {
      await firestore
          .collection('contents')
          .where('timestamp', whereIn: d)
          .get()
          .then((QuerySnapshot snap) {
        filteredData = snap.docs;
      });
    }

    notifyListeners();
    return filteredData;
  }

这是 FutureBuilder 代码:

body: sb.guestUser == true
            ? EmptyPage(
                icon: FontAwesomeIcons.heart,
                title: 'No wallpapers found.\n Sign in to access this feature',
              )
            : FutureBuilder(
                future: context.watch<BookmarkBloc>().getData(),
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  if (snapshot.hasData) {
                    if (snapshot.data.length == 0)
                      return EmptyPage(
                        icon: FontAwesomeIcons.heart,
                        title: 'No wallpapers found',
                      );
                    return _buildList(snapshot);
                  } else if (snapshot.hasError) {
                    return Center(
                      child: Text(snapshot.error),
                    );
                  }

                  return Center(
                    child: CupertinoActivityIndicator(),
                  );
                },
              ),
      ),
    );
  }

  Widget _buildList(snapshot) {
    return StaggeredGridView.countBuilder(
      crossAxisCount: 4,
      itemCount: snapshot.data.length,
      itemBuilder: (BuildContext context, int index) {
        List d = snapshot.data;

        return InkWell(
          child: Stack(
            children: <Widget>[
              Hero(
                  tag: 'bookmark$index',
                  child: cachedImage(d[index]['image url'])),
              Positioned(
                bottom: 15,
                left: 12,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Text(
                      d[index]['category'],
                      style: TextStyle(color: Colors.white, fontSize: 18),
                    )
                  ],
                ),
              ),
              Positioned(
                right: 10,
                top: 20,
                child: Row(
                  children: [
                    Icon(Icons.favorite,
                        color: Colors.white.withOpacity(0.5), size: 25),
                    Text(
                      d[index]['loves'].toString(),
                      style: TextStyle(
                          color: Colors.white.withOpacity(0.7),
                          fontSize: 16,
                          fontWeight: FontWeight.w600),
                    ),
                  ],
                ),
              ),
            ],
          ),
          onTap: () {
            Navigator.push(
                context,
                MaterialPageRoute(
                    builder: (context) => DetailsPage(
                          tag: 'bookmark$index',
                          imageUrl: d[index]['image url'],
                          catagory: d[index]['category'],
                          timestamp: d[index]['timestamp'],
                        )));
          },
        );
      },
      staggeredTileBuilder: (int index) =>
          new StaggeredTile.count(2, index.isEven ? 4 : 3),
      mainAxisSpacing: 10,
      crossAxisSpacing: 10,
      padding: EdgeInsets.all(15),
    );
  }
}

我参考了互联网,但我不完全知道如何将 FutureBuilder 转换为返回快照列表的 StreamBuilder。还有其他方法吗?

【问题讨论】:

    标签: flutter dart google-cloud-firestore stream-builder flutter-futurebuilder


    【解决方案1】:

    首先,“whereIn 限制为 10”是 Firestore 硬约束。这意味着无论您使用FutureBuilder 还是StreamBuilder,这个limit-of-10 约束仍然适用。

    现在,如果你还想切换到StreamBuilder

    StreamBuilder<QuerySnapshot>(
          stream: _yourStream,
          builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
    

    您需要调整它以成为流

    await firestore
              .collection('contents')
              .where('timestamp', whereIn: d)
              .get()
              .then((QuerySnapshot snap) {
            filteredData = snap.docs;
          });
    

    需要将.get( )改为.snapshots()并需要使用snapshot.data.docs.map

    更多详细信息请参见此处的流示例https://firebase.flutter.dev/docs/firestore/usage

    编辑:

    关于 10 项硬约束:根据您的具体项目,您可以:

    • 多次查询,然后在您的应用程序中本地合并(同时删除重复项),或者

    • 另一种解决方案是在您的应用程序中进行一些过滤客户端(基本上,从 FireStore 中获取更多内容并在 Flutter 中进行过滤),或者

    • 另一种解决方案是创建由其他字段组合而成的新字段(例如,在您的情况下,它可能是“月”或“weekNumber”,因此您在 1 种情况下有一周而不是 7 天)

    【讨论】:

    • 如果您还需要其他东西,请告诉我
    • 感谢您的回答。我试过你的建议。但正如你所说,即使我使用 streamBuilder 它也会抛出同样的错误'in' filters support a maximum of 10 elements in the value 有没有其他方法可以解决这个问题?我想把它们都拿来。
    • 不,这 10 项是硬约束。我现在将在我的回答中添加一些提示。如果您认为它回答了您的问题,请考虑“接受”我的回答
    【解决方案2】:

    Firestore 不允许您使用超过 10 条记录进行数组成员资格查询。如果您检查此documentation,您将看到:

    where() 方法接受三个参数:一个要过滤的字段,一个 比较运算符和一个值。众多中的“in”运算符 where() 方法的比较运算符用于组合最多 10 个 具有逻辑 OR 的同一字段上的相等 (==) 子句。

    如果您使用超过 10 条记录,Firestore 将不会接受它,您会收到错误消息。 我建议您将“d”数组分成多个数组,最多包含 10 条记录以进行查询,这将允许 Firestore 在其限制范围内操作 whereIn 并且它会起作用。

    还有一个类似的 stackoverflow 线程建议使用 chunkSizeCollection 将数组拆分为 10 个元素的块。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-10
      • 2014-02-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多