From 98e5faf5d0b5eb8761bad1b37458d698262b18ce Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Tue, 11 Jul 2023 11:49:21 -0500 Subject: [PATCH] feat: Add support to exclude `PFObject` fields in query results with `PFQuery.excludeKeys` (#1731) --- .../Internal/Commands/PFRESTQueryCommand.h | 2 ++ .../Internal/Commands/PFRESTQueryCommand.m | 12 ++++++++++ .../Query/State/PFMutableQueryState.h | 7 ++++++ .../Query/State/PFMutableQueryState.m | 23 +++++++++++++++++++ .../Parse/Internal/Query/State/PFQueryState.h | 1 + .../Parse/Internal/Query/State/PFQueryState.m | 1 + .../Query/State/PFQueryState_Private.h | 1 + Parse/Parse/Source/PFQuery.h | 22 ++++++++++++++++++ Parse/Parse/Source/PFQuery.m | 16 +++++++++++++ .../TestCases/UnitTestCase/PFUnitTestCase.m | 2 +- Parse/Tests/Unit/QueryStateUnitTests.m | 19 +++++++++++++++ Parse/Tests/Unit/QueryUnitTests.m | 13 +++++++++++ 12 files changed, 118 insertions(+), 1 deletion(-) diff --git a/Parse/Parse/Internal/Commands/PFRESTQueryCommand.h b/Parse/Parse/Internal/Commands/PFRESTQueryCommand.h index b8f75782b..1ef290a1d 100644 --- a/Parse/Parse/Internal/Commands/PFRESTQueryCommand.h +++ b/Parse/Parse/Internal/Commands/PFRESTQueryCommand.h @@ -28,6 +28,7 @@ NS_ASSUME_NONNULL_BEGIN conditions:(nullable NSDictionary *)conditions selectedKeys:(nullable NSSet *)selectedKeys includedKeys:(nullable NSSet *)includedKeys + excludedKeys:(nullable NSSet *)excludedKeys limit:(NSInteger)limit skip:(NSInteger)skip explain:(BOOL)explain @@ -54,6 +55,7 @@ NS_ASSUME_NONNULL_BEGIN conditions:(nullable NSDictionary *)conditions selectedKeys:(nullable NSSet *)selectedKeys includedKeys:(nullable NSSet *)includedKeys + excludedKeys:(nullable NSSet *)excludedKeys limit:(NSInteger)limit skip:(NSInteger)skip explain:(BOOL)explain diff --git a/Parse/Parse/Internal/Commands/PFRESTQueryCommand.m b/Parse/Parse/Internal/Commands/PFRESTQueryCommand.m index 06c0206bb..7cb65193c 100644 --- a/Parse/Parse/Internal/Commands/PFRESTQueryCommand.m +++ b/Parse/Parse/Internal/Commands/PFRESTQueryCommand.m @@ -36,6 +36,7 @@ + (nullable instancetype)findCommandForClassWithName:(NSString *)className conditions:(NSDictionary *)conditions selectedKeys:(NSSet *)selectedKeys includedKeys:(NSSet *)includedKeys + excludedKeys:(NSSet *)excludedKeys limit:(NSInteger)limit skip:(NSInteger)skip explain:(BOOL)explain @@ -48,6 +49,7 @@ + (nullable instancetype)findCommandForClassWithName:(NSString *)className conditions:conditions selectedKeys:selectedKeys includedKeys:includedKeys + excludedKeys:excludedKeys limit:limit skip:skip explain:explain @@ -102,6 +104,7 @@ + (nullable NSDictionary *)findCommandParametersForQueryState:(PFQueryState *)qu conditions:queryState.conditions selectedKeys:queryState.selectedKeys includedKeys:queryState.includedKeys + excludedKeys:queryState.excludedKeys limit:queryState.limit skip:queryState.skip explain:queryState.explain @@ -115,6 +118,7 @@ + (nullable NSDictionary *)findCommandParametersWithOrder:(NSString *)order conditions:(NSDictionary *)conditions selectedKeys:(NSSet *)selectedKeys includedKeys:(NSSet *)includedKeys + excludedKeys:(NSSet *)excludedKeys limit:(NSInteger)limit skip:(NSInteger)skip explain:(BOOL)explain @@ -137,6 +141,11 @@ + (nullable NSDictionary *)findCommandParametersWithOrder:(NSString *)order NSArray *keysArray = [includedKeys sortedArrayUsingDescriptors:sortDescriptors]; parameters[@"include"] = [keysArray componentsJoinedByString:@","]; } + if (excludedKeys.count > 0) { + NSArray *sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:YES selector:@selector(compare:)] ]; + NSArray *keysArray = [excludedKeys sortedArrayUsingDescriptors:sortDescriptors]; + parameters[@"excludedKeys"] = [keysArray componentsJoinedByString:@","]; + } if (limit >= 0) { parameters[@"limit"] = [NSString stringWithFormat:@"%d", (int)limit]; } @@ -171,12 +180,14 @@ + (nullable NSDictionary *)findCommandParametersWithOrder:(NSString *)order PFParameterAssert(subquery.state.skip == 0, @"OR queries do not support sub queries with skip"); PFParameterAssert(subquery.state.sortKeys.count == 0, @"OR queries do not support sub queries with order"); PFParameterAssert(subquery.state.includedKeys.count == 0, @"OR queries do not support sub-queries with includes"); + PFParameterAssert(subquery.state.excludedKeys.count == 0, @"OR queries do not support sub-queries with excludeKeys"); PFParameterAssert(subquery.state.selectedKeys == nil, @"OR queries do not support sub-queries with selectKeys"); NSDictionary *queryDict = [self findCommandParametersWithOrder:subquery.state.sortOrderString conditions:subquery.state.conditions selectedKeys:subquery.state.selectedKeys includedKeys:subquery.state.includedKeys + excludedKeys:subquery.state.excludedKeys limit:subquery.state.limit skip:subquery.state.skip explain:subquery.state.explain @@ -240,6 +251,7 @@ + (nullable id)_encodeSubqueryIfNeeded:(id)object error:(NSError * __autoreleasi conditions:subquery.state.conditions selectedKeys:subquery.state.selectedKeys includedKeys:subquery.state.includedKeys + excludedKeys:subquery.state.excludedKeys limit:subquery.state.limit skip:subquery.state.skip explain:subquery.state.explain diff --git a/Parse/Parse/Internal/Query/State/PFMutableQueryState.h b/Parse/Parse/Internal/Query/State/PFMutableQueryState.h index 78e3ba447..9e841a21e 100644 --- a/Parse/Parse/Internal/Query/State/PFMutableQueryState.h +++ b/Parse/Parse/Internal/Query/State/PFMutableQueryState.h @@ -70,6 +70,13 @@ - (void)includeKey:(NSString *)key; - (void)includeKeys:(NSArray *)keys; +///-------------------------------------- +#pragma mark - Excludes +///-------------------------------------- + +- (void)excludeKey:(NSString *)key; +- (void)excludeKeys:(NSArray *)keys; + ///-------------------------------------- #pragma mark - Selected Keys ///-------------------------------------- diff --git a/Parse/Parse/Internal/Query/State/PFMutableQueryState.m b/Parse/Parse/Internal/Query/State/PFMutableQueryState.m index 3031c0e8f..b1c2b1532 100644 --- a/Parse/Parse/Internal/Query/State/PFMutableQueryState.m +++ b/Parse/Parse/Internal/Query/State/PFMutableQueryState.m @@ -17,6 +17,7 @@ @interface PFMutableQueryState () { NSMutableDictionary *_conditions; NSMutableArray *_sortKeys; NSMutableSet *_includedKeys; + NSMutableSet *_excludedKeys; NSMutableDictionary *_extraOptions; } @@ -27,6 +28,7 @@ @implementation PFMutableQueryState @synthesize conditions = _conditions; @synthesize sortKeys = _sortKeys; @synthesize includedKeys = _includedKeys; +@synthesize excludedKeys = _excludedKeys; @synthesize extraOptions = _extraOptions; @dynamic parseClassName; @@ -53,6 +55,7 @@ + (NSDictionary *)propertyAttributes { attributes[PFQueryStatePropertyName(conditions)] = [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeMutableCopy]; attributes[PFQueryStatePropertyName(sortKeys)] = [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeMutableCopy]; attributes[PFQueryStatePropertyName(includedKeys)] = [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeMutableCopy]; + attributes[PFQueryStatePropertyName(excludedKeys)] = [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeMutableCopy]; attributes[PFQueryStatePropertyName(extraOptions)] = [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeMutableCopy]; return attributes; @@ -163,6 +166,26 @@ - (void)includeKeys:(NSArray *)keys { } } +///-------------------------------------- +#pragma mark - Excludes +///-------------------------------------- + +- (void)excludeKey:(NSString *)key { + if (!_excludedKeys) { + _excludedKeys = [NSMutableSet setWithObject:key]; + } else { + [_excludedKeys addObject:key]; + } +} + +- (void)excludeKeys:(NSArray *)keys { + if (!_excludedKeys) { + _excludedKeys = [NSMutableSet setWithArray:keys]; + } else { + [_excludedKeys addObjectsFromArray:keys]; + } +} + ///-------------------------------------- #pragma mark - Selected Keys ///-------------------------------------- diff --git a/Parse/Parse/Internal/Query/State/PFQueryState.h b/Parse/Parse/Internal/Query/State/PFQueryState.h index 5e541b292..85cfb138b 100644 --- a/Parse/Parse/Internal/Query/State/PFQueryState.h +++ b/Parse/Parse/Internal/Query/State/PFQueryState.h @@ -23,6 +23,7 @@ @property (nonatomic, copy, readonly) NSString *sortOrderString; @property (nonatomic, copy, readonly) NSSet *includedKeys; +@property (nonatomic, copy, readonly) NSSet *excludedKeys; @property (nonatomic, copy, readonly) NSSet *selectedKeys; @property (nonatomic, copy, readonly) NSDictionary *extraOptions; diff --git a/Parse/Parse/Internal/Query/State/PFQueryState.m b/Parse/Parse/Internal/Query/State/PFQueryState.m index 58707f308..02a280e7a 100644 --- a/Parse/Parse/Internal/Query/State/PFQueryState.m +++ b/Parse/Parse/Internal/Query/State/PFQueryState.m @@ -26,6 +26,7 @@ + (NSDictionary *)propertyAttributes { PFQueryStatePropertyName(conditions): [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], PFQueryStatePropertyName(sortKeys): [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], PFQueryStatePropertyName(includedKeys): [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + PFQueryStatePropertyName(excludedKeys): [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], PFQueryStatePropertyName(selectedKeys): [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], PFQueryStatePropertyName(extraOptions): [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], diff --git a/Parse/Parse/Internal/Query/State/PFQueryState_Private.h b/Parse/Parse/Internal/Query/State/PFQueryState_Private.h index f3a4e2e0d..918ab93a3 100644 --- a/Parse/Parse/Internal/Query/State/PFQueryState_Private.h +++ b/Parse/Parse/Internal/Query/State/PFQueryState_Private.h @@ -29,6 +29,7 @@ NSArray *_sortKeys; NSSet *_includedKeys; + NSSet *_excludedKeys; NSSet *_selectedKeys; NSDictionary *_extraOptions; diff --git a/Parse/Parse/Source/PFQuery.h b/Parse/Parse/Source/PFQuery.h index 7dfbdfcdc..3235997b9 100644 --- a/Parse/Parse/Source/PFQuery.h +++ b/Parse/Parse/Source/PFQuery.h @@ -107,6 +107,28 @@ typedef void (^PFQueryArrayResultBlock)(NSArray *_Nullable obje */ - (instancetype)includeKeys:(NSArray *)keys; +/** + Make the query restrict the fields of the returned `PFObject`s to exclude the provided key. + + If this is called multiple times, then all of the keys specified in each of the calls will be excluded. + + @param key The key to exclude in the result. + + @return The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)excludeKey:(NSString *)key; + +/** + Make the query restrict the fields of the returned `PFObject`s to exclude the provided keys. + + If this is called multiple times, then all of the keys specified in each of the calls will be excluded. + + @param keys The keys to exclude in the result. + + @return The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)excludeKeys:(NSArray *)keys; + /** Make the query restrict the fields of the returned `PFObject`s to include only the provided keys. diff --git a/Parse/Parse/Source/PFQuery.m b/Parse/Parse/Source/PFQuery.m index cdf2e00f7..4b4f4d625 100644 --- a/Parse/Parse/Source/PFQuery.m +++ b/Parse/Parse/Source/PFQuery.m @@ -426,6 +426,22 @@ - (instancetype)includeKeys:(NSArray *)keys { return self; } +///-------------------------------------- +#pragma mark - Exclude +///-------------------------------------- + +- (instancetype)excludeKey:(NSString *)key { + [self checkIfCommandIsRunning]; + [self.state excludeKey:key]; + return self; +} + +- (instancetype)excludeKeys:(NSArray *)keys { + [self checkIfCommandIsRunning]; + [self.state excludeKeys:keys]; + return self; +} + ///-------------------------------------- #pragma mark - Select ///-------------------------------------- diff --git a/Parse/Tests/Other/TestCases/UnitTestCase/PFUnitTestCase.m b/Parse/Tests/Other/TestCases/UnitTestCase/PFUnitTestCase.m index 828072a5a..3b5a38e1f 100644 --- a/Parse/Tests/Other/TestCases/UnitTestCase/PFUnitTestCase.m +++ b/Parse/Tests/Other/TestCases/UnitTestCase/PFUnitTestCase.m @@ -37,7 +37,7 @@ - (void)setUp { [Parse setApplicationId:self.applicationId clientKey:self.clientKey]; - [self waitForExpectations:@[expect] timeout:10]; + [self waitForExpectations:@[expect] timeout:30]; } - (void)tearDown { diff --git a/Parse/Tests/Unit/QueryStateUnitTests.m b/Parse/Tests/Unit/QueryStateUnitTests.m index 76a0fb093..56e18a3b6 100644 --- a/Parse/Tests/Unit/QueryStateUnitTests.m +++ b/Parse/Tests/Unit/QueryStateUnitTests.m @@ -41,6 +41,7 @@ - (PFQueryState *)sampleQueryState { [state sortByKey:@"a" ascending:NO]; [state includeKey:@"yolo"]; + [state excludeKey:@"yolo"]; [state selectKeys:@[ @"yolo" ]]; [state redirectClassNameForKey:@"ABC"]; return state; @@ -62,6 +63,7 @@ - (void)assertQueryState:(PFQueryState *)state equalToState:(PFQueryState *)diff XCTAssertEqualObjects(state.sortKeys, differentState.sortKeys); XCTAssertEqualObjects(state.sortOrderString, differentState.sortOrderString); XCTAssertEqualObjects(state.includedKeys, differentState.includedKeys); + XCTAssertEqualObjects(state.excludedKeys, differentState.excludedKeys); XCTAssertEqualObjects(state.selectedKeys, differentState.selectedKeys); XCTAssertEqualObjects(state.extraOptions, differentState.extraOptions); @@ -213,6 +215,23 @@ - (void)testIncludeMultipleKeys { XCTAssertEqualObjects(state.includedKeys, includedKeys); } +- (void)testExcludeKeys { + PFMutableQueryState *state = [[PFMutableQueryState alloc] initWithParseClassName:@"Yarr"]; + [state excludeKey:@"a"]; + [state excludeKey:@"b"]; + + NSSet *excludedKeys = PF_SET(@"a", @"b"); + XCTAssertEqualObjects(state.excludedKeys, excludedKeys); +} + +- (void)testExcludeMultipleKeys { + PFMutableQueryState *state = [[PFMutableQueryState alloc] initWithParseClassName:@"Yarr"]; + [state excludeKeys:@[ @"a", @"b", @"c" ]]; + + NSSet *excludedKeys = PF_SET(@"a", @"b", @"c"); + XCTAssertEqualObjects(state.excludedKeys, excludedKeys); +} + - (void)testSelectKeys { PFMutableQueryState *state = [[PFMutableQueryState alloc] initWithParseClassName:@"Yarr"]; diff --git a/Parse/Tests/Unit/QueryUnitTests.m b/Parse/Tests/Unit/QueryUnitTests.m index 93cdc2f97..2b2b0b257 100644 --- a/Parse/Tests/Unit/QueryUnitTests.m +++ b/Parse/Tests/Unit/QueryUnitTests.m @@ -261,6 +261,17 @@ - (void)testIncludeKey { XCTAssertTrue([query.state.includedKeys containsObject:@"yolo1"]); } +- (void)testExcludeKey { + PFQuery *query = [PFQuery queryWithClassName:@"a"]; + [query excludeKey:@"yolo"]; + XCTAssertEqualObjects(query.state.excludedKeys, (PF_SET(@"yolo"))); + + [query excludeKey:@"yolo1"]; + XCTAssertEqualObjects(query.state.excludedKeys, (PF_SET(@"yolo", @"yolo1"))); + XCTAssertTrue([query.state.excludedKeys containsObject:@"yolo"]); + XCTAssertTrue([query.state.excludedKeys containsObject:@"yolo1"]); +} + - (void)testSelectKeys { PFQuery *query = [PFQuery queryWithClassName:@"a"]; [query selectKeys:@[ @"a", @"a" ]]; @@ -1320,6 +1331,7 @@ - (void)testNSCopying { [query orderByAscending:@"b"]; [query includeKey:@"c"]; [query selectKeys:@[ @"d" ]]; + [query excludeKeys:@[ @"f" ]]; [query redirectClassNameForKey:@"e"]; query.limit = 10; @@ -1338,6 +1350,7 @@ - (void)testNSCopying { XCTAssertEqualObjects(queryCopy.state.conditions[@"a"], query.state.conditions[@"a"]); XCTAssertEqualObjects(queryCopy.state.sortOrderString, query.state.sortOrderString); XCTAssertEqualObjects([queryCopy.state.includedKeys anyObject], [query.state.includedKeys anyObject]); + XCTAssertEqualObjects([queryCopy.state.excludedKeys anyObject], [query.state.excludedKeys anyObject]); XCTAssertEqualObjects([queryCopy.state.selectedKeys anyObject], [query.state.selectedKeys anyObject]); XCTAssertEqualObjects([[queryCopy.state.extraOptions allValues] lastObject], [[query.state.extraOptions allValues] lastObject]);