使用 StreamBuilder 而不是 FeatureBuilder 来避免 Firestore 中的 whereIn 10 限制

Using StreamBuilder instead of FeatureBuilder to avoid whereIn 10 Limit in Firestore

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

这是我的 firebase collection。

下面是获取数据的代码:


  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 returns 快照列表。还有其他方法吗?

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

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

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

您需要将其调整为 Stream

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

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

此处的 Stream 示例中有更多详细信息https://firebase.flutter.dev/docs/firestore/usage

编辑:

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

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

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

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

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

The where() method takes three parameters: a field to filter on, a comparison operator, and a value. The ‘in’ operator out of the many comparison operators of where() method is used to combine upto 10 equality (==) clauses on the same field with a logical OR.

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

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