2012-11-07 15 views
5

Per la vita di me non riesco a farlo funzionare.Come si costruisce il predicato per NSFetchRequest setHavingPredicate :?

Si supponga che la nostra entità sia un oggetto gestito con un campo di stato e un campo di ordine.

Come potrei fare per ottenere tutti orderedEntries che hanno più di un ordine che sono la stessa cosa?

Si prega di nessuna risposta mi dice di fare solo una sottoquery con @count nel predicato principale, poiché so di tale soluzione, il punto di questo post è capire come utilizzare il predicato avere nei dati di base, che sarebbe probabilmente sarà più veloce di una subquery comunque. (a meno che non spieghi perché non posso usare una clausola having)

Il seguente codice restituirebbe una matrice di dizionari con il numero di ordini per numero di ordine. Quello che voglio è poter aggiungere una clausola having per limitare la mia richiesta di restituire solo i dizionari che rappresentano oggetti di quegli ordini che hanno un conteggio maggiore di 1.

Ecco il codice fino ad ora ei miei tentativi di avere predicato:

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]; 
+1

Avete mai ricevuto una risposta su questo? Ho trovato molti riferimenti inutili a "setHavingPredicate" che non forniscono alcuna indicazione su quale sia il formato di questo predicato. Nessun esempio funzionante, e niente di ciò che ho provato ha funzionato ... –

+2

@ChristopherKing No, non ho affatto rinunciato, non ci sono informazioni sul web e i forum web di Apple non sono più utili. Credo davvero che nessuno lo usi, perché è molto semplice costruire la stessa funzionalità del predicato Avere semplicemente eseguendo un normale recupero e spostando poi i dati con un filtro o manualmente con un ciclo. Vogliamo sapere perché c'è sempre la "maniera giusta" di fare qualcosa, specialmente se viene fornita l'API. – thewormsterror

risposta

0

In primo luogo, ogni volta che provo qualcosa di analogo a quello che stai facendo, ottengo un errore che non si può passare un rapporto a-molti a setPropertiesToFetch:. Lo NSFetchRequest documentation esegue il backup: "Le descrizioni delle proprietà potrebbero rappresentare gli attributi , le relazioni a uno o le espressioni." "Quindi questo è il problema n.

Problema n. 2: sembra che non sia possibile il gruppo da una relazione a molti (ciò non è chiaro nella documentazione, ma si ottiene lo stesso errore e ha anche senso).

Rimuovere "ordine" dalle proprietà da recuperare. Raggruppa per un attributo. Modifica il tuo predicato principale per includere solo quegli attributi raggruppati per (o semplicemente rimuovili). Specifica "ordine" nel tuo predicato.

[fetchRequest setPropertiesToGroupBy: @[ @"???" ]]; 
[fetchRequest setHavingPredicate: [NSPredicate predicateWithFormat: @"[email protected] > 1"]]; 

Ora vedrete la richiesta funzionerà, ma i risultati probabilmente non erano quello che ti aspettavi:

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

NSDictionaryResultType in realtà non si dà nulla per identificare gli oggetti da - ti dà solo i valori.

Quindi il prossimo passo è quello di tornare ID per quegli OrderedEntry oggetti. Il trucco è include an expression che restituirà NSManagedObjectID come chiave in ogni dizionario.

Non so se questo sarà effettivamente dare migliori prestazioni a tutti (over giusta e-ing in al predicato principale). Nella mia esperienza, una delle migliori cose che puoi fare per migliorare il recupero delle prestazioni è creare singleton NSPredicates e utilizzare variabili di sostituzione per impostare ogni recupero. L'analisi anticipata può essere costosa.

I tipi di risultati del dizionario possono essere un dolore. Di solito solo costruire un buon predicato è meglio. Tendo solo a usarli per le espressioni (ad esempio, eseguire calcoli di tipo statistico sul grafico che restituiscono un valore). Se si osservano tutte le restrizioni relative alle proprietà per il recupero e il raggruppamento e le restrizioni relative al predicato, questo sembra essere ciò per cui Apple intende farlo.Non sono nemmeno sicuro che sia possibile fare ciò che si vuole fare raggruppando e usando un predicato di avere - per esempio, cosa raggrupperai? Se per stato (che devi raggruppare per includere nel tuo predicato), questo non comprime tutti gli OrderEntries e non otterrai gli objectID e gli ordini dell'ordine separati? È meglio attenersi al predicato principale per questo, non raggruppare e avere predicati.

+0

Non capisco cosa stai dicendo, setPropertiesToFetch: cosa sto facendo male qui? Ho un attributo "ordine" ed espressioneDescrizione che è un'espressione. – thewormsterror

+0

per il problema numero 2 il mio ordine è un attributo, non una relazione, quindi posso raggrupparlo in base alla mia comprensione. – thewormsterror

+0

Come pensi di contare un attributo, quindi? l'ordine deve essere una relazione to-many per il tuo esempio di cui sopra per avere un senso. – karwag

1

ho finito per andare con questo per chiunque sia interessato

-(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:@"[email protected] > 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; 

}