Skip to content

Commit 6cb554b

Browse files
authored
fix: remove null value inclusion from isNotEqualTo and notIn filter results (#14704)
1 parent 7e20da3 commit 6cb554b

File tree

5 files changed

+67
-4
lines changed

5 files changed

+67
-4
lines changed

Firestore/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Unreleased
2+
- [fixed] Fixed the `null` value handling in `isNotEqualTo` and `notIn` filters.
3+
14
# 11.11.0
25
- [fixed] Fixed the customized priority queue compare function used cache index manager. (#14496)
36

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,35 @@ - (void)testQueriesCanUseNotEqualFiltersWithDocIds {
537537
(@[ testDocs[@"ab"], testDocs[@"ba"], testDocs[@"bb"] ]));
538538
}
539539

540+
- (void)testSDKUsesNotEqualFiltersSameAsServer {
541+
NSDictionary *testDocs = @{
542+
@"a" : @{@"zip" : @(NAN)},
543+
@"b" : @{@"zip" : @91102},
544+
@"c" : @{@"zip" : @98101},
545+
@"d" : @{@"zip" : @"98101"},
546+
@"e" : @{@"zip" : @[ @98101 ]},
547+
@"f" : @{@"zip" : @[ @98101, @98102 ]},
548+
@"g" : @{@"zip" : @[ @"98101", @{@"zip" : @98101} ]},
549+
@"h" : @{@"zip" : @{@"code" : @500}},
550+
@"i" : @{@"zip" : [NSNull null]},
551+
@"j" : @{@"code" : @500},
552+
};
553+
FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
554+
555+
// populate cache with all documents first to ensure getDocsFromCache() scans all docs
556+
[self readDocumentSetForRef:collection];
557+
558+
[self checkOnlineAndOfflineQuery:[collection queryWhereField:@"zip" isNotEqualTo:@98101]
559+
matchesResult:@[ @"a", @"b", @"d", @"e", @"f", @"g", @"h" ]];
560+
561+
[self checkOnlineAndOfflineQuery:[collection queryWhereField:@"zip" isNotEqualTo:@(NAN)]
562+
matchesResult:@[ @"b", @"c", @"d", @"e", @"f", @"g", @"h" ]];
563+
564+
[self checkOnlineAndOfflineQuery:[collection queryWhereField:@"zip"
565+
isNotEqualTo:@[ [NSNull null] ]]
566+
matchesResult:@[ @"a", @"b", @"c", @"d", @"e", @"f", @"g", @"h" ]];
567+
}
568+
540569
- (void)testQueriesCanUseArrayContainsFilters {
541570
NSDictionary *testDocs = @{
542571
@"a" : @{@"array" : @[ @42 ]},
@@ -694,6 +723,33 @@ - (void)testQueriesCanUseNotInFiltersWithDocIds {
694723
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ testDocs[@"ba"], testDocs[@"bb"] ]));
695724
}
696725

726+
- (void)testSDKUsesNotInFiltersSameAsServer {
727+
NSDictionary *testDocs = @{
728+
@"a" : @{@"zip" : @(NAN)},
729+
@"b" : @{@"zip" : @91102},
730+
@"c" : @{@"zip" : @98101},
731+
@"d" : @{@"zip" : @"98101"},
732+
@"e" : @{@"zip" : @[ @98101 ]},
733+
@"f" : @{@"zip" : @[ @98101, @98102 ]},
734+
@"g" : @{@"zip" : @[ @"98101", @{@"zip" : @98101} ]},
735+
@"h" : @{@"zip" : @{@"code" : @500}},
736+
@"i" : @{@"zip" : [NSNull null]},
737+
@"j" : @{@"code" : @500},
738+
};
739+
FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
740+
741+
// populate cache with all documents first to ensure getDocsFromCache() scans all docs
742+
[self readDocumentSetForRef:collection];
743+
744+
[self checkOnlineAndOfflineQuery:[collection
745+
queryWhereField:@"zip"
746+
notIn:@[ @98101, @98103, @[ @98101, @98102 ] ]]
747+
matchesResult:@[ @"a", @"b", @"d", @"e", @"g", @"h" ]];
748+
749+
[self checkOnlineAndOfflineQuery:[collection queryWhereField:@"zip" notIn:@[ [NSNull null] ]]
750+
matchesResult:@[]];
751+
}
752+
697753
- (void)testQueriesCanUseArrayContainsAnyFilters {
698754
NSDictionary *testDocs = @{
699755
@"a" : @{@"array" : @[ @42 ]},

Firestore/core/src/core/field_filter.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ bool FieldFilter::Rep::Matches(const model::Document& doc) const {
157157

158158
// Types do not have to match in NotEqual filters.
159159
if (op_ == Operator::NotEqual) {
160-
return MatchesComparison(Compare(lhs, *value_rhs_));
160+
return lhs.which_value_type != google_firestore_v1_Value_null_value_tag &&
161+
MatchesComparison(Compare(lhs, *value_rhs_));
161162
}
162163

163164
// Only compare types with matching backend order (such as double and int).

Firestore/core/src/core/not_in_filter.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ bool NotInFilter::Rep::Matches(const Document& doc) const {
6262
return false;
6363
}
6464
absl::optional<google_firestore_v1_Value> maybe_lhs = doc->field(field());
65-
return maybe_lhs && !Contains(array_value, *maybe_lhs);
65+
return maybe_lhs &&
66+
maybe_lhs->which_value_type !=
67+
google_firestore_v1_Value_null_value_tag &&
68+
!Contains(array_value, *maybe_lhs);
6669
}
6770

6871
} // namespace core

Firestore/core/test/unit/core/query_test.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ TEST(QueryTest, NanFilter) {
279279
EXPECT_THAT(query, Matches(doc3));
280280
EXPECT_THAT(query, Matches(doc4));
281281
EXPECT_THAT(query, Matches(doc5));
282-
EXPECT_THAT(query, Matches(doc6));
282+
EXPECT_THAT(query, Not(Matches(doc6)));
283283
}
284284

285285
TEST(QueryTest, ArrayContainsFilter) {
@@ -383,7 +383,7 @@ TEST(QueryTest, NotInFilters) {
383383

384384
// Null match.
385385
doc = Doc("collection/1", 0, Map("zip", nullptr));
386-
EXPECT_THAT(query, Matches(doc));
386+
EXPECT_THAT(query, Not(Matches(doc)));
387387

388388
// NAN match.
389389
doc = Doc("collection/1", 0, Map("zip", NAN));

0 commit comments

Comments
 (0)