1

我在使用 Core Data 的应用程序中创建获取请求时遇到了很大的问题。我有 2 个实体,歌曲和流派。我的模型如下所示: 在此处输入图像描述

我需要使用 Song 实体的 playCount 属性进行查询,以返回前 5 名播放艺术家。最后,我需要按 Artist.name 属性对结果进行分组。

即结果字典应该是这样的:

result_array('my_first_artist' => '3200', 'my_second_artist' => '12');

我知道我无法在 Core Data 中完成上述结果数组,但类似的东西会很棒。

我试过这段代码:

NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:@"Artist" inManagedObjectContext:context]];
[request setPropertiesToFetch:@[@"name", @"songs.@sum.playCount"]];
[request setPropertiesToGroupBy:@[@"name"]];
[request setResultType:NSDictionaryResultType];
[request setSortDescriptors:@[[[NSSortDescriptor alloc] initWithKey:@"songs.@sum.playCount" ascending:NO]]];
[request setFetchLimit:5];

但它总是会引发部分异常@"songs.@sum.playCount"。所以我的问题是:如何使用属性对关系对象求和?

任何建议都会很棒。

编辑:

多亏了“Martin R”,部分问题得以解决。使用NSExpressionDescriptionI 可以进行提取,但NSSortDescriptor不允许对不是NSManagedObject实体属性的键进行排序。

如果有人需要它,这是解决方案:

NSExpression *aggregateExpression = [NSExpression expressionForFunction:@"sum:"
                                                              arguments:@[[NSExpression expressionForKeyPath:@"songs.playCount"]]];

NSExpressionDescription *aggregateDescription = [[NSExpressionDescription alloc] init];
[aggregateDescription setName:@"count"];
[aggregateDescription setExpression:aggregateExpression];
[aggregateDescription setExpressionResultType:NSInteger32AttributeType];

[request setPropertiesToFetch:@[@"name", aggregateDescription]];
4

2 回答 2

1

最后我得到了答案。

我向 Genre.h 添加了新属性

@property (nonatomic, retain) NSNumber * songsPlayCountSum;

在 Genre.m 我添加了

@dynamic songsPlayCountSum;

- (NSNumber *)songsPlayCountSum {

    return [self valueForKeyPath:@"songs.@sum.playCount"];
}

我还在 XCDATAMODEL 中添加了名为 songPlayCountSum 的新属性。

这最终是我想要的代码:

NSExpression *aggregateExpression = [NSExpression expressionForFunction:@"sum:"
                                                              arguments:@[[NSExpression expressionForKeyPath:@"songs.playCount"]]];

NSExpressionDescription *aggregateDescription = [[NSExpressionDescription alloc] init];
[aggregateDescription setName:@"count"];
[aggregateDescription setExpression:aggregateExpression];
[aggregateDescription setExpressionResultType:NSInteger32AttributeType];


NSManagedObjectContext *context = [self getManagedObjectContext];

NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:@"Genre" inManagedObjectContext:context]];
[request setPropertiesToFetch:@[@"name", aggregateDescription]];
[request setPropertiesToGroupBy:@[@"name"]];
[request setPredicate:[NSPredicate predicateForNotEmptyAtrribute:@"name"]];
[request setResultType:NSDictionaryResultType];
[request setSortDescriptors:@[[[NSSortDescriptor alloc] initWithKey:@"songsPlayCountSum" ascending:NO]]];
[request setFetchLimit:5];

NSArray *results = [[self getManagedObjectContext] executeFetchRequest:request
                                                                 error:nil];

编辑:

在 NSManagedObject 子类中使用自定义 getter 并不容易,所以当我想使用它时,我得到了错误的结果。而是将 KVO 添加到您的关系属性中,当 Core Data 对关系 NSSet 进行更改时,只需更新您需要用于排序的属性。就我而言,它看起来像这样:

#pragma mark - Memory Management

- (void) dealloc {

    [self removeObserver:self
              forKeyPath:@"songs"];
}

#pragma mark - Initialization

- (id) initWithEntity:(NSEntityDescription *)entity insertIntoManagedObjectContext:(NSManagedObjectContext *)context {

    if (self = [super initWithEntity:entity insertIntoManagedObjectContext:context]) {

        [self addObserver:self
               forKeyPath:@"songs"
                  options:0
                  context:NULL];
    }

    return self;
}

#pragma mark - Observing 

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {

    if ([keyPath isEqualToString:@"songs"]) {

        self.songsPlayCountSum = [self valueForKeyPath:@"songs.@sum.playCount"];
    }
}
于 2013-06-24T12:15:45.810 回答
0

也许您可以向(或)添加一个瞬态(参见文档)属性。用它来返回它的所有. 您可以使用此属性对输出进行排序。songsPlayCountGenreArtistplayCountsongs

于 2013-06-24T15:08:18.960 回答