使用 CoreData 执行乘法(聚合):如何?

Posted

技术标签:

【中文标题】使用 CoreData 执行乘法(聚合):如何?【英文标题】:Performing multiplication (aggregation) with CoreData: how to? 【发布时间】:2012-01-24 10:25:30 【问题描述】:

在a fantastic tutorial by Jeff Lamarche 之后,我正在尝试为NSManagedObject 的特定子类聚合数据。

这就是场景。我创建了一个名为 Product 的类,它扩展了 NSManagedObject 类。 Product 类具有如下三个属性:

@property (nonatomic, retain) NSString* name;
@property (nonatomic, retain) NSNumber* quantity;
@property (nonatomic, retain) NSNumber* price;

我还创建了一个名为Product+Aggregate 的类别,我在其中执行总和聚合。特别是,按照 Jeff 教程,我管理了数量属性的总和。

+(NSNumber *)aggregateOperation:(NSString *)function onAttribute:(NSString *)attributeName withPredicate:(NSPredicate *)predicate inManagedObjectContext:(NSManagedObjectContext *)context

    NSString* className = NSStringFromClass([self class]);

    NSExpression *ex = [NSExpression expressionForFunction:function 
        arguments:[NSArray arrayWithObject:[NSExpression expressionForKeyPath:attributeName]]];

    NSExpressionDescription *ed = [[NSExpressionDescription alloc] init];
    [ed setName:@"result"];
    [ed setExpression:ex];
    [ed setExpressionResultType:NSInteger64AttributeType];

    NSArray *properties = [NSArray arrayWithObject:ed];
    [ed release];

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setPropertiesToFetch:properties];
    [request setResultType:NSDictionaryResultType];

    if (predicate != nil)
        [request setPredicate:predicate];

    NSEntityDescription *entity = [NSEntityDescription entityForName:className
        inManagedObjectContext:context];
    [request setEntity:entity];

    NSArray *results = [context executeFetchRequest:request error:nil];
    NSDictionary *resultsDictionary = [results objectAtIndex:0];
    NSNumber *resultValue = [resultsDictionary objectForKey:@"result"];

    return resultValue;

这个类方法被一个特定的UIViewController调用如下:

NSNumber *totalQuantity = [Product aggregateOperation:@"sum:" onAttribute:@"quantity" withPredicate:nil inManagedObjectContext:self.context];

代码运行良好。事实上,如果我说 3 个产品

NAME         QUANTITY      PRICE
PRODUCT 1    2             23.00 
PRODUCT 2    4             12.00
PRODUCT 3    1             2.00

aggregateOperation 方法按预期返回 7。

现在我会多走一步。修改该方法,我需要返回产品订单的总成本。换句话说,我需要计算每个产品的 QUANTITY*PRICE 值,最后返回 TOTAL。

你能建议我正确的方法吗?提前谢谢你。

编辑这是我在 Cyber​​fox 建议后使用的新代码,但不幸的是它不起作用。

NSString* className = NSStringFromClass([self class]);

NSArray *quantityPrice = [NSArray arrayWithObjects: [NSExpression expressionForKeyPath:@"quantity"], [NSExpression expressionForKeyPath:@"price"], nil];

NSArray *multiplyExpression = [NSArray arrayWithObject:[NSExpression expressionForFunction:@"multiply:by:" arguments:quantityPrice]];

NSExpression *ex = [NSExpression expressionForFunction:function arguments:multiplyExpression];

NSExpressionDescription *ed = [[NSExpressionDescription alloc] init];
[ed setName:@"result"];
[ed setExpression:ex];
[ed setExpressionResultType:NSInteger64AttributeType];

// same as before

【问题讨论】:

【参考方案1】:

对于那些感兴趣的人,我找到了解决上述问题的方法。

代码如下:

static NSString* exprName1 = @"val1";
static NSString* exprName2 = @"val2";

NSString *className = NSStringFromClass([self class]); 

NSExpression *quantityPathExpression = [NSExpression expressionForKeyPath:firstAttribute]; //e.g. quantity    
NSExpression *unitaryPricePathExpression = [NSExpression expressionForKeyPath:secondAttribute]; //e.g. price

NSExpressionDescription *quantityED = [[NSExpressionDescription alloc] init];
[quantityED setName:exprName1];
[quantityED setExpression:quantityPathExpression];
[quantityED setExpressionResultType:NSDictionaryResultType];    

NSExpressionDescription *unitaryPriceED = [[NSExpressionDescription alloc] init];
[unitaryPriceED setName:exprName2];
[unitaryPriceED setExpression:unitaryPricePathExpression];
[unitaryPriceED setExpressionResultType:NSDictionaryResultType];

NSArray *properties = [NSArray arrayWithObjects:quantityED, unitaryPriceED, nil];
[quantityED release];
[unitaryPriceED release];

NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setPropertiesToFetch:properties];
[request setResultType:NSDictionaryResultType];

if (predicate != nil)
   [request setPredicate:predicate];

NSEntityDescription *entity = [NSEntityDescription entityForName:className inManagedObjectContext:context];
[request setEntity:entity];

NSError* error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];

if(error != nil)

   NSLog(@"An error occurred: %@", [error localizedDescription]);
   abort();


float total = 0;
for (NSDictionary *resultDict in results) 

   NSNumber* quantityNumber = [resultDict valueForKey:exprName1];
   NSNumber* unitaryPriceNumber = [resultDict valueForKey:exprName2];

   int moltVal = [quantityNumber intValue]*[unitaryPriceNumber intValue];

   total += moltVal;
    

return [NSNumber numberWithInt:total];

P.S. 使用它创建一个 Class 方法,该方法返回 NSNumber 并接受托管上下文和 2 个您要执行数据检索的属性 (NSString) 作为参数。

希望对你有帮助!

【讨论】:

【参考方案2】:

这是在黑暗中拍摄,因为要复制您的代码,我需要构建一个数据库等,但我相信您想要:

NSArray *quantityPrice = [NSArray arrayWithObjects:
                          [NSExpression expressionForKeyPath:@"quantity"],
                          [NSExpression expressionForKeyPath:@"price"], nil];
NSArray *multiplyExpression = [NSArray arrayWithObject:[NSExpression expressionForFunction:@"multiply:by:" arguments:quantityPrice]];
NSExpression *ex = [NSExpression expressionForFunction:function arguments:multiplyExpression];

quantityPrice 是引用quantityprice 的表达式对的数组。 multiplyExpression 是表达式multiply:by:,参数为quantityPriceex 是您的 sum: 表达式,引用了引用数量和价格键路径的多个表达式。

我很确定您会想要做类似的事情,但是如果没有像您这样的数据库设置等,我无法对其进行测试,所以这只是理论。

【讨论】:

感谢您的回复。运行它,代码停在NSArray *results = [context executeFetchRequest:request error:nil];,但有以下异常EXCEPTIONS: unwinding through frame有什么建议吗? 您是否得到了我的编辑版本,我在 'multiply:by:' 表达式中添加了尾随 ':'? *** 给我解决了大约 10 分钟的麻烦。 :( 也就是说,堆栈跟踪中的某处应该有一个更全面的错误,类似于:Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error while updating. 'constraint failed'' 你能贴出完整堆栈跟踪的粘贴吗? 是的,我做到了。堆栈跟踪显示该异常。我可以做些什么来查看完整的堆栈跟踪吗?我的方案项目中启用了日志异常。 另一件有用的事情是编辑应用程序的运行方案,并将-com.apple.CoreData.SQLDebug 1(它可能需要作为两个单独的参数输入)添加到参数中。这将(在某些平台上)在针对数据库运行时触发 SQL 语句的核心数据转储到控制台。有时查看生成的 SQL 可以告诉您出了什么问题。 当我的意思是“multiplyExpression”时,看起来我也写了“multipleExpression”。仍然,您在 XCode 中查看控制台视图,它给出的只是一行“展开框架”而没有额外的细节,或者如果您向上或向下滚动,它会提供任何东西?呃;这将使调试变得困难。确实应该有一个更深层次的“原因”。

以上是关于使用 CoreData 执行乘法(聚合):如何?的主要内容,如果未能解决你的问题,请参考以下文章

CoreData:如何计算行数和其他聚合函数

CoreData 与子表达式聚合

SQL中的乘法聚合运算符

CoreData - 如何使用 NSPrivateQueueConcurrencyType 使用临时上下文执行 NSFetchRequest?

NS3-对象框架之聚合

如何在 Swift 中聚合 [关闭]