Skip to content

Commit

Permalink
Fix CBLIS sorting with relationship keys
Browse files Browse the repository at this point in the history
- Use post filter instead of CBLQueryBuilder when sort descriptors contain relationship keys.
- Fix fetchByEntityAndDoPostFilterWithRequest to support sorting with relationship keys.

#749
  • Loading branch information
pasin committed Jun 1, 2015
1 parent b42244c commit f2d7c52
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 36 deletions.
102 changes: 68 additions & 34 deletions Source/API/Extras/CBLIncrementalStore.m
Original file line number Diff line number Diff line change
Expand Up @@ -717,36 +717,39 @@ - (NSArray*) executeFetchWithRequest: (NSFetchRequest*)request
return result;
}

NSArray* objects;
BOOL useQueryBuilder = NO;
NSPredicate* proPredicate = nil;
NSMutableDictionary* templateVars = [NSMutableDictionary dictionary];
if (request.predicate) {
NSError* scanError;
BOOL hasNonQueryBuilderExp;
proPredicate = [self scanPredicate: request.predicate
withEntity: request.entity
outTemplateVars: templateVars
outHasNonQueryBuilderExp: &hasNonQueryBuilderExp
outError: &scanError];

if (proPredicate) {
useQueryBuilder = !hasNonQueryBuilderExp;
} else {
if (outError) *outError = scanError;
return nil;

if (![self containsRelationshipKeyPathsInSortDescriptors: request.sortDescriptors]) {
NSPredicate* proPredicate = nil;
NSMutableDictionary* templateVars = [NSMutableDictionary dictionary];
if (request.predicate) {
NSError* scanError;
BOOL hasNonQueryBuilderExp;
proPredicate = [self scanPredicate: request.predicate
withEntity: request.entity
outTemplateVars: templateVars
outHasNonQueryBuilderExp: &hasNonQueryBuilderExp
outError: &scanError];

if (proPredicate) {
useQueryBuilder = !hasNonQueryBuilderExp;
} else {
if (outError) *outError = scanError;
return nil;
}
}
}

NSArray* objects;
if (useQueryBuilder) {
NSError* queryBuilderError;
objects = [self fetchByUsingQueryBuilderWithPredicate: proPredicate
withOriginalRequest: request
withTemplateVars: templateVars
withContext: context
outError: &queryBuilderError];
if (!objects) {
useQueryBuilder = NO;
if (useQueryBuilder) {
NSError* queryBuilderError;
objects = [self fetchByUsingQueryBuilderWithPredicate: proPredicate
withOriginalRequest: request
withTemplateVars: templateVars
withContext: context
outError: &queryBuilderError];
if (!objects) {
useQueryBuilder = NO;
}
}
}

Expand Down Expand Up @@ -913,6 +916,14 @@ - (NSString*) cacheKeyForQueryBuilderWithPredicate: (NSPredicate*)predicate
return [self digestCacheKey: keys];
}

- (BOOL) containsRelationshipKeyPathsInSortDescriptors: (NSArray*)sortDescriptors {
for (NSSortDescriptor *sd in sortDescriptors) {
if ([sd.key containsString:@"."])
return YES;
}
return NO;
}

/*
* Scan thru the predicate and its subpredicates, validate keypaths,
* detect non query builder expressions, replace constant values with
Expand Down Expand Up @@ -1159,12 +1170,12 @@ - (NSArray*) fetchByEntityAndDoPostFilterWithRequest: (NSFetchRequest*)request
NSPredicate* predicate = [self documentTypePredicateForFetchRequest:request];

CBLQueryBuilder* builder = [self cachedQueryBuilderForPredicate: predicate
sortDescriptors: request.sortDescriptors];
sortDescriptors: nil];
if (!builder) {
builder = [[CBLQueryBuilder alloc] initWithDatabase: self.database
select: nil
wherePredicate: predicate
orderBy: request.sortDescriptors
orderBy: nil
error: outError];
}

Expand All @@ -1173,17 +1184,19 @@ - (NSArray*) fetchByEntityAndDoPostFilterWithRequest: (NSFetchRequest*)request

[self cacheQueryBuilder: builder
forPredicate: predicate
sortDescriptors: request.sortDescriptors];
sortDescriptors: nil];

CBLQuery* query = [builder createQueryWithContext: nil];
query.prefetch = request.predicate != nil;

CBLQueryEnumerator* rows = [query run: outError];
if (!rows) return nil;

BOOL needSort = (request.sortDescriptors != nil);

// Post filter:
NSUInteger offset = 0;
NSMutableArray* result = [NSMutableArray array];
NSMutableArray* objects = [NSMutableArray array];
for (CBLQueryRow* row in rows) {
_relationshipSearchDepth = 0;
if (!request.predicate ||
Expand All @@ -1192,17 +1205,38 @@ - (NSArray*) fetchByEntityAndDoPostFilterWithRequest: (NSFetchRequest*)request
withProperties: row.documentProperties
withContext: context
outError: outError]) {
if (offset >= request.fetchOffset) {
if (needSort || offset >= request.fetchOffset) {
NSManagedObjectID* objectID = [self newObjectIDForEntity: request.entity
managedObjectContext: context
couchID: row.documentID];
NSManagedObject* object = [context objectWithID: objectID];
[result addObject: object];
[objects addObject: object];
}
if (request.fetchLimit > 0 && result.count == request.fetchLimit) break;
if (!needSort && request.fetchLimit > 0 && objects.count == request.fetchLimit)
break;
offset++;
}
}

if (needSort && (request.fetchOffset > 0 || request.fetchLimit > 0)) {
if (request.fetchOffset >= objects.count)
return @[];

NSUInteger limit = request.fetchLimit;
if (request.fetchLimit == 0 || (request.fetchOffset + request.fetchLimit > objects.count)) {
limit = objects.count - request.fetchOffset;
}

NSRange range = NSMakeRange(request.fetchOffset, limit);
[objects subarrayWithRange: range];
}

NSArray* result;
if (needSort)
result = [objects sortedArrayUsingDescriptors: request.sortDescriptors];
else
result = objects;

return result;
}

Expand Down
6 changes: 4 additions & 2 deletions Unit-Tests/IncrementalStore_Tests.m
Original file line number Diff line number Diff line change
Expand Up @@ -1356,15 +1356,17 @@ - (void)test_FetchWithNestedRelationshipAndSortDescriptors {
}];

// Deep Sort
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"entry.text" ascending:NO], [NSSortDescriptor sortDescriptorWithKey:@"text" ascending:YES]];
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"entry.text" ascending:NO],
[NSSortDescriptor sortDescriptorWithKey:@"text" ascending:YES]];
[self assertFetchRequest: fetchRequest block: ^(NSArray *result, NSFetchRequestResultType resultType) {
AssertEq((int)result.count, 7);
Subentry *first = result[0];
Assert([first.text isEqualToString:@"Entry2-Sub0"]);
}];

// Deep Sort 2
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"entry.text" ascending:YES], [NSSortDescriptor sortDescriptorWithKey:@"text" ascending:YES]];
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"entry.text" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:@"text" ascending:YES]];
[self assertFetchRequest: fetchRequest block: ^(NSArray *result, NSFetchRequestResultType resultType) {
AssertEq((int)result.count, 7);
Subentry *first = result[0];
Expand Down

0 comments on commit f2d7c52

Please sign in to comment.