Skip to content

Commit

Permalink
Add ordered relationship warning message to CBLIS
Browse files Browse the repository at this point in the history
With the current approach of using inverse relationship view for storing to-many relationship, we are not supporting ordered many relationship. This commit prints warning messages when detecting that the core data model uses ordered many relationship.

#637
  • Loading branch information
pasin committed Apr 3, 2015
1 parent 38dd697 commit ae6022a
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 37 deletions.
38 changes: 9 additions & 29 deletions Source/API/Extras/CBLIncrementalStore.m
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ @implementation CBLIncrementalStore
NSMutableArray* _coalescedChanges;
NSCache* _queryBuilderCache;
NSMutableDictionary* _fetchRequestResultCache;
NSMutableDictionary* _entityAndPropertyToFetchViewName;
CBLLiveQuery* _conflictsQuery;
NSString * _documentTypeKey;
}
Expand Down Expand Up @@ -242,7 +241,6 @@ - (id) initWithPersistentStoreCoordinator: (NSPersistentStoreCoordinator*)root
_coalescedChanges = [[NSMutableArray alloc] init];
_fetchRequestResultCache = [[NSMutableDictionary alloc] init];
_queryBuilderCache = [[NSCache alloc] init];
_entityAndPropertyToFetchViewName = [[NSMutableDictionary alloc] init];

self.conflictHandler = [self defaultConflictHandler];

Expand Down Expand Up @@ -270,11 +268,15 @@ -(BOOL) loadMetadata: (NSError**)outError {
// Check if the entity contains to-many relationship without an inverse relation and warn:
NSDictionary* relationship = [entity relationshipsByName];
for (NSRelationshipDescription *rel in [relationship allValues]) {
if (rel.isToMany && !rel.inverseRelationship) {
WARN(@"'%@' entity has a to-many relationship '%@' that has no inverse relationship "
"defined. The inverse relationship is requried by the CBLIncrementalStore for "
"fetching to-many relationship entities.", entity.name, rel.name);
break;
if (rel.isToMany) {
if (!rel.inverseRelationship)
WARN(@"'%@' entity has a to-many relationship '%@' that has no inverse relationship "
"defined. The inverse relationship is requried by the CBLIncrementalStore for "
"fetching to-many relationship entities.", entity.name, rel.name);

if (rel.ordered)
WARN(@"'%@' entity has an ordered to-many relationship '%@', which is not supported "
"by the CBLIncrementalStore.", entity.name, rel.name);
}
}
}
Expand Down Expand Up @@ -633,8 +635,6 @@ - (NSString *)documentTypeKey {

/** Initializes the views needed for querying objects by type and for to-many relationships.*/
- (void) initializeViews {
NSMutableDictionary* subentitiesToSuperentities = [NSMutableDictionary dictionary];

// Create a view for each to-many relationship
NSArray* entites = self.persistentStoreCoordinator.managedObjectModel.entities;
for (NSEntityDescription* entity in entites) {
Expand All @@ -661,22 +661,9 @@ - (void) initializeViews {
emit([doc objectForKey: invertRelPropName], nil);
}
} version: @"1.0"];

// Remember view for mapping super-entity and all sub-entities:
for (NSString* entityName in entityNames) {
[self setViewName: viewName
forFetchingProperty: invertRelPropName
fromEntity: entityName];
}
}
}
}

if (entity.subentities.count > 0) {
for (NSEntityDescription* subentity in entity.subentities) {
[subentitiesToSuperentities setObject: entity.name forKey: subentity.name];
}
}
}
}

Expand Down Expand Up @@ -1790,13 +1777,6 @@ - (void) purgeCachedObjectsForEntityName: (NSString*)entity {
}
}

#pragma mark - Views

- (void) setViewName: (NSString*)viewName forFetchingProperty: (NSString*)propertyName fromEntity: (NSString*)entity {
[_entityAndPropertyToFetchViewName setObject: viewName
forKey: [NSString stringWithFormat:@"%@_%@", entity, propertyName]];
}

#pragma mark - Attachments

- (NSData*) loadDataForAttachmentWithName: (NSString*)name ofDocumentWithID: (NSString*)documentID {
Expand Down
88 changes: 80 additions & 8 deletions Unit-Tests/IncrementalStore_Tests.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ @interface IncrementalStore_Tests : CBLTestCaseWithDB

@class Entry;
@class Subentry;
@class AnotherSubentry;
@class File;
@class Article;
@class User;
Expand All @@ -53,23 +54,41 @@ @interface Entry : NSManagedObject
// To-Many relationship without an inverse relationship.
@property (nonatomic, retain) NSSet *articles;

// To-Many with ordered relationship (not supported by CBLIS)
@property (nonatomic, retain) NSOrderedSet *anotherSubentries;

@end

@interface Entry (CoreDataGeneratedAccessors)
// subEntries:
- (void)addSubEntriesObject:(Subentry *)value;
- (void)removeSubEntriesObject:(Subentry *)value;
- (void)addSubEntries:(NSSet *)values;
- (void)removeSubEntries:(NSSet *)values;

// files:
- (void)addFilesObject:(File *)value;
- (void)removeFilesObject:(File *)value;
- (void)addFiles:(NSSet *)values;
- (void)removeFiles:(NSSet *)values;

// articles:
- (void)addArticlesObject:(Article *)value;
- (void)removeArticlesObject:(Article *)value;
- (void)addArticles:(NSSet *)values;
- (void)removeArticles:(NSSet *)values;

// anotherSubentries:
- (void)insertObject:(NSManagedObject *)value inAnotherSubentriesAtIndex:(NSUInteger)idx;
- (void)removeObjectFromAnotherSubentriesAtIndex:(NSUInteger)idx;
- (void)insertAnotherSubentries:(NSArray *)value atIndexes:(NSIndexSet *)indexes;
- (void)removeAnotherSubentriesAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectInAnotherSubentriesAtIndex:(NSUInteger)idx withObject:(NSManagedObject *)value;
- (void)replaceAnotherSubentriesAtIndexes:(NSIndexSet *)indexes withAnotherSubentries:(NSArray *)values;
- (void)addAnotherSubentriesObject:(NSManagedObject *)value;
- (void)removeAnotherSubentriesObject:(NSManagedObject *)value;
- (void)addAnotherSubentries:(NSOrderedSet *)values;
- (void)removeAnotherSubentries:(NSOrderedSet *)values;
@end

@interface Subentry : NSManagedObject
Expand All @@ -78,6 +97,12 @@ @interface Subentry : NSManagedObject
@property (nonatomic, retain) Entry *entry;
@end

@interface AnotherSubentry : NSManagedObject
@property (nonatomic, retain) NSString * text;
@property (nonatomic, retain) NSNumber * number;
@property (nonatomic, retain) Entry *entry;
@end

@interface File : NSManagedObject
@property (nonatomic, retain) NSString * filename;
@property (nonatomic, retain) NSData * data;
Expand Down Expand Up @@ -346,7 +371,7 @@ - (void) test_ToManyRelationship {
RequireTestCase(CBLIncrementalStoreCRUD);
NSError *error;

// To-Many with inverse relationship
// To-Many with inverse relationship:
Entry *entry = [NSEntityDescription insertNewObjectForEntityForName:@"Entry"
inManagedObjectContext:context];
entry.created_at = [NSDate new];
Expand All @@ -365,7 +390,7 @@ - (void) test_ToManyRelationship {
success = [context save:&error];
Assert(success, @"Could not save context: %@", error);

// To-Many without inverse relationship
// To-Many without inverse relationship:
for (NSUInteger i = 0; i < 3; i++) {
Article *article = [NSEntityDescription insertNewObjectForEntityForName:@"Article"
inManagedObjectContext:context];
Expand All @@ -376,19 +401,30 @@ - (void) test_ToManyRelationship {
success = [context save:&error];
Assert(success, @"Could not save context: %@", error);

// Ordered To-Many relationship:
for (NSUInteger i = 0; i < 3; i++) {
AnotherSubentry *sub = [NSEntityDescription insertNewObjectForEntityForName:@"AnotherSubentry"
inManagedObjectContext:context];
sub.text = [NSString stringWithFormat:@"AnotherSub%lu", (unsigned long)i];
[entry addAnotherSubentriesObject:sub];
}

success = [context save:&error];
Assert(success, @"Could not save context: %@", error);

NSManagedObjectID *objectID = entry.objectID;

// tear down and re-init for checking that data got saved
// tear down and re-init for checking that data got saved:
context = [CBLIncrementalStore createManagedObjectContextWithModel:model
databaseName:db.name error:&error];

entry = (Entry*)[context existingObjectWithID:objectID error:&error];
Assert(entry, @"Entry could not be loaded: %@", error);
AssertEq(entry.subEntries.count, 3u);
// Current we do not support to-many-non-inverse-relationship.
// We do not support to-many-non-inverse-relationship.
AssertEq(entry.articles.count, 0u);

// Tear down and re-init and test with fetch request
// Tear down and re-init and test with fetch request:
context = [CBLIncrementalStore createManagedObjectContextWithModel:model
databaseName:db.name error:&error];

Expand All @@ -397,7 +433,11 @@ - (void) test_ToManyRelationship {
AssertEq(result.count, 1u);
entry = result.firstObject;
AssertEq(entry.subEntries.count, 3u);
// NOTE: Current we do not support to-many-non-inverse-relationship.

// Not support the order feature but should return the data.
AssertEq(entry.anotherSubentries.count, 3u);

// We do not support to-many-non-inverse-relationship.
AssertEq(entry.articles.count, 0u);
}

Expand Down Expand Up @@ -1183,6 +1223,10 @@ - (void)assertFetchResult:(NSArray *)result key:(NSString *)key
[subentry setName:@"Subentry"];
[subentry setManagedObjectClassName:@"Subentry"];

NSEntityDescription *anotherSubentry = [NSEntityDescription new];
[anotherSubentry setName:@"AnotherSubentry"];
[anotherSubentry setManagedObjectClassName:@"AnotherSubentry"];

NSEntityDescription *article = [NSEntityDescription new];
[article setName:@"Article"];
[article setManagedObjectClassName:@"Article"];
Expand All @@ -1202,17 +1246,24 @@ - (void)assertFetchResult:(NSArray *)result key:(NSString *)key

NSRelationshipDescription *entryFiles = CBLISRelationshipDescription(@"files", YES, YES, NSCascadeDeleteRule, file);
NSRelationshipDescription *entrySubentries = CBLISRelationshipDescription(@"subEntries", YES, YES, NSCascadeDeleteRule, subentry);
NSRelationshipDescription *entryAnotherSubentries = CBLISRelationshipDescription(@"anotherSubentries", YES, YES, NSCascadeDeleteRule, anotherSubentry);
NSRelationshipDescription *fileEntry = CBLISRelationshipDescription(@"entry", YES, NO, NSNullifyDeleteRule, entry);
NSRelationshipDescription *subentryEntry = CBLISRelationshipDescription(@"entry", YES, NO, NSNullifyDeleteRule, entry);
NSRelationshipDescription *anotherSubentryEntry = CBLISRelationshipDescription(@"entry", YES, NO, NSNullifyDeleteRule, entry);
NSRelationshipDescription *entryArticles = CBLISRelationshipDescription(@"articles", YES, YES, NSCascadeDeleteRule, article);
NSRelationshipDescription *entryUser = CBLISRelationshipDescription(@"user", YES, NO, NSNullifyDeleteRule, user);
NSRelationshipDescription *userEntry = CBLISRelationshipDescription(@"entry", YES, NO, NSNullifyDeleteRule, entry);

[entryFiles setInverseRelationship:fileEntry];
[entrySubentries setInverseRelationship:subentryEntry];
[entryAnotherSubentries setInverseRelationship:anotherSubentryEntry];
[fileEntry setInverseRelationship:entryFiles];
[subentryEntry setInverseRelationship:entrySubentries];
[anotherSubentryEntry setInverseRelationship:entryAnotherSubentries];
[userEntry setInverseRelationship:entryUser];

[entryAnotherSubentries setOrdered:YES];
[anotherSubentryEntry setOrdered:YES];

[entry setProperties:@[
CBLISAttributeDescription(@"check", YES, NSBooleanAttributeType, nil),
Expand All @@ -1224,6 +1275,7 @@ - (void)assertFetchResult:(NSArray *)result key:(NSString *)key
CBLISAttributeDescription(@"text2", YES, NSStringAttributeType, nil),
entryFiles,
entrySubentries,
entryAnotherSubentries,
entryArticles,
entryUser
]];
Expand All @@ -1240,6 +1292,12 @@ - (void)assertFetchResult:(NSArray *)result key:(NSString *)key
subentryEntry
]];

[anotherSubentry setProperties:@[
CBLISAttributeDescription(@"number", YES, NSInteger32AttributeType, @(0)),
CBLISAttributeDescription(@"text", YES, NSStringAttributeType, nil),
anotherSubentryEntry
]];

[article setProperties:@[
CBLISAttributeDescription(@"name", YES, NSStringAttributeType, nil)
]];
Expand All @@ -1257,19 +1315,33 @@ - (void)assertFetchResult:(NSArray *)result key:(NSString *)key
CBLISAttributeDescription(@"anotherName", YES, NSStringAttributeType, nil)
]];

[model setEntities:@[entry, file, subentry, article, user, parent, child]];
[model setEntities:@[entry, file, subentry, anotherSubentry, article, user, parent, child]];

return model;
}

@implementation Entry
@dynamic check, created_at, text, text2, number, decimalNumber, doubleNumber, subEntries, files, articles, user;
@dynamic check, created_at, text, text2, number, decimalNumber, doubleNumber;
@dynamic subEntries, files, articles, anotherSubentries;
@dynamic user;

// Known Core Data Bug:
// Error: [nsset intersectsset:]: set argument is not an nsset.
// Workaround: Override and reimplement the method.
- (void)addAnotherSubentriesObject: (NSManagedObject *)value; {
NSMutableOrderedSet* tempSet = [self mutableOrderedSetValueForKey :@"anotherSubentries"];
[tempSet addObject:value];
}
@end

@implementation Subentry
@dynamic text, number, entry;
@end

@implementation AnotherSubentry
@dynamic text, number, entry;
@end

@implementation File
@dynamic filename, data, entry;
@end
Expand Down

0 comments on commit ae6022a

Please sign in to comment.