【问题标题】:Dynamically Creating a Predicate with a Varying Number of Attributes and Values动态创建具有不同数量的属性和值的谓词
【发布时间】:2011-03-25 09:30:45
【问题描述】:

我有一个具有三个属性的实体:studyID:string、studyName:string 和 studyDate:date。

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

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

【问题讨论】:

    标签: objective-c core-data nspredicate


    【解决方案1】:

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

    最简洁的解决方案是使用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 下面指出,在上面的第一段代码中,我创建的子谓词过于复杂。在这种情况下(除了执行速度)不需要从表达式生成子谓词。而是像平常一样构建子谓词,然后像这样复合它们:

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

    【讨论】:

    • 虽然在运行时创建NSExpression 对象可能更快,但它会创建相当多的代码来工作。我建议使用谓词模板 (@"keyPath = $value"),然后根据需要替换值,或者仅使用 predicateWithFormat: 以提高可读性。
    • 我跳过了以正常方式构建谓词,因为 OP 作者要求基本上有 6 个不同的可能谓词。我不知道用可变数量的 AND 运算符构造谓词的简单方法。我尝试使用predicateWithFormat 这样做失败了。
    • 对;我的意思是在每个if() 语句中,您可以使用普通的predicateWithFormat: 构造子谓词,将它们聚合到一个数组中(就像您做的那样),然后将它们组合成一个NSCompoundPredicate(就像您做的那样)。
    • 哦,我明白了,是的。我想我只是习惯于将 NSCompoundPredicate 与表达式一起使用。
    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-13
      • 1970-01-01
      • 1970-01-01
      • 2018-02-21
      • 2015-10-31
      • 1970-01-01
      相关资源
      最近更新 更多