Skip to content

Commit 0170957

Browse files
author
Brian Chen
authored
Allow querying for arrays in IN queries (#4285)
1 parent e6565d0 commit 0170957

File tree

6 files changed

+38
-8
lines changed

6 files changed

+38
-8
lines changed

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -330,14 +330,17 @@ - (void)testQueriesCanUseInFilters {
330330
@"c" : @{@"zip" : @98103},
331331
@"d" : @{@"zip" : @[ @98101 ]},
332332
@"e" : @{@"zip" : @[ @"98101", @{@"zip" : @98101} ]},
333-
@"f" : @{@"zip" : @{@"code" : @500}}
333+
@"f" : @{@"zip" : @{@"code" : @500}},
334+
@"g" : @{@"zip" : @[ @98101, @98102 ]}
334335
};
335336
FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
336337

337-
// Search for zips matching [98101, 98103].
338-
FIRQuerySnapshot *snapshot =
339-
[self readDocumentSetForRef:[collection queryWhereField:@"zip" in:@[ @98101, @98103 ]]];
340-
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ testDocs[@"a"], testDocs[@"c"] ]));
338+
// Search for zips matching 98101, 98103, and [98101, 98102].
339+
FIRQuerySnapshot *snapshot = [self
340+
readDocumentSetForRef:[collection queryWhereField:@"zip"
341+
in:@[ @98101, @98103, @[ @98101, @98102 ] ]]];
342+
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot),
343+
(@[ testDocs[@"a"], testDocs[@"c"], testDocs[@"g"] ]));
341344

342345
// With objects
343346
snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"zip"

Firestore/Source/API/FIRQuery.mm

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,10 @@ - (FieldValue)parsedQueryValue:(id)value {
429429
return [self.firestore.dataConverter parsedQueryValue:value];
430430
}
431431

432+
- (FieldValue)parsedQueryValue:(id)value allowArrays:(bool)allowArrays {
433+
return [self.firestore.dataConverter parsedQueryValue:value allowArrays:allowArrays];
434+
}
435+
432436
- (QuerySnapshot::Listener)wrapQuerySnapshotBlock:(FIRQuerySnapshotBlock)block {
433437
class Converter : public EventListener<QuerySnapshot> {
434438
public:
@@ -462,7 +466,8 @@ - (FIRQuery *)queryWithFilterOperator:(Filter::Operator)filterOperator
462466
- (FIRQuery *)queryWithFilterOperator:(Filter::Operator)filterOperator
463467
path:(const FieldPath &)fieldPath
464468
value:(id)value {
465-
FieldValue fieldValue = [self parsedQueryValue:value];
469+
FieldValue fieldValue = [self parsedQueryValue:value
470+
allowArrays:filterOperator == Filter::Operator::In];
466471
auto describer = [value] { return util::MakeString(NSStringFromClass([value class])); };
467472
return Wrap(_query.Filter(fieldPath, filterOperator, std::move(fieldValue), describer));
468473
}

Firestore/Source/API/FSTUserDataConverter.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ typedef id _Nullable (^FSTPreConverterBlock)(id _Nullable);
8282
/** Parse a "query value" (e.g. value in a where filter or a value in a cursor bound). */
8383
- (model::FieldValue)parsedQueryValue:(id)input;
8484

85+
/**
86+
* Parse a "query value" (e.g. value in a where filter or a value in a cursor bound).
87+
*
88+
* @param allowArrays Whether the query value is an array that may directly contain additional
89+
* arrays (e.g.) the operand of an `in` query).
90+
*/
91+
- (model::FieldValue)parsedQueryValue:(id)input allowArrays:(bool)allowArrays;
92+
8593
@end
8694

8795
NS_ASSUME_NONNULL_END

Firestore/Source/API/FSTUserDataConverter.mm

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,12 @@ - (ParsedUpdateData)parsedUpdateData:(id)input {
225225
}
226226

227227
- (FieldValue)parsedQueryValue:(id)input {
228-
ParseAccumulator accumulator{UserDataSource::Argument};
228+
return [self parsedQueryValue:input allowArrays:false];
229+
}
230+
231+
- (FieldValue)parsedQueryValue:(id)input allowArrays:(bool)allowArrays {
232+
ParseAccumulator accumulator{allowArrays ? UserDataSource::ArrayArgument
233+
: UserDataSource::Argument};
229234

230235
absl::optional<FieldValue> parsed = [self parseData:input context:accumulator.RootContext()];
231236
HARD_ASSERT(parsed, "Parsed data should not be nil.");
@@ -266,7 +271,10 @@ - (FieldValue)parsedQueryValue:(id)input {
266271

267272
if ([input isKindOfClass:[NSArray class]]) {
268273
// TODO(b/34871131): Include the path containing the array in the error message.
269-
if (context.array_element()) {
274+
// In the case of IN queries, the parsed data is an array (representing the set of values to
275+
// be included for the IN query) that may directly contain additional arrays (each
276+
// representing an individual field value), so we disable this validation.
277+
if (context.array_element() && context.data_source() != UserDataSource::ArrayArgument) {
270278
ThrowInvalidArgument("Nested arrays are not supported");
271279
}
272280
return [self parseArray:(NSArray *)input context:std::move(context)];

Firestore/core/src/firebase/firestore/core/user_data.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ bool ParseContext::write() const {
161161
case UserDataSource::Update:
162162
return true;
163163
case UserDataSource::Argument:
164+
case UserDataSource::ArrayArgument:
164165
return false;
165166
default:
166167
ThrowInvalidArgument("Unexpected case for UserDataSource: %s",

Firestore/core/src/firebase/firestore/core/user_data.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ enum class UserDataSource {
6161
* false.
6262
*/
6363
Argument,
64+
/**
65+
* Indicates that the source is an Argument that may directly contain nested
66+
* arrays (e.g. the operand of a `in` query).
67+
*/
68+
ArrayArgument
6469
};
6570

6671
/**

0 commit comments

Comments
 (0)