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」のおかげで、問題の一部が解決されました。I を使用NSExpressionDescriptionするとフェッチできますが、エンティティ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"];
}

また、songsPlayCountSum という新しい属性を XCDATAMODEL に追加しました。

そして、これは最終的に私が望むように機能するコードです:

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 サブクラスでカスタム ゲッターを使用するのはそれほど簡単ではないため、使用したいときに間違った結果が得られました。代わりに、関係プロパティに 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

おそらく、一時的な (ドキュメントを参照) 属性songsPlayCountGenre(または) に追加できますArtistplayCountそれを使用して、そのすべての を返しsongsます。この属性を使用して、出力を並べ替えることができます。

于 2013-06-24T15:08:18.960 に答える