【问题标题】:Flutter Firebase complex queriesFlutter Firebase 复杂查询
【发布时间】:2021-09-01 03:35:10
【问题描述】:

我有一个复杂的查询要执行。我在我的 Firebase 项目中为此查询添加了一个复合索引。 这是查询。

FirebaseFirestore.instance
              .collection('HouseDetails')
              .where('housePrice', isGreaterThan: GV.price[0].ceil() - 1)
              .where('housePrice', isLessThan: GV.price[1].ceil() + 1)
              .where('houseArea', isGreaterThan: GV.area[0].ceil() - 1)
              .where('houseArea', isLessThan: GV.area[1].ceil() + 1)
              .where('bathrooms',
                  isEqualTo: int.parse(bathrooms.first).ceil() + 1)
              .snapshots();

引发错误

I/flutter ( 9116): ══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter ( 9116): The following assertion was thrown while handling a gesture:
I/flutter ( 9116): All where filters with an inequality (<, <=, >, or >=) must be on the same field. But you have
I/flutter ( 9116): inequality filters on 'FieldPath([housePrice])' and 'FieldPath([houseArea])'.
I/flutter ( 9116): 'package:cloud_firestore/src/query.dart':
I/flutter ( 9116): Failed assertion: line 484 pos 18: 'hasInequality == field'

阅读文档后,我了解到我无法一次过滤出多个字段类型。但我不明白如何执行需要多个字段进行比较的查询 (housePricehouseArea 在我的例子中)。

如何执行这些查询?

【问题讨论】:

    标签: firebase flutter google-cloud-firestore


    【解决方案1】:

    你必须选择一个字段,然后从结果中,你可以过滤另一个字段。

    例如,

    ModelHouseDetails _requestHouseDetailsFromSnapshot(DocumentSnapshot snapshot) {
    return ModelHouseDetails(
        houseId: snapshot.id,
        houseArea: snapshot['houseArea'],
        housePrice: snapshot['housePrice'],
        bathrooms: snapshot['bathrooms']);
    }
    
    Stream<List<HouseDetails>> get listHouseDetails {
    return FirebaseFirestore.instance.collection('request-service')
          .where('housePrice', isGreaterThan: GV.price[0].ceil() - 1)
          .where('housePrice', isLessThan: GV.price[1].ceil() + 1)
          .snapshots()
          .map((snapshot) =>
          snapshot.docs.where((element) => element['houseArea'] >= GV.area[0].ceil() - 1 && element['houseArea'] <= GV.area[1].ceil() + 1 && element['bathrooms'] == int.parse(bathrooms.first).ceil() + 1).map(_requestHouseDetailsFromSnapshot).toList());
    }
    

    【讨论】:

      【解决方案2】:

      我知道这很烦人(我去过那里),但 Firestore 不允许 where 过滤器在不同字段上存在不等式,如文档中所述:

      In a compound query, range (<, <=, >, >=) and not equals (!=, not-in) comparisons must all filter on the same field.

      您有两种解决方法:

      1. 选择一个字段来使用 where 过滤器,然后在客户端执行其余的过滤。以下代码对housePrice 以及服务器端的bathrooms 和客户端的houseArea 执行过滤:
      FirebaseFirestore.instance
        .collection('houseDetails')
        .where('housePrice', isGreaterThan: GV.price[0].ceil() - 1)
        .where('housePrice', isLessThan: GV.price[1].ceil() + 1)
        .where('bathrooms', isEqualTo: int.parse(bathrooms.first).ceil() + 1)
        .snapshots()
        // Transform each query snapshot into new stream as list of document snapshots
        .map<List<QueryDocumentSnapshot<Map<String, dynamic>>>>(
        // Discard any document snapshots whose house area data is not in the range
        (querySnapshot) => querySnapshot.docs.where((snapshot) {
          // Try to parse house area as number
          // Note: I don't know house area is a double or int, so I used num for type safety
          final houseArea = num.tryParse(snapshot.data()["houseArea"]);
      
          // Make sure house area is not null
          if (houseArea != null) {
            // and make sure house area is in the range you specified
            if (houseArea > GV.area[0].ceil() - 1) {
              if (houseArea < GV.area[1].ceil() + 1) {
                // Return true to add the document snapshot to list
                return true;
              }
            }
          }
      
          // Return false to discard the document snapshot
          return false;
        }).toList(),
      );
      
      1. 更改数据模型时考虑复制数据,这是 Firestore 的常见情况。例如,您可以将价格范围为200k-350k的房屋详情存储在一个名为houseDetails-200k-350k的集合中,并对houseAreabathrooms进行过滤:
      FirebaseFirestore.instance
        .collection('houseDetails-200k-350k')
        .where('houseArea', isGreaterThan: GV.area[0].ceil() - 1)
        .where('houseArea', isLessThan: GV.area[1].ceil() + 1)
        .where('bathrooms', isEqualTo: int.parse(bathrooms.first).ceil() + 1)
        .snapshots();
      

      【讨论】:

      • 如何在客户端过滤数据?
      • 查看第一个解决方法的示例。
      • 是的。我认为有一些库可以添加功能。还没弄清楚客户端意味着业务逻辑。
      • Flutter 的官方 Firestore 库只有一个。如果它不包含此内容,您将无法在其他任何地方找到它。仅供参考,也没有其他 SDK 支持它,因为 Firestore 根本不支持它。
      猜你喜欢
      • 2019-10-16
      • 1970-01-01
      • 2016-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-17
      相关资源
      最近更新 更多