动态创建具有不同数量的属性和值的谓词

时间:2011-03-25 09:30:45

标签: objective-c core-data nspredicate

我有一个包含三个属性的实体:studyID:string,s tudyName:string和studyDate:date。

我需要为搜索创建谓词,但用户可以选择输入所有三个属性的任何可能组合,例如一个搜索位于studyIDstudyDate但不是studyName,而下一个搜索位于studyID

如何根据用户决定搜索的属性组合动态创建谓词?

2 个答案:

答案 0 :(得分:7)

您需要创建一个复合谓词,其最终形式取决于用户搜索的属性。

最干净的解决方案是使用NSExpressionNSComparisonPredicateNSCompoundPredicate在代码中构建谓词。这样您就可以自定义每个搜索的谓词。

这样的事情:

-(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;
}

然后你会像这样使用它:

- (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;                           
}

请参阅Predicate Programming Guide: Creating Predicate Directly in Code

更新

下面的@DaveDelong指出,在上面的第一个代码块中,我创建的subpredicates过于复杂。在这种情况下(除执行速度之外)不需要从表达式生成子预测。而只是像往常一样构建子预测,然后像这样复合它们:

-(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;
}

答案 1 :(得分:0)

利用这个问题的灵感,我为连接管理器创建了自己的通用提取,以便于重用。使用字典我能够发送键/值对来动态生成适当的谓词并返回结果,而不必每次都担心其余的。我希望它有所帮助...

- (NSArray *)fetchObjects:(NSString *)entityName
                Parameters:(NSMutableDictionary *)parameters
{
    NSMutableArray *preds = [[NSMutableArray alloc] init];

    NSPredicate *pred;
    NSEnumerator *enumerator = [parameters keyEnumerator];

    for(NSString *aKey in enumerator)
    {
        pred=[NSPredicate predicateWithFormat:@"%K = %@", aKey, [parameters objectForKey:aKey]];
        [preds addObject:pred];

        [Log LogTrace:[NSString stringWithFormat:@"Query - Entity: %@ Predicate: %@ = %@", entityName, aKey, [parameters objectForKey:aKey]]];
    }

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity =
    [NSEntityDescription entityForName:entityName
                inManagedObjectContext:_managedObjectContext];
    [request setEntity:entity];


    NSPredicate *requestPreditace = [NSCompoundPredicate andPredicateWithSubpredicates:preds];
    [request setPredicate:requestPreditace];

    NSError *error;
    NSArray *array = [_managedObjectContext executeFetchRequest:request error:&error];

    [Log LogTrace:[NSString stringWithFormat:@"Results count: %d", [array count]]];

    if (error)
        [Log LogError:[NSString stringWithFormat:@"*** Core Data fetch ERROR: %@", [error localizedDescription]]];

    return array;
}