where method
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));
}