Flutter Firestore中如何实现arrayNotContains查询功能?
替代Firestore arrayContains实现arrayNotContains的简便方案
嘿,这个问题确实困扰了不少Firestore开发者——官方至今还没提供原生的arrayNotContains查询方法,不过咱们有几个实用的替代方案,我给你拆解下:
方案1:客户端过滤(快速上手)
如果你的alerts集合数据量不大,最直接的方式就是先获取所有文档,再在客户端过滤掉包含当前用户UID的项。代码示例如下:
FirebaseFirestore.instance .collection('alerts') .snapshots() .map((snapshot) => snapshot.docs .where((doc) => !(doc.data()['acknowledge'] as List).contains(user?.uid)) .map((doc) => doc.data()) .toList());
注意:这种方法适合小数据集,要是集合里有成百上千条文档,会浪费带宽和客户端资源,不推荐在大数据量场景用。
方案2:反向存储数组(高效查询)
修改你的数据结构,新增一个unacknowledgedUids数组,专门存储未确认该alert的用户UID。写入数据时维护这两个数组:
- 当用户确认alert时,把UID从
unacknowledgedUids移除,同时加到acknowledge数组里 - 初始化alert时,
unacknowledgedUids可以设置为目标用户的UID列表
这样查询就可以直接用原生的arrayContains了:
FirebaseFirestore.instance .collection('alerts') .where('unacknowledgedUids', arrayContains: user?.uid) .snapshots();
这个方案的优势是查询效率高,完全利用Firestore的服务器端查询能力,适合大数据量场景。
方案3:用Map替代数组(灵活且索引友好)
把acknowledge数组改成acknowledgedBy Map结构,键是用户UID,值可以是确认时间或者布尔值(比如true):
{ "acknowledgedBy": { "user123": true, "user456": "2024-05-20T10:00:00Z" } }
然后查询时就可以用isNull或者isNotEqualTo来筛选未确认的alert:
FirebaseFirestore.instance .collection('alerts') .where('acknowledgedBy.${user?.uid}', isNull: true) .snapshots();
这种方案的好处是支持更灵活的查询(比如可以同时筛选确认时间范围),而且Firestore可以为Map的键创建索引,查询性能很好。唯一要注意的是用户UID不能包含Firestore禁止的字符(比如.、/等),不过官方生成的UID都是合法的,一般不用担心。
内容的提问来源于stack exchange,提问作者Jared Stevenson
相关产品推荐
相关产品推荐

