Skip to content

Commit 86b09fa

Browse files
author
Brian Chen
authored
Add IN and ARRAY_CONTAINS_ANY (not publicly exposed) (#3492)
1 parent 3051291 commit 86b09fa

File tree

28 files changed

+1002
-74
lines changed

28 files changed

+1002
-74
lines changed

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

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
#import <XCTest/XCTest.h>
2020

21+
#import "Firestore/Source/API/FIRQuery+Internal.h"
22+
2123
#import "Firestore/Example/Tests/Util/FSTEventAccumulator.h"
2224
#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
2325

@@ -302,7 +304,7 @@ - (void)testCanHaveMultipleMutationsWhileOffline {
302304
]));
303305
}
304306

305-
- (void)testArrayContainsQueries {
307+
- (void)testQueriesCanUseArrayContainsFilters {
306308
NSDictionary *testDocs = @{
307309
@"a" : @{@"array" : @[ @42 ]},
308310
@"b" : @{@"array" : @[ @"a", @42, @"c" ]},
@@ -314,15 +316,85 @@ - (void)testArrayContainsQueries {
314316
// Search for 42
315317
FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"array"
316318
arrayContains:@42]];
317-
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[
318-
@{@"array" : @[ @42 ]}, @{@"array" : @[ @"a", @42, @"c" ]},
319-
@{@"array" : @[ @42 ], @"array2" : @[ @"bingo" ]}
320-
]));
319+
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot),
320+
(@[ testDocs[@"a"], testDocs[@"b"], testDocs[@"d"] ]));
321321

322322
// NOTE: The backend doesn't currently support null, NaN, objects, or arrays, so there isn't much
323323
// of anything else interesting to test.
324324
}
325325

326+
- (void)testQueriesCanUseInFilters {
327+
// TODO(b/138855186): Re-enable in prod once feature lands in backend.
328+
if (![FSTIntegrationTestCase isRunningAgainstEmulator]) return;
329+
330+
NSDictionary *testDocs = @{
331+
@"a" : @{@"zip" : @98101},
332+
@"b" : @{@"zip" : @91102},
333+
@"c" : @{@"zip" : @98103},
334+
@"d" : @{@"zip" : @[ @98101 ]},
335+
@"e" : @{@"zip" : @[ @"98101", @{@"zip" : @98101} ]},
336+
@"f" : @{@"zip" : @{@"code" : @500}}
337+
};
338+
FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
339+
340+
// Search for zips matching [98101, 98103].
341+
FIRQuerySnapshot *snapshot =
342+
[self readDocumentSetForRef:[collection queryWhereField:@"zip" in:@[ @98101, @98103 ]]];
343+
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ testDocs[@"a"], testDocs[@"c"] ]));
344+
345+
// With objects
346+
snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"zip"
347+
in:@[ @{@"code" : @500} ]]];
348+
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ testDocs[@"f"] ]));
349+
}
350+
351+
- (void)testQueriesCanUseInFiltersWithDocIds {
352+
// TODO(b/138855186): Re-enable in prod once feature lands in backend.
353+
if (![FSTIntegrationTestCase isRunningAgainstEmulator]) return;
354+
355+
NSDictionary *testDocs = @{
356+
@"aa" : @{@"key" : @"aa"},
357+
@"ab" : @{@"key" : @"ab"},
358+
@"ba" : @{@"key" : @"ba"},
359+
@"bb" : @{@"key" : @"bb"},
360+
};
361+
FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
362+
363+
FIRQuerySnapshot *snapshot =
364+
[self readDocumentSetForRef:[collection queryWhereFieldPath:[FIRFieldPath documentID]
365+
in:@[ @"aa", @"ab" ]]];
366+
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ testDocs[@"aa"], testDocs[@"ab"] ]));
367+
}
368+
369+
- (void)testQueriesCanUseArrayContainsAnyFilters {
370+
// TODO(b/138855186): Re-enable in prod once feature lands in backend.
371+
if (![FSTIntegrationTestCase isRunningAgainstEmulator]) return;
372+
373+
NSDictionary *testDocs = @{
374+
@"a" : @{@"array" : @[ @42 ]},
375+
@"b" : @{@"array" : @[ @"a", @42, @"c" ]},
376+
@"c" : @{@"array" : @[ @41.999, @"42", @{@"a" : @[ @42 ]} ]},
377+
@"d" : @{@"array" : @[ @42 ], @"array2" : @[ @"bingo" ]},
378+
@"e" : @{@"array" : @[ @43 ]},
379+
@"f" : @{@"array" : @[ @{@"a" : @42} ]},
380+
@"g" : @{@"array" : @42},
381+
};
382+
FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
383+
384+
// Search for zips matching [42, 43].
385+
FIRQuerySnapshot *snapshot = [self
386+
readDocumentSetForRef:[collection queryWhereField:@"array" arrayContainsAny:@[ @42, @43 ]]];
387+
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot),
388+
(@[ testDocs[@"a"], testDocs[@"b"], testDocs[@"d"], testDocs[@"e"] ]));
389+
390+
// With objects.
391+
snapshot = [self readDocumentSetForRef:[collection queryWhereField:@"array"
392+
arrayContainsAny:@[ @{@"a" : @42} ]]];
393+
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[
394+
testDocs[@"f"],
395+
]));
396+
}
397+
326398
- (void)testCollectionGroupQueries {
327399
// Use .document() to get a random collection group name to use but ensure it starts with 'b'
328400
// for predictable ordering.

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

Lines changed: 139 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <limits>
2222

2323
#import "Firestore/Source/API/FIRFieldValue+Internal.h"
24+
#import "Firestore/Source/API/FIRQuery+Internal.h"
2425

2526
#import "Firestore/Example/Tests/Util/FSTHelpers.h"
2627
#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
@@ -399,6 +400,10 @@ - (void)testNonEqualityQueriesOnNullOrNaNFail {
399400
@"Invalid Query. Null supports only equality comparisons.");
400401
FSTAssertThrows([[self collectionRef] queryWhereField:@"a" arrayContains:[NSNull null]],
401402
@"Invalid Query. Null supports only equality comparisons.");
403+
FSTAssertThrows([[self collectionRef] queryWhereField:@"a" arrayContainsAny:[NSNull null]],
404+
@"Invalid Query. A non-empty array is required for 'arrayContainsAny' filters.");
405+
FSTAssertThrows([[self collectionRef] queryWhereField:@"a" arrayContainsAny:[NSNull null]],
406+
@"Invalid Query. A non-empty array is required for 'arrayContainsAny' filters.");
402407

403408
FSTAssertThrows([[self collectionRef] queryWhereField:@"a" isGreaterThan:@(NAN)],
404409
@"Invalid Query. NaN supports only equality comparisons.");
@@ -511,7 +516,7 @@ - (void)testQueryMustNotSpecifyStartingOrEndingPointAfterOrder {
511516
FSTAssertThrows([[query queryEndingBeforeValues:@[ @1 ]] queryOrderedByField:@"bar"], reason);
512517
}
513518

514-
- (void)testQueriesFilteredByDocumentIDMustUseStringsOrDocumentReferences {
519+
- (void)testQueriesFilteredByDocumentIdMustUseStringsOrDocumentReferences {
515520
FIRCollectionReference *collection = [self collectionRef];
516521
NSString *reason = @"Invalid query. When querying by document ID you must provide a valid "
517522
"document ID, but it was an empty string.";
@@ -539,6 +544,37 @@ - (void)testQueriesFilteredByDocumentIDMustUseStringsOrDocumentReferences {
539544
"are not arrays.";
540545
FSTAssertThrows([collection queryWhereFieldPath:[FIRFieldPath documentID] arrayContains:@1],
541546
reason);
547+
548+
reason = @"Invalid query. You can't perform arrayContainsAny queries on document ID since "
549+
@"document IDs "
550+
"are not arrays.";
551+
FSTAssertThrows([collection queryWhereFieldPath:[FIRFieldPath documentID] arrayContainsAny:@1],
552+
reason);
553+
}
554+
555+
- (void)testQueriesUsingInAndDocumentIdMustHaveProperDocumentReferencesInArray {
556+
FIRCollectionReference *collection = [self collectionRef];
557+
NSString *reason = @"Invalid query. When querying by document ID you must provide a valid "
558+
"document ID, but it was an empty string.";
559+
FSTAssertThrows([collection queryWhereFieldPath:[FIRFieldPath documentID] in:@[ @"" ]], reason);
560+
561+
reason = @"Invalid query. When querying a collection by document ID you must provide a "
562+
"plain document ID, but 'foo/bar/baz' contains a '/' character.";
563+
FSTAssertThrows([collection queryWhereFieldPath:[FIRFieldPath documentID] in:@[ @"foo/bar/baz" ]],
564+
reason);
565+
566+
reason = @"Invalid query. When querying by document ID you must provide a valid string or "
567+
"DocumentReference, but it was of type: __NSArrayI";
568+
NSArray *value = @[ @1, @2 ];
569+
FSTAssertThrows([collection queryWhereFieldPath:[FIRFieldPath documentID] in:value], reason);
570+
571+
reason = @"Invalid query. When querying a collection group by document ID, the value "
572+
"provided must result in a valid document path, but 'foo' is not because it "
573+
"has an odd number of segments.";
574+
FSTAssertThrows(
575+
[[self.db collectionGroupWithID:@"collection"] queryWhereFieldPath:[FIRFieldPath documentID]
576+
in:@[ @"foo" ]],
577+
reason);
542578
}
543579

544580
- (void)testQueryInequalityFieldMustMatchFirstOrderByField {
@@ -569,6 +605,9 @@ - (void)testQueryInequalityFieldMustMatchFirstOrderByField {
569605
@"Inequality and equality on different fields works");
570606
XCTAssertNoThrow([base queryWhereField:@"y" arrayContains:@"cat"],
571607
@"Inequality and array_contains on different fields works");
608+
XCTAssertNoThrow([base queryWhereField:@"y" arrayContainsAny:@[ @"cat" ]],
609+
@"array-contains-any on different fields works");
610+
XCTAssertNoThrow([base queryWhereField:@"y" in:@[ @"cat" ]], @"IN on different fields works");
572611

573612
XCTAssertNoThrow([base queryOrderedByField:@"x"], @"inequality same as order by works");
574613
XCTAssertNoThrow([[coll queryOrderedByField:@"x"] queryWhereField:@"x" isGreaterThan:@32],
@@ -585,11 +624,108 @@ - (void)testQueryInequalityFieldMustMatchFirstOrderByField {
585624
@"array_contains different than orderBy works.");
586625
}
587626

588-
- (void)testQueryMustNotHaveMultipleArrayContainsFilters {
627+
- (void)testQueriesWithMultipleArrayFiltersFail {
589628
FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
590629
FSTAssertThrows([[coll queryWhereField:@"foo" arrayContains:@1] queryWhereField:@"foo"
591630
arrayContains:@2],
592-
@"Invalid Query. Queries only support a single arrayContains filter.");
631+
@"Invalid Query. You cannot use more than one 'arrayContains' filter.");
632+
633+
FSTAssertThrows(
634+
[[coll queryWhereField:@"foo" arrayContains:@1] queryWhereField:@"foo"
635+
arrayContainsAny:@[ @2 ]],
636+
@"Invalid Query. You cannot use 'arrayContainsAny' filters with 'arrayContains' filters.");
637+
638+
FSTAssertThrows(
639+
[[coll queryWhereField:@"foo" arrayContainsAny:@[ @1 ]] queryWhereField:@"foo"
640+
arrayContains:@2],
641+
@"Invalid Query. You cannot use 'arrayContains' filters with 'arrayContainsAny' filters.");
642+
}
643+
644+
- (void)testQueriesWithMultipleDisjunctiveFiltersFail {
645+
FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
646+
FSTAssertThrows([[coll queryWhereField:@"foo" in:@[ @1 ]] queryWhereField:@"foo" in:@[ @2 ]],
647+
@"Invalid Query. You cannot use more than one 'in' filter.");
648+
649+
FSTAssertThrows([[coll queryWhereField:@"foo" arrayContainsAny:@[ @1 ]] queryWhereField:@"foo"
650+
arrayContainsAny:@[ @2 ]],
651+
@"Invalid Query. You cannot use more than one 'arrayContainsAny' filter.");
652+
653+
FSTAssertThrows([[coll queryWhereField:@"foo" arrayContainsAny:@[ @1 ]] queryWhereField:@"foo"
654+
in:@[ @2 ]],
655+
@"Invalid Query. You cannot use 'in' filters with 'arrayContainsAny' filters.");
656+
657+
FSTAssertThrows([[coll queryWhereField:@"foo" in:@[ @1 ]] queryWhereField:@"foo"
658+
arrayContainsAny:@[ @2 ]],
659+
@"Invalid Query. You cannot use 'arrayContainsAny' filters with 'in' filters.");
660+
661+
// This is redundant with the above tests, but makes sure our validation doesn't get confused.
662+
FSTAssertThrows([[[coll queryWhereField:@"foo"
663+
in:@[ @1 ]] queryWhereField:@"foo"
664+
arrayContains:@2] queryWhereField:@"foo"
665+
arrayContainsAny:@[ @2 ]],
666+
@"Invalid Query. You cannot use 'arrayContainsAny' filters with 'in' filters.");
667+
668+
FSTAssertThrows([[[coll queryWhereField:@"foo"
669+
arrayContains:@1] queryWhereField:@"foo"
670+
in:@[ @2 ]] queryWhereField:@"foo"
671+
arrayContainsAny:@[ @2 ]],
672+
@"Invalid Query. You cannot use 'arrayContainsAny' filters with 'in' filters.");
673+
}
674+
675+
- (void)testQueriesCanUseInWithArrayContain {
676+
FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
677+
XCTAssertNoThrow([[coll queryWhereField:@"foo" arrayContains:@1] queryWhereField:@"foo"
678+
in:@[ @2 ]],
679+
@"arrayContains with IN works.");
680+
681+
XCTAssertNoThrow([[coll queryWhereField:@"foo" in:@[ @1 ]] queryWhereField:@"foo"
682+
arrayContains:@2],
683+
@"IN with arrayContains works.");
684+
685+
FSTAssertThrows([[[coll queryWhereField:@"foo"
686+
in:@[ @1 ]] queryWhereField:@"foo"
687+
arrayContains:@2] queryWhereField:@"foo"
688+
arrayContains:@3],
689+
@"Invalid Query. You cannot use more than one 'arrayContains' filter.");
690+
691+
FSTAssertThrows([[[coll queryWhereField:@"foo"
692+
arrayContains:@1] queryWhereField:@"foo"
693+
in:@[ @2 ]] queryWhereField:@"foo"
694+
in:@[ @3 ]],
695+
@"Invalid Query. You cannot use more than one 'in' filter.");
696+
}
697+
698+
- (void)testQueriesInAndArrayContainsAnyArrayRules {
699+
FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
700+
701+
FSTAssertThrows([coll queryWhereField:@"foo" in:@[]],
702+
@"Invalid Query. A non-empty array is required for 'in' filters.");
703+
704+
FSTAssertThrows([coll queryWhereField:@"foo" arrayContainsAny:@[]],
705+
@"Invalid Query. A non-empty array is required for 'arrayContainsAny' filters.");
706+
707+
// The 10 element max includes duplicates.
708+
NSArray *values = @[ @1, @2, @3, @4, @5, @6, @7, @8, @9, @9, @9 ];
709+
FSTAssertThrows(
710+
[coll queryWhereField:@"foo" in:values],
711+
@"Invalid Query. 'in' filters support a maximum of 10 elements in the value array.");
712+
FSTAssertThrows([coll queryWhereField:@"foo" arrayContainsAny:values],
713+
@"Invalid Query. 'arrayContainsAny' filters support a maximum of 10 elements"
714+
" in the value array.");
715+
716+
NSArray *withNullValues = @[ @1, [NSNull null] ];
717+
FSTAssertThrows([coll queryWhereField:@"foo" in:withNullValues],
718+
@"Invalid Query. 'in' filters cannot contain 'null' in the value array.");
719+
FSTAssertThrows(
720+
[coll queryWhereField:@"foo" arrayContainsAny:withNullValues],
721+
@"Invalid Query. 'arrayContainsAny' filters cannot contain 'null' in the value array.");
722+
723+
NSArray *withNaNValues = @[ @2, @(NAN) ];
724+
FSTAssertThrows([coll queryWhereField:@"foo" in:withNaNValues],
725+
@"Invalid Query. 'in' filters cannot contain 'NaN' in the value array.");
726+
FSTAssertThrows(
727+
[coll queryWhereField:@"foo" arrayContainsAny:withNaNValues],
728+
@"Invalid Query. 'arrayContainsAny' filters cannot contain 'NaN' in the value array.");
593729
}
594730

595731
#pragma mark - GeoPoint Validation

Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,36 @@ - (void)testEncodesArrayContainsFilter {
578578
XCTAssertEqual(*input, *roundTripped);
579579
}
580580

581+
- (void)testEncodesArrayContainsAnyFilter {
582+
auto input = Filter("item.tags", "array-contains-any", Array("food"));
583+
GCFSStructuredQuery_Filter *actual = [self.serializer encodedUnaryOrFieldFilter:*input];
584+
585+
GCFSStructuredQuery_Filter *expected = [GCFSStructuredQuery_Filter message];
586+
GCFSStructuredQuery_FieldFilter *prop = expected.fieldFilter;
587+
prop.field.fieldPath = @"item.tags";
588+
prop.op = GCFSStructuredQuery_FieldFilter_Operator_ArrayContainsAny;
589+
[prop.value.arrayValue.valuesArray addObject:[self.serializer encodedString:"food"]];
590+
XCTAssertEqualObjects(actual, expected);
591+
592+
auto roundTripped = [self.serializer decodedFieldFilter:prop];
593+
XCTAssertEqual(*input, *roundTripped);
594+
}
595+
596+
- (void)testEncodesInFilter {
597+
auto input = Filter("item.tags", "in", Array("food"));
598+
GCFSStructuredQuery_Filter *actual = [self.serializer encodedUnaryOrFieldFilter:*input];
599+
600+
GCFSStructuredQuery_Filter *expected = [GCFSStructuredQuery_Filter message];
601+
GCFSStructuredQuery_FieldFilter *prop = expected.fieldFilter;
602+
prop.field.fieldPath = @"item.tags";
603+
prop.op = GCFSStructuredQuery_FieldFilter_Operator_In;
604+
[prop.value.arrayValue.valuesArray addObject:[self.serializer encodedString:"food"]];
605+
XCTAssertEqualObjects(actual, expected);
606+
607+
auto roundTripped = [self.serializer decodedFieldFilter:prop];
608+
XCTAssertEqual(*input, *roundTripped);
609+
}
610+
581611
- (void)testEncodesKeyFieldFilter {
582612
auto input = Filter("__name__", "==", Ref("p/d", "coll/doc"));
583613
GCFSStructuredQuery_Filter *actual = [self.serializer encodedUnaryOrFieldFilter:*input];

Firestore/Protos/cpp/google/firestore/v1/query.pb.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,8 @@ const StructuredQuery_FieldFilter_Operator StructuredQuery_FieldFilter::GREATER_
566566
const StructuredQuery_FieldFilter_Operator StructuredQuery_FieldFilter::GREATER_THAN_OR_EQUAL;
567567
const StructuredQuery_FieldFilter_Operator StructuredQuery_FieldFilter::EQUAL;
568568
const StructuredQuery_FieldFilter_Operator StructuredQuery_FieldFilter::ARRAY_CONTAINS;
569+
const StructuredQuery_FieldFilter_Operator StructuredQuery_FieldFilter::IN;
570+
const StructuredQuery_FieldFilter_Operator StructuredQuery_FieldFilter::ARRAY_CONTAINS_ANY;
569571
const StructuredQuery_FieldFilter_Operator StructuredQuery_FieldFilter::Operator_MIN;
570572
const StructuredQuery_FieldFilter_Operator StructuredQuery_FieldFilter::Operator_MAX;
571573
const int StructuredQuery_FieldFilter::Operator_ARRAYSIZE;

Firestore/Protos/cpp/google/firestore/v1/query.pb.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ enum StructuredQuery_FieldFilter_Operator {
161161
StructuredQuery_FieldFilter_Operator_GREATER_THAN_OR_EQUAL = 4,
162162
StructuredQuery_FieldFilter_Operator_EQUAL = 5,
163163
StructuredQuery_FieldFilter_Operator_ARRAY_CONTAINS = 7,
164+
StructuredQuery_FieldFilter_Operator_IN = 8,
165+
StructuredQuery_FieldFilter_Operator_ARRAY_CONTAINS_ANY = 9,
164166
StructuredQuery_FieldFilter_Operator_StructuredQuery_FieldFilter_Operator_INT_MIN_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32min,
165167
StructuredQuery_FieldFilter_Operator_StructuredQuery_FieldFilter_Operator_INT_MAX_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32max
166168
};
@@ -714,6 +716,10 @@ class StructuredQuery_FieldFilter : public ::google::protobuf::Message /* @@prot
714716
StructuredQuery_FieldFilter_Operator_EQUAL;
715717
static const Operator ARRAY_CONTAINS =
716718
StructuredQuery_FieldFilter_Operator_ARRAY_CONTAINS;
719+
static const Operator IN =
720+
StructuredQuery_FieldFilter_Operator_IN;
721+
static const Operator ARRAY_CONTAINS_ANY =
722+
StructuredQuery_FieldFilter_Operator_ARRAY_CONTAINS_ANY;
717723
static inline bool Operator_IsValid(int value) {
718724
return StructuredQuery_FieldFilter_Operator_IsValid(value);
719725
}

Firestore/Protos/nanopb/google/firestore/v1/query.nanopb.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ typedef enum _google_firestore_v1_StructuredQuery_FieldFilter_Operator {
6161
google_firestore_v1_StructuredQuery_FieldFilter_Operator_GREATER_THAN = 3,
6262
google_firestore_v1_StructuredQuery_FieldFilter_Operator_GREATER_THAN_OR_EQUAL = 4,
6363
google_firestore_v1_StructuredQuery_FieldFilter_Operator_EQUAL = 5,
64-
google_firestore_v1_StructuredQuery_FieldFilter_Operator_ARRAY_CONTAINS = 7
64+
google_firestore_v1_StructuredQuery_FieldFilter_Operator_ARRAY_CONTAINS = 7,
65+
google_firestore_v1_StructuredQuery_FieldFilter_Operator_IN = 8,
66+
google_firestore_v1_StructuredQuery_FieldFilter_Operator_ARRAY_CONTAINS_ANY = 9
6567
} google_firestore_v1_StructuredQuery_FieldFilter_Operator;
6668
#define _google_firestore_v1_StructuredQuery_FieldFilter_Operator_MIN google_firestore_v1_StructuredQuery_FieldFilter_Operator_OPERATOR_UNSPECIFIED
6769
#define _google_firestore_v1_StructuredQuery_FieldFilter_Operator_MAX google_firestore_v1_StructuredQuery_FieldFilter_Operator_ARRAY_CONTAINS

0 commit comments

Comments
 (0)