-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Implement NSPredicate-based query #531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
… vars. * add queryByRemovingFilters() in FSTQuery and test case; * add property usePredicate in FIRQuery and update old APIs queryWhereField/Path; * add a no-op queryFilteredUsingPredicate() skeleton in FIRQuery. All tests pass.
* implement queryFilteredUsingPredicate in FIRQuery; * add new unit test skeleton FIRQueryTests.m; * reverts last change on adding usePredicate as we are now allowing mix usage of two types of query filtering APIs.
* add unit test and integration test; * revert a few irrelevant line-change in last commit in project.pbxproj.
rename queryFilteredUsingPredicate to queryWherePredicate
raise assertion in the particular case of NSBlockPredicate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is definitely on the right track.
@@ -0,0 +1,72 @@ | |||
/* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please put this file in the location that parallels the implementation: under API
as a peer of FIRGeoPointTests.m
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
* limitations under the License. | ||
*/ | ||
|
||
#import "FirebaseFirestore/FIRFirestore.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the public surface area of Firestore, please just @import
the module, like so:
@import FirebaseFirestore;
That gets both the FIRFirestore.h and FIRQuery.h headers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Firestore/Source/API/FIRQuery.m
Outdated
id value = nil; | ||
if (comparison.leftExpression.keyPath == nil && | ||
comparison.rightExpression.keyPath == nil) { | ||
FSTThrowInvalidArgument(@"Invalid query. Predicate comparison must have " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This error message is more specific than it needs to be and could be simplified. Maybe:
Predicate comparisons must include a key path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Firestore/Source/API/FIRQuery.m
Outdated
} | ||
NSString *path; | ||
id value = nil; | ||
if (comparison.leftExpression.keyPath == nil && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic doesn't handle the case where both sides are keyPaths. Is that possible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored the logic, now only keypath = constant and constant = keypath are allowed comparison through type check.
Firestore/Source/API/FIRQuery.m
Outdated
comparison.rightExpression.keyPath == nil) { | ||
FSTThrowInvalidArgument(@"Invalid query. Predicate comparison must have " | ||
"a key path on either side of its expression."); | ||
} else if (comparison.leftExpression.keyPath == nil) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: This could be clearer if you tested that the key path was non-nil then used it in the immediately following block.
As it stands you're comparing against leftExpression.keyPath
then using the rightExpression.keyPath
right below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done, refactored
Firestore/Source/API/FIRQuery.m
Outdated
^BOOL(id obj, NSDictionary *bindings) { return true; }] class]]) { | ||
FSTThrowInvalidArgument(@"Invalid query. Block-based predicates are not " | ||
"supported. Please use predicateWithFormat to " | ||
"construct predicate instead."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Construct" is a C++ and Java-ism that's not native to Objective-C (where objects are created or initialized). Also "predicate" should be pluralized.
Please use predicateWithFormat to create predicates instead.
Here and below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
* Creates and returns a new `FIRQuery` with the additional filter that documents must | ||
* satisfy the specified predicate. | ||
* | ||
* @param predicate The predicate the document must satisfy. Can be either comparison |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should specifically mention that block-based predicates are not supported
Firestore/Source/API/FIRQuery.m
Outdated
value:value]; | ||
} | ||
|
||
- (FIRQuery *)queryWherePredicate:(NSPredicate *)predicate { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is very large. Consider breaking it down into a few smaller pieces.
For example, you could make queryWherePredicate:
do the instanceof checks and then delegate to strongly-typed single-purpose methods like queryWhereComparisonPredicate:
and queryWhereCompoundPredicate
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Firestore/Source/API/FIRQuery.m
Outdated
"not supported."); | ||
break; | ||
default: | ||
FSTThrowInvalidArgument(@"Invalid query. Predicate does not support " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This "Predicate does not support" phrasing isn't quite right. NSPredicate
does support these things and this phrasing seems to suggest that the predicate itself doesn't support these.
The other phrasing just above is much better: "Custom predicate filters are not supported" is a better example.
Maybe this one could be
Operator type %lu is not supported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Firestore/Source/API/FIRQuery.m
Outdated
NSCompoundPredicate *compound = (NSCompoundPredicate *)predicate; | ||
if (compound.compoundPredicateType != NSAndPredicateType || | ||
compound.subpredicates.count == 0) { | ||
FSTThrowInvalidArgument(@"Invalid query. Predicate only supports " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to above, this would be better phrased as:
Only compound queries using AND are supported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Also, please add an entry to
|
revert name queryWherePredicate() back to queryFilteredUsingPredicate().
* improve docs / assertion message; * fix a bug where transposed comparison is not treated correctly; also add unit test to cover that case.
project change of adding the FIRQueryTests.m file
XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(snapshot), (@[ @"b", @"a" ])); | ||
} | ||
|
||
- (void)testQueryWithPredicate { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix the trailing space travis failure -
$ ! git grep -I ' $'
Firestore/Example/Tests/Integration/API/FIRQueryTests.m:
The command "! git grep -I ' $'" exited with 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
* refactoring to split logic into two helpers; * update change log.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated. PTAL.
@@ -0,0 +1,72 @@ | |||
/* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
* Creates and returns a new `FIRQuery` with the additional filter that documents must | ||
* satisfy the specified predicate. | ||
* | ||
* @param predicate The predicate the document must satisfy. Can be either comparison |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added in @param part
* limitations under the License. | ||
*/ | ||
|
||
#import "FirebaseFirestore/FIRFirestore.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Firestore/Source/API/FIRQuery.m
Outdated
} | ||
NSString *path; | ||
id value = nil; | ||
if (comparison.leftExpression.keyPath == nil && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored the logic, now only keypath = constant and constant = keypath are allowed comparison through type check.
Firestore/Source/API/FIRQuery.m
Outdated
id value = nil; | ||
if (comparison.leftExpression.keyPath == nil && | ||
comparison.rightExpression.keyPath == nil) { | ||
FSTThrowInvalidArgument(@"Invalid query. Predicate comparison must have " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Firestore/Source/API/FIRQuery.m
Outdated
"not supported."); | ||
break; | ||
default: | ||
FSTThrowInvalidArgument(@"Invalid query. Predicate does not support " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Firestore/Source/API/FIRQuery.m
Outdated
NSCompoundPredicate *compound = (NSCompoundPredicate *)predicate; | ||
if (compound.compoundPredicateType != NSAndPredicateType || | ||
compound.subpredicates.count == 0) { | ||
FSTThrowInvalidArgument(@"Invalid query. Predicate only supports " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Firestore/Source/API/FIRQuery.m
Outdated
^BOOL(id obj, NSDictionary *bindings) { return true; }] class]]) { | ||
FSTThrowInvalidArgument(@"Invalid query. Block-based predicates are not " | ||
"supported. Please use predicateWithFormat to " | ||
"construct predicate instead."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(snapshot), (@[ @"b", @"a" ])); | ||
} | ||
|
||
- (void)testQueryWithPredicate { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Firestore/Source/API/FIRQuery.m
Outdated
value:value]; | ||
} | ||
|
||
- (FIRQuery *)queryWherePredicate:(NSPredicate *)predicate { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
remove trailing space
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty much LGTM except one (minor) question.
Note that I've changed this to target the firestore-api-changes branch so that we can release all of these API changes at once.
* @return The created `FIRQuery`. | ||
*/ | ||
// clang-format off | ||
- (FIRQuery *)queryFilteredUsingPredicate:(NSPredicate *)predicate NS_SWIFT_NAME(filter(using:)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this NS_SWIFT_NAME
be filtered(using:)
? Mentioned this in the API proposal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really sorry to have pointed this out because I was totally wrong :-(.
@mikelehen points out that the other swift names are things like orderBy
rather than orderedBy
so this really should be filter(using:)
.
Again I'm super sorry to have caused you extra work like this and I've reverted that change here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no problem, fellow. I like discussions, which is the only way great idea comes from.
please advise when it is OK to merge. |
swift name filter -> filtered
This reverts commit 5585202.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM to squash and merge into firestore-api-changes
.
* implement queryFilteredUsingPredicate in FIRQuery; * add unit test and integration test for queryFilteredUsingPredicate; * project change of adding the FIRQueryTests.m file; * refactoring queryFilteredUsingPredicate to split logic into two helpers;
Discussion
API change proposal, http://go/firestore-nspredicate-based-query
Testing
New test is added to cover the new function and all tests pass.
API Changes
API change proposal, http://go/firestore-nspredicate-based-query