对于我的生活,我似乎无法让它发挥作用。
假设我们的实体是一个具有状态字段和订单字段的托管对象。
我如何让所有有序的订单都有多个相同的订单?
请不要回答告诉我在主谓词中用@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];
答案 0 :(得分: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;
}
答案 1 :(得分:1)
我可以使用以下方法来工作:
[fetchRequest setHavingPredicate:[NSPredicate predicateWithFormat:@"$orderCount > 1"]];
使用expressionDecription的名称作为变量$orderCount
。
或者,您可以使用
NSExpression *countExpression = [NSExpression expressionForVariable:@"orderCount"];
[fetchRequest setHavingPredicate:[NSPredicate predicateWithFormat:@"%@ > 1", countExpression]];
答案 2 :(得分:0)
首先,每当我尝试类似于您正在做的事情时,我都会收到错误消息,指出您无法将多对多关系传递给setPropertiesToFetch:
。 NSFetchRequest documentation支持这一点:“属性描述可能代表属性,一对一关系或表达式。”这就是问题#1。
问题#2似乎你不能通过多对多关系分组(这在文档中没有说明,但是你得到了同样的错误,它也是有道理)。
从要获取的属性中删除“order”。按属性分组。修改您的主谓词只包括您分组的那些属性(或只是删除它)。在您的谓词中指定“order”。
[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打算使用的。我甚至不确定通过分组和使用有谓词来做你想做的事情 - 例如,你要分组的是什么?如果按状态(您需要将其分组以包含在谓词中),是否会折叠所有OrderEntries并且您不会获得单独的objectID和orderCounts?最好坚持主谓词,而不是分组和谓词。