anup davda
anup davda

Reputation: 15

Is There another way apart from if else statement for Multi Filter Search Flutter Firebase?

I am trying to achieve Multi Filter Feature like that In Zillow App. I am using Flutter, Firebase, and Provider Package.

Users can filter by Price Range, Area Range, City, Zipcode, Bathrooms, Bedrooms, and Amenities. Users can select any combination of filters. When calculating it using combinations, I ended up with 127 combinations?

  1. Is there any efficient way to get a query of every single combination?
  2. Is the Looping or Switch statement is good code practice for a large number of queries?

 Future<void> getApartmentbyCityPrice(PersonalHomeList personalApartmentList,String city,double 
  maxPrice, double minPrice) async {
     if (city == "Any" && maxPrice != null && minPrice != null) {
         QuerySnapshot snapshot = await FirebaseFirestore.instance.collection('apartments'). 
          where("price",isGreaterThanOrEqualTo: minPrice, isLessThanOrEqualTo: maxPrice).get();
            List<PersonalApartment> _loadedPersonalApartment = [];
            snapshot.docChanges.forEach((result) {
            PersonalApartment personalApartment = new PersonalApartment(
            id: result.doc['id'],
            userId: result.doc['userId'],
            amenities: result.doc['amenities'],
            area: result.doc['area'],
            bathroom: result.doc['bathroom'],
            bedroom: result.doc['bedroom'],
            city: result.doc['city'],
            createdAt: result.doc['createdAt'],
            description: result.doc['description'],
            imageUrl: result.doc['imageUrl'],
            price: result.doc['price'],
            streetName: result.doc['streetName'],
            updatedAt: result.doc['updatedAt'],
            zipcode: result.doc['zipcode'],
          );
      _loadedPersonalApartment.add(personalApartment);
    });
   personalApartmentList.apartmentListByCity = _loadedPersonalApartment;
   } else if (city.isNotEmpty && city != "Any" && maxPrice == 0.0 && minPrice == 0.0) {
         QuerySnapshot snapshot = await FirebaseFirestore.instance.collection('apartments') 
         .where("city", isEqualTo: city).get();
          List<PersonalApartment> _loadedPersonalApartment = [];
          snapshot.docChanges.forEach((result) {
          PersonalApartment personalApartment = new PersonalApartment(
          id: result.doc['id'],
          userId: result.doc['userId'],
          amenities: result.doc['amenities'],
          area: result.doc['area'],
          bathroom: result.doc['bathroom'],
          bedroom: result.doc['bedroom'],
          city: result.doc['city'],
          createdAt: result.doc['createdAt'],
          description: result.doc['description'],
          imageUrl: result.doc['imageUrl'],
          price: result.doc['price'],
          streetName: result.doc['streetName'],
          updatedAt: result.doc['updatedAt'],
          zipcode: result.doc['zipcode'],
        );
        _loadedPersonalApartment.add(personalApartment);
     });
     personalApartmentList.apartmentListByCity = _loadedPersonalApartment;
   } else {
    QuerySnapshot snapshot = await FirebaseFirestore.instance.collection('apartments').where("city", 
    isEqualTo: city).where("price",isGreaterThanOrEqualTo: minPrice, isLessThanOrEqualTo: maxPrice)
    .get();
     List<PersonalApartment> _loadedPersonalApartment = [];
     snapshot.docChanges.forEach((result) {
     PersonalApartment personalApartment = new PersonalApartment(
       id: result.doc['id'],
       userId: result.doc['userId'],
       amenities: result.doc['amenities'],
       area: result.doc['area'],
       bathroom: result.doc['bathroom'],
       bedroom: result.doc['bedroom'],
       city: result.doc['city'],
       createdAt: result.doc['createdAt'],
       description: result.doc['description'],
       imageUrl: result.doc['imageUrl'],
       price: result.doc['price'],
       streetName: result.doc['streetName'],
       updatedAt: result.doc['updatedAt'],
       zipcode: result.doc['zipcode'],
      );
      _loadedPersonalApartment.add(personalApartment);
      });
       personalApartmentList.apartmentListByCity = _loadedPersonalApartment;
  }   
}

Example of Sample Queries

  QuerySnapshot snapshot = await FirebaseFirestore.instance.collection('apartments').where("price", 
   isGreaterThanOrEqualTo: minPrice, isLessThanOrEqualTo: maxPrice).get();
  QuerySnapshot snapshot = await FirebaseFirestore.instance.collection('apartments').where("area", 
   isGreaterThanOrEqualTo: areaRange.start, isLessThanOrEqualTo: areaRange.end).get();
  QuerySnapshot snapshot = await FirebaseFirestore.instance.collection('apartments').
    where("bathroom", arrayContainsAny: bathroom).get();
  QuerySnapshot snapshot = await FirebaseFirestore.instance.collection('apartments').where("bedroom", 
    arrayContainsAny: bedroom).get();

Filter Screen

[![Filter Screen][1]] [![Filter Screen][2]]

Upvotes: 0

Views: 492

Answers (1)

Thierry
Thierry

Reputation: 8383

I suppose you are using Firestore.

Now, you want to query Firestore for apartments based on:

  • Values
    • City
    • Zipcode
  • Ranges
    • Price Range
    • Area Range
    • Bathrooms
    • Bedrooms
  • Arrays
    • Amenities

What you want to do is to use Compound Queries:

Query query = FirebaseFirestore.instance.collection('apartments');
if (city.isNotEmpty && city != "Any") {
  query = query.where("city", isEqualTo: city)
}
if (zipCode.isNotEmpty && zipCode != "Any") {
  query = query.where("zipCode", isEqualTo: zipCode)
}
...
QuerySnapshot snapshot = await query.get();
List<PersonalApartment> apartments = snapshot.docChanges.map(
  (result) => PersonalApartment.fromFirestore(result.doc),
);

Your main problem will be that Compound Queries are very limited in Firestore. One of the limitations is that you cannot combine range filters on multiple fields.

So, you can only perform one range filter in your query and the other client-side within your application. Maybe you could determine an order on the range filters from the most restrictive (the one that will return the smallest set of apartments) to the most general.

Query _buildQuery() {
  Query query = FirebaseFirestore.instance.collection('apartments');

  // SIMPLE QUERIES
  if (city.isNotEmpty && city != "Any") {
    query = query.where("city", isEqualTo: city)
  }
  if (zipCode.isNotEmpty && zipCode != "Any") {
    query = query.where("zipCode", isEqualTo: zipCode)
  }

  // RANGE QUERIES
  if (minPrice != null || maxPrice != null) {
    if (minPrice != null) {
      query = query.where("price", ">=", minPrice);
    }
    if (maxPrice != null) {
      query = query.where("price", "<=", maxPrice);
    }
    return query;
  }
  if (minArea != null || maxArea != null) {
    ...
    return query;
  }
  if (...) { ... }
  ...
  return query;
}

...

QuerySnapshot snapshot = await _buildQuery().get();
List<PersonalApartment> apartments = snapshot.docChanges.map(
  (result) => PersonalApartment.fromFirestore(result.doc),
);
apartments = apartments.where((apartment) { /* perform remaining filters clmient-side */ })

Upvotes: 2

Related Questions