你如何构造 NSFetchRequest setHavingPredicate: 的谓词?

Posted

技术标签:

【中文标题】你如何构造 NSFetchRequest setHavingPredicate: 的谓词?【英文标题】:How do you construct the predicate for NSFetchRequest setHavingPredicate:? 【发布时间】:2012-11-07 12:47:27 【问题描述】:

对于我的生活,我似乎无法让它发挥作用。

假设我们的实体是一个带有状态字段和订单字段的托管对象。

我将如何获取具有多个相同订单的所有orderedEntries?

请没有答案告诉我只在主谓词中使用 @count 进行子查询,因为我知道该解决方案,所以这篇文章的重点是了解如何在核心数据中使用 having 谓词,这无论如何,可能会比子查询更快。 (除非你解释为什么我不能使用having子句)

以下代码将返回一个字典数组,其中包含每个订单号的订单数。我想要的是能够添加一个having子句来限制我的请求只返回代表那些计数大于1的订单对象的字典。

这是到目前为止的代码,以及我对谓词的尝试:

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

NSPredicate *predicate = [NSPredicate predicateWithFormat:
                 @"(status == %@)",[NSNumber numberWithInt:EntryStatusAlive]];


[fetchRequest setPredicate:predicate];


NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: @"order"]; // Does not really matter
NSExpression *maxExpression = [NSExpression expressionForFunction: @"count:"
                                                        arguments: [NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: @"orderCount"];
[expressionDescription setExpression: maxExpression]; 
[expressionDescription setExpressionResultType: NSInteger32AttributeType];

[fetchRequest setPropertiesToFetch: [NSArray arrayWithObjects:expressionDescription,@"order",nil]];


[fetchRequest setPropertiesToGroupBy:[NSArray arrayWithObjects:@"order",nil]];
//[fetchRequest setHavingPredicate:[NSPredicate predicateWithFormat:@"@count > 1"]];
//[fetchRequest setHavingPredicate:[NSComparisonPredicate predicateWithLeftExpression:maxExpression rightExpression:[NSExpression expressionForConstantValue:[NSNumber numberWithInteger:1]] modifier:NSDirectPredicateModifier type:NSGreaterThanPredicateOperatorType options:NSCaseInsensitivePredicateOption]];

NSError *error;

NSArray * array = [context executeFetchRequest:fetchRequest error:&error];

【问题讨论】:

你有没有得到这个答案?我发现了许多对“setHavingPredicate”的无用引用,这些引用没有说明该谓词的格式需要是什么。没有工作示例,我尝试过的任何方法都没有工作...... @ChristopherKing 一点也不,我基本上放弃了,网络上没有信息,苹果网络论坛也没有更多帮助。我真的相信没有人使用它,因为很容易构建与 Have 谓词相同的功能,只需执行正常提取,然后使用过滤器或手动使用循环移动数据。我们想知道,因为总是有“正确的方法”来做某事,尤其是在提供 api 的情况下。 【参考方案1】:

我最终为任何感兴趣的人选择了这个

-(BOOL)ordersAreSaneOnDay:(NSNumber*)dayNumber forUser:(User*)user inContext:(NSManagedObjectContext*)context 
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"BasicEntry"
                                          inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setResultType:NSDictionaryResultType];

NSPredicate *predicate = [NSPredicate predicateWithFormat:
                 @"(status == %@) && ((type != %@) && (type != %@) && (dayNumber == %@))  && ((user == NIL) || (user == %@))",[NSNumber numberWithInt:EntryStatusAlive],[NSNumber numberWithInt:EntryTypeTask],[NSNumber numberWithInt:EntryTypeCompletedTask],dayNumber,user];


[fetchRequest setPredicate:predicate];


NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: @"order"]; // Does not really matter
NSExpression *maxExpression = [NSExpression expressionForFunction: @"count:"
                                                        arguments: [NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: @"orderCount"];
[expressionDescription setExpression: maxExpression]; 
[expressionDescription setExpressionResultType: NSInteger32AttributeType];

[fetchRequest setPropertiesToFetch: [NSArray arrayWithObjects:expressionDescription,@"order",nil]];
[expressionDescription release];

[fetchRequest setPropertiesToGroupBy:[NSArray arrayWithObjects:@"order",nil]];
//[fetchRequest setHavingPredicate:[NSPredicate predicateWithFormat:@"self.order.@count > 1"]];
//[fetchRequest setHavingPredicate:[NSComparisonPredicate predicateWithLeftExpression:maxExpression rightExpression:[NSExpression expressionForConstantValue:[NSNumber numberWithInteger:1]] modifier:NSDirectPredicateModifier type:NSGreaterThanPredicateOperatorType options:NSCaseInsensitivePredicateOption]];

NSError *error;

NSArray * array = [context executeFetchRequest:fetchRequest error:&error];
array = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"orderCount > 1"]];
//NSLog(@"it worked %@",array);
[fetchRequest release];

if ([array count]) return FALSE;
return TRUE;

【讨论】:

【参考方案2】:

我已经使用以下方法进行了这项工作:

[fetchRequest setHavingPredicate:[NSPredicate predicateWithFormat:@"$orderCount > 1"]];

使用您的表达式Decription 的名称作为变量$orderCount。 或者,您可以使用

NSExpression *countExpression = [NSExpression expressionForVariable:@"orderCount"];
[fetchRequest setHavingPredicate:[NSPredicate predicateWithFormat:@"%@ > 1", countExpression]];

【讨论】:

【参考方案3】:

首先,每当我尝试类似于您正在做的事情时,我都会收到一个错误,即您无法将一对多关系传递给setPropertiesToFetch:。 NSFetchRequest documentation 支持这一点:“属性描述可能表示属性、一对一关系或表达式。”这就是问题 #1。

问题 #2 是您似乎也无法通过一对多关系分组(这在文档中没有明确说明,但您会遇到同样的错误,而且有道理)。

从要获取的属性中删除“订单”。按属性分组。修改您的主要谓词以仅包含您分组的那些属性(或仅将其删除)。在你的谓词中指定“顺序”。

[fetchRequest setPropertiesToGroupBy: @[ @"???" ]];
[fetchRequest setHavingPredicate: [NSPredicate predicateWithFormat: @"self.order.@count > 1"]];

现在您会看到请求会起作用,但结果可能不是您所期望的:

 - NSArray
     - NSDictionary  status = @"alive", orderCount = "4" 
     - NSDictionary  status = @"alive", orderCount = "9" 
     - NSDictionary  status = @"alive", orderCount = "2" 
     - etc...

NSDictionaryResultType 实际上并没有给你任何东西来识别这些对象 - 它只是给你值。

因此,下一步是取回这些 OrderedEntry 对象的 ID。诀窍是include an expression,它将返回 NSManagedObjectID 作为每个字典中的键。

我不知道这是否真的会给您带来更好的性能(不仅仅是将它与主要谓词相加)。根据我的经验,提高抓取性能的最佳方法之一是创建单例 NSPredicates 并使用替换变量来设置每次抓取。谓词解析可能很昂贵。

字典结果类型可能很痛苦。通常只构造一个好的谓词就更好了。我倾向于只将它们用于表达式(即在返回值的图上执行统计类型的计算)。如果您查看有关要获取和分组的属性的所有限制以及谓词限制,这似乎是 Apple 的意图。我什至不确定是否可以通过分组和使用 having 谓词来做你想做的事情 - 例如,你打算按什么分组?如果按状态(您需要按状态分组以包含在谓词中),那不会折叠所有 OrderEntries 并且您不会获得单独的 objectID 和 orderCounts 吗?最好坚持使用主要谓词,而不是分组和拥有谓词。

【讨论】:

我不明白你在说什么,setPropertiesToFetch:我在这里做错了什么?我有一个属性“order”和 expressionDescription 这是一个表达式。 对于第2个问题,我的顺序是一个属性,而不是一个关系,所以我可以根据我的理解对其进行分组。 那么您希望如何计算一个属性呢?对于上面的示例, order 必须是一对多的关系才有意义。 您可以计算具有特定值属性的对象的出现次数,在我的示例中,我想知道每个订单有多少次,例如 order = 1 -> orderCount = 2, order = 3 -> orderCount = 1. 我正在尝试对 having 谓词做的只是返回计数高于 1 的订单号,我已经可以获得 orderCount。这在带有谓词的 sql 中很容易,但我不知道这里的语法。

以上是关于你如何构造 NSFetchRequest setHavingPredicate: 的谓词?的主要内容,如果未能解决你的问题,请参考以下文章

如何将类型应用于 NSFetchRequest 实例?

如何将类型应用于 NSFetchRequest 实例?

如何将类型应用于NSFetchRequest实例?

NSFetchRequest 如何知道哪个托管对象上下文在范围内?

如何将此 SQL 查询转换为 NSFetchRequest

如何取消设置 NSFetchRequest 的 fetchLimit?