views:

221

answers:

3

I am using CoreData for an iPhone project and I am stuck trying to build a predicate.

My core data entity is

Folder 
   parent  - Point to the folder class parent, can be null and is one to one.
   secure  - An enum that holds the security type.

The problem I have is that I am trying to make it so I don't show any folder that are in a secure folder.

Right now my predicate looks something like this.

NSPredicate *pred = [NSPredicate predicateWithFormat:@"secure = $@ AND (parent = %@ OR parent.secure = %@)",[NSNumber numberWithInteger:kNoSecurity], [NSNull null], [NSNumber numberWithInteger:kNoSecurity]];

This works find when I only have a chain like folder1 -> folder2 and folder1 is secure. But if I have folder1 -> folder2 -> folder3 (folder2 and folder3 are not secure). Folder3 gets returned because I only check one level up. Is there a way to get the predicate to do the check for a entire chain?

Thanks.

+1  A: 

Why not just grab all Folder entities with kNoSecurity?

NSPredicate *pred = [NSPredicate predicateWithFormat:@"secure = %@ ", [NSNumber numberWithInteger:kNoSecurity]];
Alex Reynolds
Seems like evanchri doesn't want to select folders who have parent folder (at any level) that is secure.
Seamus Campbell
Seamus you are correct. I only want to see folders that are not in a secure folder.
evanchri
I'm not sure you can encapsulate that in just one predicate. You may need to do recursive searches and stop if any parent folder is secure, or if you are at the root of your folder tree.
Alex Reynolds
So basically I would have to search each item I get back from a NSFetchResults and manually see if they are protected? Is there a way to get this to work with a NSFetchResultsController? In other words build a NSFetchResultsController with a list of NSManagedObjects? Or Be able to remove them form the controller with out removing them from the store?
evanchri
A: 

How about going back up through the relationship:

NSPredicate *pred = [NSPredicate predicateWithFormat:"parent.secure = %@", [NSNumber numberWithInteger:kNoSecurity]];
Kendall Helmstetter Gelner
Thanks for the response. I am doing that in my code, I just forgot to add that to my code here. The problem is that this only goes up one level. So if I have folder1 -> folder2 -> folder3 -> folder4, and folder1 is secure. Then folder2 is not show but folder3 and folder4 are.
evanchri
A: 

The problem is that this only goes up one level. So if I have folder1 -> folder2 -> folder3 -> folder4, and folder1 is secure. Then folder2 is not show but folder3 and folder4 are.

You can't recursively walk relationships in predicates because keypaths only describe the relationship between the abstract entities and not the concrete, living managed objects that actually contain the data. An entity graph can be very simple yet generate a vastly complex graph of live objects when populated at runtime. You can't logically capture the complexity of that live graph with a simple keypath.

In this case, you have a Folder entity which has a relationship to itself called parent and an attribute of secure. Therefore, a keypath can only describe at most those two properties with path parent.secure. You can't create a keypath of parent.parent.secure because no such relationship actually exists in the entity graph. Such a path only exist sometimes in the live object graph. It would be logically impossible to hard code a path that might or might not exist depending on the particulars of the data at any given time.

This type of situation is where the ability to create customized NSManagedObject subclasses really comes in handy. Your Folder entites don't have to be just dumb data, you can add behaviors to them so that each object can access its own state and return different data as needed.

In this case, I would recommend adding a transient boolean property named something like hasSecureAncestor. Then create a custom getter method like:

- (BOOL) hasSecureAncestor{
    hasSecureAncestor=[NSNumber numberWithBool:NO];
    if (self.parent.secure==kNoSecurity) {
        hasSecureAncestor=[NSNumber numberWithBool:YES];
    }else {
        if (self.parent.parent!=nil) {
            hasSecureAncestor=self.parent.hasSecureAncestor;
        }else {
            hasSecureAncestor=[NSNumber numberWithBool:NO]
        }
    }
    return hasSecureAncestor;
}

Then just create a predicate to test for "hasSecureAncestor==YES". The custom accessor will walk an arbitrarily deep recursive relationship looking for a secure ancestor.

TechZen
I didn't follow this suggestion, I made an ancestors table, because my real code had some requirement not stated here. But the suggestion made here would work for the problem stated.
evanchri