2012-11-07 9 views
5

Za moje życie nie mogę tego zmusić do działania.Jak skonstruować predykat dla zestawu NSFetchRequest setHavingPredicate :?

Załóżmy, że nasz obiekt jest obiektem zarządzanym z polem statusu i polem zamówienia.

W jaki sposób mogę uzyskać wszystkie zamówione towary posiadające więcej niż jedno zamówienie, które są takie same?

Proszę nie odpowiadać, żebym po prostu wykonał podzapytanie z @count w głównym predykacie, ponieważ znam to rozwiązanie, celem tego posta jest zrozumienie, jak używać predykatu mającego w danych podstawowych, co prawdopodobnie i tak będzie szybciej niż podzapytanie. (chyba że wyjaśnisz, dlaczego nie mogę użyć klauzuli mającej)

Poniższy kod zwróciłby tablicę słowników z liczbą zamówień na numer zamówienia. Chcę tylko dodać klauzulę mającą na celu ograniczenie mojej prośby, by zwracała tylko słowniki reprezentujące obiekty tych zamówień, których liczba jest większa niż 1.

Oto kod do tej pory i moje próby posiadania orzecznik:

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

Czy kiedykolwiek uzyskać odpowiedź na ten temat? Znalazłem wiele niepotrzebnych odniesień do "setHavingPredicate", które nie wskazują, jaki musi być format tego predykatu. Nie ma przykładów pracy i nic, co próbowałem nie zadziałało ... –

+2

@ ChristopherKing Nie, w zasadzie zrezygnowałem, nie ma ŻADNYCH informacji w sieci, a fora internetowe Apple nie są bardziej pomocne. Naprawdę wierzę, że nikt tego nie używa, ponieważ bardzo łatwo jest skonstruować tę samą funkcjonalność orzeczenia Posi, wykonując normalne pobieranie, a następnie przesuwając dane za pomocą filtru lub ręcznie za pomocą pętli. Chcemy wiedzieć, ponieważ zawsze jest "właściwa droga" do zrobienia czegoś, zwłaszcza jeśli jest to api. – thewormsterror

Odpowiedz

0

Po pierwsze, gdy próbuję coś analogicznego do tego, co robisz, pojawia się błąd, że nie można zdać relację do wielu do setPropertiesToFetch:. The NSFetchRequest documentation popiera to: "Opisy właściwości mogą reprezentować atrybuty , do jednej relacji lub wyrażeń:" To jest problem nr 1.

Zadanie nr 2 polega na tym, że nie można uzyskać grupy przez relację wiele-wiele (nie jest to wyjaśnione w dokumentacji, ale pojawia się ten sam błąd i ma to również sens).

Usuń "order" z właściwości do pobrania. Grupuj według atrybutu. Zmodyfikuj główny predykat tak, aby zawierał tylko te atrybuty, do których grupujesz (lub po prostu je usuń). Określ "zamówienie" w swoim predykacie.

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

Teraz zobaczysz żądanie będzie działać, ale wyniki nie były prawdopodobnie czego oczekiwali:

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

NSDictionaryResultType faktycznie nie daje nic w celu zidentyfikowania tych obiektów przez - po prostu daje ci wartości.

Następnym krokiem jest odzyskanie identyfikatorów dla obiektów OrderedEntry. Sztuczka polega na tym, że include an expression przywróci NSManagedObjectID jako klucz w każdym słowniku.

Nie wiem, czy to faktycznie da w efekcie lepszą wydajność (po prostu ORAZ w głównym orzeczniku). Z mojego doświadczenia wynika, że ​​jedną z najlepszych rzeczy, które możesz zrobić, aby poprawić wydajność pobierania, jest utworzenie singla NSPredicates i użycie zmiennych podstawiania do skonfigurowania każdego pobrania. Parsowanie predykatów może być kosztowne.

Typy słownika mogą być uciążliwe. Zazwyczaj lepiej jest skonstruować dobry predykat. Używam ich tylko do wyrażeń (tj. Wykonywania obliczeń statystycznych na wykresie, które zwracają wartość). Jeśli spojrzysz na wszystkie ograniczenia wokół właściwości, które można pobrać i pogrupować według ograniczeń predykatu, wydaje się, że to właśnie zamierza Apple.Nie jestem nawet pewien, czy można zrobić to, co chcesz zrobić, grupując i stosując predykat mający - na przykład, co masz zamiar pogrupować według? Jeśli według statusu (który musisz pogrupować, aby uwzględnić w swoim predykacie), nie spowoduje to zwinięcia wszystkich pozycji OrderEntries i nie otrzymasz oddzielnych objectID i orderCounts? Najlepiej trzymać się głównego predykatu, a nie grupować i mieć predykaty.

+0

Nie rozumiem, co mówisz, setPropertiesToFetch: co ja tu robię źle? Mam atrybut "order" i wyrażenie expression, które jest wyrażeniem. – thewormsterror

+0

dla numeru problemu 2 moje zamówienie jest atrybutem, a nie relacją, więc mogę pogrupować według mojego zrozumienia. – thewormsterror

+0

Jak można zatem liczyć atrybut? Aby powyższy przykład miał jakikolwiek sens, kolejność musi być relacją wiele do wielu. – karwag

1

skończyło się dzieje z tym dla wszystkich zainteresowanych

-(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; 

}