Skip to content

Commit 1c1ca00

Browse files
author
Brian Chen
authored
Allow null/NAN values in filters (#6871)
1 parent ffe8675 commit 1c1ca00

File tree

5 files changed

+62
-70
lines changed

5 files changed

+62
-70
lines changed

Firestore/CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Unreleased
2-
- [Fixed] Remove explicit MobileCoreServices library linkage from podspec. (#6850)
2+
- [fixed] Remove explicit MobileCoreServices library linkage from podspec
3+
(#6850).
4+
- [fixed] Removed excess validation of null and NaN values in query filters.
5+
This more closely aligns the SDK with the Firestore backend, which has always
6+
accepted null and NaN for all operators, even though this isn't necessarily
7+
useful.
38

49
# v7.0.0
510
- [changed] **Breaking change:** Removed the `areTimestampsInSnapshotsEnabled`

Firestore/Example/Tests/Integration/API/FIRQueryTests.mm

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,9 @@ - (void)testQueriesCanUseArrayContainsFilters {
452452
@"a" : @{@"array" : @[ @42 ]},
453453
@"b" : @{@"array" : @[ @"a", @42, @"c" ]},
454454
@"c" : @{@"array" : @[ @41.999, @"42", @{@"a" : @[ @42 ]} ]},
455-
@"d" : @{@"array" : @[ @42 ], @"array2" : @[ @"bingo" ]}
455+
@"d" : @{@"array" : @[ @42 ], @"array2" : @[ @"bingo" ]},
456+
@"e" : @{@"array" : @[ [NSNull null] ]},
457+
@"f" : @{@"array" : @[ @(NAN) ]},
456458
};
457459
FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
458460

@@ -462,8 +464,15 @@ - (void)testQueriesCanUseArrayContainsFilters {
462464
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot),
463465
(@[ testDocs[@"a"], testDocs[@"b"], testDocs[@"d"] ]));
464466

465-
// NOTE: The backend doesn't currently support null, NaN, objects, or arrays, so there isn't much
466-
// of anything else interesting to test.
467+
// With null.
468+
snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"array"
469+
arrayContains:[NSNull null]]];
470+
XCTAssertTrue(snapshot.isEmpty);
471+
472+
// With NAN.
473+
snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"array"
474+
arrayContains:@(NAN)]];
475+
XCTAssertTrue(snapshot.isEmpty);
467476
}
468477

469478
- (void)testQueriesCanUseInFilters {
@@ -474,7 +483,9 @@ - (void)testQueriesCanUseInFilters {
474483
@"d" : @{@"zip" : @[ @98101 ]},
475484
@"e" : @{@"zip" : @[ @"98101", @{@"zip" : @98101} ]},
476485
@"f" : @{@"zip" : @{@"code" : @500}},
477-
@"g" : @{@"zip" : @[ @98101, @98102 ]}
486+
@"g" : @{@"zip" : @[ @98101, @98102 ]},
487+
@"h" : @{@"zip" : [NSNull null]},
488+
@"i" : @{@"zip" : @(NAN)}
478489
};
479490
FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
480491

@@ -489,6 +500,24 @@ - (void)testQueriesCanUseInFilters {
489500
snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"zip"
490501
in:@[ @{@"code" : @500} ]]];
491502
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ testDocs[@"f"] ]));
503+
504+
// With null.
505+
snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"zip" in:@[ [NSNull null] ]]];
506+
XCTAssertTrue(snapshot.isEmpty);
507+
508+
// With null and a value.
509+
snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"zip"
510+
in:@[ [NSNull null], @98101 ]]];
511+
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ testDocs[@"a"] ]));
512+
513+
// With NAN.
514+
snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"zip" in:@[ @(NAN) ]]];
515+
XCTAssertTrue(snapshot.isEmpty);
516+
517+
// With NAN and a value.
518+
snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"zip"
519+
in:@[ @(NAN), @98101 ]]];
520+
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ testDocs[@"a"] ]));
492521
}
493522

494523
- (void)testQueriesCanUseInFiltersWithDocIds {
@@ -584,6 +613,9 @@ - (void)testQueriesCanUseArrayContainsAnyFilters {
584613
@"e" : @{@"array" : @[ @43 ]},
585614
@"f" : @{@"array" : @[ @{@"a" : @42} ]},
586615
@"g" : @{@"array" : @42},
616+
@"h" : @{@"array" : @[ [NSNull null] ]},
617+
@"g" : @{@"array" : @[ @(NAN) ]},
618+
587619
};
588620
FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
589621

@@ -599,6 +631,26 @@ - (void)testQueriesCanUseArrayContainsAnyFilters {
599631
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[
600632
testDocs[@"f"],
601633
]));
634+
635+
// With null.
636+
snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"array"
637+
arrayContainsAny:@[ [NSNull null] ]]];
638+
XCTAssertTrue(snapshot.isEmpty);
639+
640+
// With null and a value.
641+
snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"array"
642+
arrayContainsAny:@[ [NSNull null], @43 ]]];
643+
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ testDocs[@"e"] ]));
644+
645+
// With NAN.
646+
snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"array"
647+
arrayContainsAny:@[ @(NAN) ]]];
648+
XCTAssertTrue(snapshot.isEmpty);
649+
650+
// With NAN and a value.
651+
snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"array"
652+
arrayContainsAny:@[ @(NAN), @43 ]]];
653+
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ testDocs[@"e"] ]));
602654
}
603655

604656
- (void)testCollectionGroupQueries {

Firestore/Example/Tests/Integration/API/FIRValidationTests.mm

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -415,20 +415,6 @@ - (void)testQueryWithNonPositiveLimitFails {
415415
@"Invalid Query. Query limit (-1) is invalid. Limit must be positive.");
416416
}
417417

418-
- (void)testNonEqualityQueriesOnNullOrNaNFail {
419-
NSString *expected = @"Invalid Query. Null supports only 'equalTo' and 'notEqualTo' comparisons.";
420-
FSTAssertThrows([[self collectionRef] queryWhereField:@"a" isGreaterThan:nil], expected);
421-
FSTAssertThrows([[self collectionRef] queryWhereField:@"a" isGreaterThan:[NSNull null]],
422-
expected);
423-
FSTAssertThrows([[self collectionRef] queryWhereField:@"a" arrayContains:nil], expected);
424-
FSTAssertThrows([[self collectionRef] queryWhereField:@"a" arrayContains:[NSNull null]],
425-
expected);
426-
427-
expected = @"Invalid Query. NaN supports only 'equalTo' and 'notEqualTo' comparisons.";
428-
FSTAssertThrows([[self collectionRef] queryWhereField:@"a" isGreaterThan:@(NAN)], expected);
429-
FSTAssertThrows([[self collectionRef] queryWhereField:@"a" arrayContains:@(NAN)], expected);
430-
}
431-
432418
- (void)testQueryCannotBeCreatedFromDocumentsMissingSortValues {
433419
FIRCollectionReference *testCollection =
434420
[self collectionRefWithDocuments:@{@"f" : @{@"v" : @"f", @"nosort" : @1.0}}];
@@ -787,20 +773,6 @@ - (void)testQueriesInAndArrayContainsAnyArrayRules {
787773
FSTAssertThrows(
788774
[coll queryWhereField:@"foo" notIn:values],
789775
@"Invalid Query. 'notIn' filters support a maximum of 10 elements in the value array.");
790-
791-
NSArray *withNullValues = @[ @1, [NSNull null] ];
792-
FSTAssertThrows([coll queryWhereField:@"foo" in:withNullValues],
793-
@"Invalid Query. 'in' filters cannot contain 'null' in the value array.");
794-
FSTAssertThrows(
795-
[coll queryWhereField:@"foo" arrayContainsAny:withNullValues],
796-
@"Invalid Query. 'arrayContainsAny' filters cannot contain 'null' in the value array.");
797-
798-
NSArray *withNaNValues = @[ @2, @(NAN) ];
799-
FSTAssertThrows([coll queryWhereField:@"foo" in:withNaNValues],
800-
@"Invalid Query. 'in' filters cannot contain 'NaN' in the value array.");
801-
FSTAssertThrows(
802-
[coll queryWhereField:@"foo" arrayContainsAny:withNaNValues],
803-
@"Invalid Query. 'arrayContainsAny' filters cannot contain 'NaN' in the value array.");
804776
}
805777

806778
#pragma mark - GeoPoint Validation

Firestore/core/src/api/query_core.cc

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -394,24 +394,6 @@ void Query::ValidateDisjunctiveFilterElements(
394394
" elements in the value array.",
395395
Describe(op));
396396
}
397-
398-
std::vector<FieldValue> array = field_value.array_value();
399-
for (const auto& val : array) {
400-
if (op == Operator::In || op == Operator::ArrayContainsAny) {
401-
if (val.is_null()) {
402-
ThrowInvalidArgument(
403-
"Invalid Query. '%s' filters cannot contain 'null' in"
404-
" the value array.",
405-
Describe(op));
406-
}
407-
if (val.is_nan()) {
408-
ThrowInvalidArgument(
409-
"Invalid Query. '%s' filters cannot contain 'NaN' in"
410-
" the value array.",
411-
Describe(op));
412-
}
413-
}
414-
}
415397
}
416398

417399
FieldValue Query::ParseExpectedReferenceValue(

Firestore/core/src/core/field_filter.cc

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -94,25 +94,6 @@ FieldFilter FieldFilter::Create(FieldPath path,
9494
CanonicalName(op));
9595
return KeyFieldFilter(std::move(path), op, std::move(value_rhs));
9696
}
97-
98-
} else if (value_rhs.type() == FieldValue::Type::Null) {
99-
if (op != Filter::Operator::Equal && op != Filter::Operator::NotEqual) {
100-
ThrowInvalidArgument(
101-
"Invalid Query. Null supports only 'equalTo' and 'notEqualTo' "
102-
"comparisons.");
103-
}
104-
Rep filter(std::move(path), op, std::move(value_rhs));
105-
return FieldFilter(std::make_shared<const Rep>(std::move(filter)));
106-
107-
} else if (value_rhs.is_nan()) {
108-
if (op != Filter::Operator::Equal && op != Filter::Operator::NotEqual) {
109-
ThrowInvalidArgument(
110-
"Invalid Query. NaN supports only 'equalTo' and 'notEqualTo' "
111-
"comparisons.");
112-
}
113-
Rep filter(std::move(path), op, std::move(value_rhs));
114-
return FieldFilter(std::make_shared<const Rep>(std::move(filter)));
115-
11697
} else if (op == Operator::ArrayContains) {
11798
return ArrayContainsFilter(std::move(path), std::move(value_rhs));
11899

0 commit comments

Comments
 (0)