where method

Query where(
  1. dynamic field, {
  2. dynamic isEqualTo,
  3. dynamic isNotEqualTo,
  4. dynamic isLessThan,
  5. dynamic isLessThanOrEqualTo,
  6. dynamic isGreaterThan,
  7. dynamic isGreaterThanOrEqualTo,
  8. dynamic arrayContains,
  9. List? arrayContainsAny,
  10. List? whereIn,
  11. List? whereNotIn,
  12. bool? isNull,
})

Creates and returns a new Query with additional filter on specified field. field refers to a field in a document.

The field may be a String consisting of a single field name (referring to a top level field in the document), or a series of field names separated by dots '.' (referring to a nested field in the document). Alternatively, the field can also be a FieldPath.

Only documents satisfying provided condition are included in the result set.

Implementation

Query where(
  dynamic field, {
  dynamic isEqualTo,
  dynamic isNotEqualTo,
  dynamic isLessThan,
  dynamic isLessThanOrEqualTo,
  dynamic isGreaterThan,
  dynamic isGreaterThanOrEqualTo,
  dynamic arrayContains,
  List<dynamic>? arrayContainsAny,
  List<dynamic>? whereIn,
  List<dynamic>? whereNotIn,
  bool? isNull,
}) {
  _assertValidFieldType(field);

  const ListEquality<dynamic> equality = ListEquality<dynamic>();
  final List<List<dynamic>> conditions =
      List<List<dynamic>>.from(parameters['where']);

  // Conditions can be chained from other [Query] instances
  void addCondition(dynamic field, String operator, dynamic value) {
    List<dynamic> condition;
    dynamic codecValue = _CodecUtility.valueEncode(value);

    if (field == FieldPath.documentId) {
      condition = <dynamic>[field, operator, codecValue];
    } else {
      FieldPath fieldPath =
          field is String ? FieldPath.fromString(field) : field as FieldPath;
      condition = <dynamic>[fieldPath, operator, codecValue];
    }

    assert(
        conditions
            .where((List<dynamic> item) => equality.equals(condition, item))
            .isEmpty,
        'Condition $condition already exists in this query.');
    conditions.add(condition);
  }

  if (isEqualTo != null) addCondition(field, '==', isEqualTo);
  if (isNotEqualTo != null) addCondition(field, '!=', isNotEqualTo);
  if (isLessThan != null) addCondition(field, '<', isLessThan);
  if (isLessThanOrEqualTo != null) {
    addCondition(field, '<=', isLessThanOrEqualTo);
  }
  if (isGreaterThan != null) addCondition(field, '>', isGreaterThan);
  if (isGreaterThanOrEqualTo != null) {
    addCondition(field, '>=', isGreaterThanOrEqualTo);
  }
  if (arrayContains != null) {
    addCondition(field, 'array-contains', arrayContains);
  }
  if (arrayContainsAny != null) {
    addCondition(field, 'array-contains-any', arrayContainsAny);
  }
  if (whereIn != null) addCondition(field, 'in', whereIn);
  if (whereNotIn != null) addCondition(field, 'not-in', whereNotIn);
  if (isNull != null) {
    assert(
        isNull,
        'isNull can only be set to true. '
        'Use isEqualTo to filter on non-null values.');
    addCondition(field, '==', null);
  }

  dynamic hasInequality;
  bool hasIn = false;
  bool hasNotIn = false;
  bool hasNotEqualTo = false;
  bool hasArrayContains = false;
  bool hasArrayContainsAny = false;

  // Once all conditions have been set, we must now check them to ensure the
  // query is valid.
  for (final dynamic condition in conditions) {
    dynamic field = condition[0]; // FieldPath or FieldPathType
    String operator = condition[1];
    dynamic value = condition[2];

    // Initial orderBy() parameter has to match every where() fieldPath parameter when
    // inequality operator is invoked
    List<List<dynamic>> orders = List.from(parameters['orderBy']);
    if (_isInequality(operator) && orders.isNotEmpty) {
      assert(field == orders[0][0],
          "The initial orderBy() field '$orders[0][0]' has to be the same as the where() field parameter '$field' when an inequality operator is invoked.");
    }

    if (value == null) {
      assert(operator == '==',
          'You can only perform equals comparisons on null.');
    }

    if (operator == 'in' ||
        operator == 'array-contains-any' ||
        operator == 'not-in') {
      assert(value is List,
          "A non-empty [List] is required for '$operator' filters.");
      assert((value as List).length <= 10,
          "'$operator' filters support a maximum of 10 elements in the value [List].");
      assert((value as List).isNotEmpty,
          "'$operator' filters require a non-empty [List].");
      assert((value as List).where((value) => value == null).isEmpty,
          "'$operator' filters cannot contain 'null' in the [List].");
    }

    if (operator == '!=') {
      assert(!hasNotEqualTo, "You cannot use '!=' filters more than once.");
      assert(!hasNotIn, "You cannot use '!=' filters with 'not-in' filters.");
      hasNotEqualTo = true;
    }

    if (operator == 'not-in') {
      assert(!hasNotIn, "You cannot use 'not-in' filters more than once.");
      assert(!hasNotEqualTo,
          "You cannot use 'not-in' filters with '!=' filters.");
    }

    if (operator == 'in') {
      assert(!hasIn, "You cannot use 'whereIn' filters more than once.");
      hasIn = true;
    }

    if (operator == 'array-contains') {
      assert(!hasArrayContains,
          "You cannot use 'array-contains' filters more than once.");
      hasArrayContains = true;
    }

    if (operator == 'array-contains-any') {
      assert(!hasArrayContainsAny,
          "You cannot use 'array-contains-any' filters more than once.");
      hasArrayContainsAny = true;
    }

    if (operator == 'array-contains-any' || operator == 'in') {
      assert(!(hasIn && hasArrayContainsAny),
          "You cannot use 'in' filters with 'array-contains-any' filters.");
    }

    if (operator == 'array-contains' || operator == 'array-contains-any') {
      assert(!(hasArrayContains && hasArrayContainsAny),
          "You cannot use both 'array-contains-any' or 'array-contains' filters together.");
    }

    if (_isInequality(operator)) {
      if (hasInequality == null) {
        hasInequality = field;
      } else {
        assert(hasInequality == field,
            "All where filters with an inequality (<, <=, >, or >=) must be on the same field. But you have inequality filters on '$hasInequality' and '$field'.");
      }
    }
  }

  return Query._(firestore, _delegate.where(conditions));
}