0

I have following CoreData objects Model

enter image description here

Now I am having issue in making a predicate with following conditions.

Fetch all those DBOpportunity WHERE

DBOpportunity.stateCode == 1

AND

DBOpportunity.invoiceDate >= GIVEN_DATE

AND

DBOpportunityLines.crmAccept == 1 OR DBOpportunityLines.crmAccept == 3

I have tried lots of examples and programming guide by the apple but can't able to achieve this.

Irfan DANISH
  • 8,349
  • 12
  • 42
  • 67

3 Answers3

3

opportunitylines is a to-many relationship, so there are multiple DBOpportunityLines objects for one DBOpportunity object. Assuming that the last condition

DBOpportunityLines.crmAccept == 1 OR DBOpportunityLines.crmAccept == 3

should hold for any of the related objects, you need a SUBQUERY:

NSDate *givenDate = ...;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"stateCode == 1 AND invoiceDate >= %@ "
    "AND SUBQUERY(opportunitylines, $x, $x.crmAccept == 1 OR $x.crmAccept == 3).@count > 0",
    givenDate];

Remark: Unfortunately, the usage of SUBQUERY in predicates is poorly documented. There is one example in the NSExpression class reference. See also Quick Explanation of SUBQUERY in NSPredicate Expression.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • @Marting thanx alot, it was excellent solution. Can you guide me in slight modificaiton of above sub query. I just want to keep all conditions as such with one extra condition. Currently opportunitylines.crmAccept should be 1 or 3 i want to also include if there is no opportunitylines with opportunity entity. – Irfan DANISH May 24 '13 at 11:17
  • 1
    @IrfanDANISH: Probably something like `"stateCode == 1 AND invoiceDate >= %@ AND (SUBQUERY(opportunitylines, $x, $x.crmAccept == 1 OR $x.crmAccept == 3).@count > 0 OR opportunitylines.@count == 0)"`. – Martin R May 24 '13 at 11:33
  • 1
    Thanx during hit and trial i did that :) – Irfan DANISH May 24 '13 at 11:37
2

The structure of your predicate is A && B && (C || D)

Setup your predicates

NSPredicate *aPredicate = [NSPredicate predicateWithFormat:@"stateCode == %d", value];
NSPredicate *bPredicate = [NSPredicate predicateWithFormat:@"invoiceDate >=  %@", givenDate];

Similar do the cPredicate and dPredicate. Then first combine c and d with OR

NSArray *cdPredicateArray = @[cPredicate, dPredicate];
NSPredicate *cdPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:cdPredicateArray];

and then all of them with AND

NSArray *allPredicateArray = @[aPredicate, bPredicate, cdPredicate];
NSPredicate *allPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:allPredicateArray];

If I misunderstood your question and your structure is A && B && C || D Then you have to combine A, B and C first (with AND) and then combine that result with D (with OR).

Marc
  • 6,051
  • 5
  • 26
  • 56
  • 1
    Note that the entity name is not part of the predicate, so the first one would be just `... "stateCode == %d", value]`. – Martin R May 24 '13 at 07:16
1

You could also fetch your opportunityLines and then get the parent entities like this:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"opportunityLines" inManagedObjectContext:context];
[fetchRequest setEntity:entity];

NSDate *yourDate = [NSDate date];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(crmAccept==1 OR crmaccept==3) AND opportunity.stateCode==1 AND opportunity.invoiceDate>=%@", yourDate];
[fetchRequest setPredicate:predicate];

NSError *error;

//So here you have your array of opportunitylines
NSArray *opportunityLines = [context executeFetchRequest:fetchRequest error:&error];

//And this is how you get your opportunity objects
NSArray *opportunities = [opportunityLines valueForKeyPath:@"@distinctUnionOfObjects.opportunity"];
gasparuff
  • 2,295
  • 29
  • 48