动态创建具有不同数量的属性和值的谓词
Posted
技术标签:
【中文标题】动态创建具有不同数量的属性和值的谓词【英文标题】:Dynamically Creating a Predicate with a Varying Number of Attributes and Values 【发布时间】:2011-03-25 09:30:45 【问题描述】:我有一个具有三个属性的实体:studyID
:string、studyName
:string 和 studyDate
:date。
我需要为搜索创建一个谓词,但用户可以选择输入所有三个属性的任何可能组合,例如一个搜索是在studyID
和studyDate
,但不是studyName
,而下一个搜索是在studyID
。
如何根据用户决定搜索的属性组合动态创建谓词?
【问题讨论】:
【参考方案1】:您需要创建一个复合谓词,其最终形式取决于用户搜索的属性。
最简洁的解决方案是使用NSExpression、NSComparisonPredicate 和NSCompoundPredicate 在代码中构建谓词。这将允许您为每个搜索自定义谓词。
类似这样的:
-(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;
【讨论】:
以上是关于动态创建具有不同数量的属性和值的谓词的主要内容,如果未能解决你的问题,请参考以下文章