Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically Creating a Predicate with a Varying Number of Attributes and Values

I have an entity with three attributes: studyID:string, studyName:string and studyDate:date.

I need to create a predicate for searches but the user has the option to enter any possible combination of all three attributes e.g. one search is on studyID and studyDate but not studyName while the next search is just on studyID.

How to I create a predicate dynamically depending on what combination of attributes the user decides to search on?

like image 427
Ali Avatar asked Nov 16 '25 05:11

Ali


1 Answers

You need to create a compound predicate whose final form depends on which attributes the user searches on.

The cleanest solution is to build the predicate in code using NSExpression, NSComparisonPredicate and NSCompoundPredicate. That will let you customize the predicate for each search.

Something like this:

-(NSPredicate *) predicateWithStudyID:(NSString *) aStudyID studyName:(NSString *) aStudyName date:(NSDate *) aDate{
  NSPredicate *p;
  NSMutableArray *preds=[[NSMutableArray alloc] initWithCapacity:1];
  if (aDate != nil) {
    NSExpression *dateKeyEx=[NSExpression expressionForKeyPath:@"studyDate"];
    NSExpression *aDateEx=[NSExpression expressionForConstantValue:aDate];
    NSPredicate *datePred=[NSComparisonPredicate predicateWithLeftExpression:dateKeyEx
                                                             rightExpression:aDateEx 
                                                                    modifier:NSDirectPredicateModifier
                                                                        type:NSEqualToPredicateOperatorType
                                                                     options:0];
    [preds addObject:datePred];
  }
  if (![aStudyID isEqualToString:@""]) {
    NSExpression *studyIDKeyEx=[NSExpression expressionForKeyPath:@"studyID"];
    NSExpression *aStudyIDEx=[NSExpression expressionForConstantValue:aStudyID];
    NSPredicate *studyIDPred=[NSComparisonPredicate predicateWithLeftExpression:studyIDKeyEx
                                                                rightExpression:aStudyIDEx 
                                                                       modifier:NSDirectPredicateModifier
                                                                           type:NSLikePredicateOperatorType
                                                                        options:NSCaseInsensitivePredicateOption];
    [preds addObject:studyIDPred];
  }
  if (![aStudyName isEqualToString:@""]) {
    NSExpression *studyNameKeyEx=[NSExpression expressionForKeyPath:@"studyName"];
    NSExpression *aStudyNameEx=[NSExpression expressionForConstantValue:aStudyName];
    NSPredicate *studyNamePred=[NSComparisonPredicate predicateWithLeftExpression:studyNameKeyEx
                                                                rightExpression:aStudyNameEx 
                                                                       modifier:NSDirectPredicateModifier
                                                                           type:NSLikePredicateOperatorType
                                                                        options:NSCaseInsensitivePredicateOption];
    [preds addObject:studyNamePred];
  }
  p=[NSCompoundPredicate andPredicateWithSubpredicates:preds];  
  [preds release];
  return p;
}

Then you would use it like so:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {  
  NSDate *firstDate=[NSDate date];
  NSDate *secondDate=[firstDate dateByAddingTimeInterval:60*60*24];
  NSDate *thirdDate=[secondDate dateByAddingTimeInterval:60*60*24];

  // I use an array of dictionaries for convenience but it works
  //   for managed objects just as well. 
  NSDictionary *d1=[NSDictionary dictionaryWithObjectsAndKeys:
                   @"abc123", @"studyID", 
                   @"firstStudy", @"studyName", 
                   firstDate, @"studyDate", 
                   nil];
  NSDictionary *d2=[NSDictionary dictionaryWithObjectsAndKeys:
                    @"abc456", @"studyID", 
                    @"secondStudy", @"studyName", 
                    secondDate, @"studyDate", 
                    nil];
  NSDictionary *d3=[NSDictionary dictionaryWithObjectsAndKeys:
                    @"abc789", @"studyID", 
                    @"thirdStudy", @"studyName", 
                    thirdDate, @"studyDate", 
                    nil];
  NSArray *a=[NSArray arrayWithObjects:d1,d2,d3, nil];
  NSPredicate *p=[self predicateWithStudyID:@"" studyName:@"thirdStudy" date:thirdDate];
  NSLog(@"p = %@",p);  
  NSArray *filtered=[a filteredArrayUsingPredicate:p];
  NSLog(@"%@",filtered);
  return YES;                           
}

See the Predicate Programming Guide: Creating Predicate Directly in Code

Update:

@DaveDelong below points out that in the first block of code above, my creation of the subpredicates is overly complex. There is no need in this case (other than execution speed) to generate the subpredicates from expressions. Instead just build the subpredicates as normal and then compound them like so:

-(NSPredicate *) predicateWithStudyID:(NSString *) aStudyID studyName:(NSString *) aStudyName date:(NSDate *) aDate{
  NSPredicate *p;
  NSMutableArray *preds=[[NSMutableArray alloc] initWithCapacity:1];
  if (aDate != nil) {
    NSPredicate *datePred=[NSPredicate predicateWithFormat:@"studyDate==%@",aDate];    
    [preds addObject:datePred];
  }
  if (![aStudyID isEqualToString:@""]) {
    NSPredicate *studyIDPred=[NSPredicate predicateWithFormat:@"studyID Like %@",aStudyID]; 
    [preds addObject:studyIDPred];
  }
  if (![aStudyName isEqualToString:@""]) {
    NSPredicate *studyNamePred=[NSPredicate predicateWithFormat:@"studyName Like %@",aStudyName]; 
    [preds addObject:studyNamePred];
  }
  p=[NSCompoundPredicate andPredicateWithSubpredicates:preds];  
  [preds release];
  return p;
}
like image 82
TechZen Avatar answered Nov 18 '25 20:11

TechZen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!