2

I have started using NSCompoundPredicate to combine my predicates, however have a problem.

Here is an example of one predicate:

Aircraft *obj = (Aircraft *)[self.managedObjectContext objectWithID:objID];
if ([predicateAircraft.predicateFormat isEqualToString:@""] || !predicateAircraft) 
    predicateAircraft = [NSPredicate predicateWithFormat:@"aircraft = %@", obj];
else
{
    NSPredicate *newPredicate = [NSPredicate predicateWithFormat:@"aircraft = %@", obj];
    NSPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:[NSArray arrayWithObjects:predicateAircraft, newPredicate, nil]];
    predicateAircraft = compoundPredicate;
}

Aircraft is a subclass of NSManagedObject. This predicate works great, I'm just looping through some Aircraft objects, and adding each to the predicate, so if aircraft1 OR aircraft2 OR etc. etc...

I do this for several properties, same code as above, but creating predicates for different relationships etc. Basically, so the user can filter what data they see, such as which aircraft.

Anyway, once I have built a predicate for each of these, I combine them like so:

NSPredicate *predicateFinal = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:predicateAircraft, predicateBatteries, predicatePilot, nil]];

This time, using AND to combine them. However, using this predicate doesn't filter my data at all. They work individually, but not once combined together in that final line. Any ideas why? That's what I need to resolve.

I have a hunch, could it be that it is creating this: aircraft1 OR aircraft2 OR ... AND ... pilot1 OR pilot2 OR...

Normally you would need parentheses around each bit, such as around the aircraft and pilots to separate them, but the compound doesn't do this?

Effectively what I am getting is:

aircraft = aircraftObj1 OR aircraft = aircraftObj2 AND pilot = pilotObj1 OR pilot = pilotObj2

What I need (I think):

(aircraft = aircraftObj1 OR aircraft = aircraftObj2) AND (pilot = pilotObj1 OR pilot = pilotObj2)

Regardless, any ideas? Thanks.

EDIT:

predicateFinal logs as : TRUEPREDICATE. Not much on it, but apparently TRUEPREDICATE: A predicate that always evaluates to TRUE and is a predicate boolean value. God knows why its returning that?! Presumably that returning true is why there is no visual change is fetch results? As when that is returned there is no change, all objects are fetched.

EDIT 2:

Real progress now. I've fixed any problem with battery objects being part of an NSSet to-many relation but doing ANY battery. But it has revealed the next issue (probably the problem all along).

Josh Kahane
  • 16,765
  • 45
  • 140
  • 253
  • Please post an `NSLog` output of `predicateFinal`. – memmons Mar 03 '14 at 04:08
  • Note: If you already have an array of managed objects, you can create a predicate directly using `IN`: `NSArray *uniqueAircraftIDs = [aircraftArray valueForKeypath:@"distinctUnionOfObjects.objectID]; NSPredicate *newPredicate = [NSPredicate predicateWithFormat:@"(objectID IN %@)", uniqueAircraftIDs];` – memmons Mar 03 '14 at 04:18
  • Thanks for the tip (sadly not the solution). `predicateFinal` just outputs `TRUEPREDICATE` – Josh Kahane Mar 03 '14 at 10:51
  • Put a breakpoint after you create the final predicate and use `po predicateFinal`. – memmons Mar 03 '14 at 17:34
  • Yep, done that, I get `TRUEPREDICATE`. – Josh Kahane Mar 03 '14 at 20:54
  • Not much on it, but apparently `TRUEPREDICATE`: A predicate that always evaluates to TRUE and is a predicate boolean value. God knows why its returning that?! Presumably that returning true is why there is no visual change is fetch results? As when that is returned there is no change, all objects are fetched. – Josh Kahane Mar 03 '14 at 21:06
  • 1
    That's correct. Apple's Docs say that TRUEPREDICATE is returned when there are no subpredicates. Can you NSLog your predicateAircraft, Batteries and Pilot just before you combine them? – mackworth Mar 03 '14 at 21:33
  • Have done on the line before I combine them, they all log as expected, `aircraft == aircraftObj` etc, same with all of them. They are all fine individually. – Josh Kahane Mar 03 '14 at 21:38
  • I think I'm a touch closer to a solution. I think that it could be that the `battery` objects I am filtering, are a to-many relationship, so in an NSSet, whereas the `pilot` and `aircraft` are to-one relations. Ideas? – Josh Kahane Mar 03 '14 at 21:55
  • Solved! Thanks for helping me there. – Josh Kahane Mar 03 '14 at 22:49

2 Answers2

3

Turns out, the NSCompoundPredicate was returning TRUEPREDICATE because the first sub predicate in my compound was nil. (Simple eh? Doh!) So if the first sub predicate is nil, it doesn't matter if any others are not nil, it will stop there and return TRUEPREDICATE and not filter your fetch.

Josh Kahane
  • 16,765
  • 45
  • 140
  • 253
2

Ok to mix "AND" and "OR" in a compound Predicate I did this...

if ([searchCriteria isEqualToString:@"All"]) {
    [parr addObject:[NSPredicate predicateWithFormat:@"aID_Name     like[cd] %@",mySearchBar.text]];
    [parr addObject:[NSPredicate predicateWithFormat:@"aTrackingNum like[cd]         [parr addObject:[NSPredicate predicateWithFormat:@"aRegNum      like[cd] %@",mySearchBar.text]];
    predicate = [NSCompoundPredicate orPredicateWithSubpredicates:parr];
} 

[parr removeAllObjects];
[parr addObject:predicate];
if (aGroups)
    [parr addObject:[NSPredicate predicateWithFormat:@"SUBQUERY(self.group, $x, $x.gID_Key IN %@).@count > 0",[aGroups valueForKey:@"gID_Key"]]];

predicate = [NSCompoundPredicate andPredicateWithSubpredicates:parr];
NSLog(@"%@",predicate);
Rob George
  • 126
  • 1
  • 4