Reputation: 6983
Is there a way create a predicate query to filter child entities? I would like to be able to return a single result set with filtering applied to the children.
For example I have Shape. And Circle an Square are shapes and I want to do this:
NSManagedObjectContext * context = [self managedObjectContext];
Circle * c = [NSEntityDescription insertNewObjectForEntityForName:@"Circle" inManagedObjectContext:context];
c.radius = @(10);
Square * s = [NSEntityDescription insertNewObjectForEntityForName:@"Square" inManagedObjectContext:context];
s.width = @(100);
s.height = @(200);
NSFetchRequest * f= [NSFetchRequest fetchRequestWithEntityName:@"Shape"];
f.predicate =[NSPredicate predicateWithFormat:@" radius = 10 OR hight = 200"];;
NSArray * results = [context executeFetchRequest:f error:nil];
NSLog(@"%@",results);
This produces a key value exception to be called when the fetch request is executed because a square does not have a radius, and a circle does not have a height and width.
Upvotes: 3
Views: 262
Reputation: 6011
I would go with denormalizing the Shape
entity:
Include all properties (which is the internal implementation of CoreData in any case [for a SQLite store]) in the Shape
entity and only create implementations (@dynamic) for specialised shapes (Circle
, Square
, ...).
You can limit this denormalization to only properties you need to search by.
Notes:
Shape
entity, as the inheriting entities would still return value when using: [object valueForKey:@"somepropertynamefromspecializedclass"];
. This way they would return nil
for such keys.shapeType
property to your Shape
entity so you might be able to limit your search for only specific types of Shape
sThis way you only need a single fetch to get the objects you want and track them with a single FRC.
Example:
Shape
: [abstract entity] (shapeType,name,radius,width,height): generate implementation for this entity (Shape.h and Shape.m) but remove the implementation for properties that were denormalized (radius,width,height)
so your Shape
.h file looks like:
@interface Shape : NSManagedObject
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSString * shapeType;
//radius ,width and height were removed (REMEMBER TO REMOVE THEM FROM YOUR .M FILE AS WELL)
@end
Shape
.m file:
@implementation Shape
@dynamic name;
@dynamic shapeType;
@end
Circle
: [inherit from Shape
] ()
Circle
.h file:
@interface Circle : Shape
@property (nonatomic,strong) NSNumber* radius;
@end
Circle
.m file:
@implementation Circle
@dynamic radius;
@end
Rectangle
: [inherit from Shape
] ()
implemented very similarly to Circle
and now somewhere in your code you can do:
Circle* c = (Circle*)[NSEntityDescription insertNewObjectForEntityForName:@"Circle" inManagedObjectContext:self.managedObjectContext];
c.name = @"circle";
c.shapeType = @"c";
c.radius = @(100);
Rectangle* rect = (Rectangle*)[NSEntityDescription insertNewObjectForEntityForName:@"Rectangle" inManagedObjectContext:self.managedObjectContext];
rect.name = @"rectangle";
rect.shapeType = @"r";
rect.width = @(150);
rect.height = @(200);
[self.managedObjectContext save:nil];
[self.managedObjectContext reset];
NSFetchRequest* r = [NSFetchRequest fetchRequestWithEntityName:@"Shape"];
r.predicate = [NSPredicate predicateWithFormat:@"width > %@ OR radius > %@",@(100),@(90)];
NSArray* res = [self.managedObjectContext executeFetchRequest:r error:nil];
Upvotes: 2
Reputation: 57050
You can only achieve this by two ways - either add all possible properties to all entities (add radius
to Square
and width
, height
to Circle
) or execute two fetch requests, one for circles and one for squares. I am for the second method.
NSFetchRequest * f = [NSFetchRequest fetchRequestWithEntityName:@"Circle"];
f.predicate =[NSPredicate predicateWithFormat:@" radius = 10"];
NSArray * results = [context executeFetchRequest:f error:nil];
if(results == nil) { results = @[]; }
f = [NSFetchRequest fetchRequestWithEntityName:@"Square"];
f.predicate = [NSPredicate predicateWithFormat:@"height = 200"];
results = [results arrayByAddingObjectsFromArray:[context executeFetchRequest:f error:nil]];
Upvotes: 2