2012-02-06 16 views
7

Actualmente utilizando Datos principales. Tengo una tabla en la que estoy tratando de recuperar la información a lo largo de estas líneas:Recuento distintivo a través de datos centrales, NSExpression en NSFetchedResultsController

SELECT item, COUNT(*) FROM myTable GROUP BY item; 

con el fin de producir este tipo de resultado:

+---------+------+-------+ 
| item  | COUNT(*) | 
+---------+------+-------+ 
| group 1  | 2  | 
| group 2  | 5  | 
| group 3  | 8  | 
+---------+------+-------+ 

tuve la brillante idea de utilizar un NSExpression , con la esperanza de tener Core Data hacer todo el trabajo por mí. Estoy empezando a hacer girar mis ruedas. La función de expresión : me falla. La excepción no es muy clara. El uso de otras funciones de expresión, como suma: no bloquea la aplicación.

Sería bueno almacenar los resultados en un NSFetchedResultsController. Exploré otras opciones, ninguna de las cuales es demasiado atractiva. ¿Tendría más sentido escribir una consulta SQL y terminarla, en lugar de usar Core Data como contenedor de SQL en este caso?

El código fuente de referencia está debajo.

NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:@"item"]; 
NSExpression *totalExpression = [NSExpression expressionForFunction:@"count:" arguments:[NSArray arrayWithObject:keyPathExpression]]; 

NSExpressionDescription * totalExpressionDescription = [[NSExpressionDescription alloc] init]; 
[totalExpressionDescription setExpression:totalExpression]; 
[totalExpressionDescription setExpressionResultType:NSInteger64AttributeType]; 
[totalExpressionDescription setName:@"totalInstances"]; 

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
NSEntityDescription *entity = [NSEntityDescription entityForName:@"myEntity" inManagedObjectContext:self.managedObjectContext]; 
NSArray *propertiesToFetch = [[NSArray alloc] initWithObjects:strFieldName, totalExpressionDescription, nil]; 

[fetchRequest setEntity:entity]; 
[fetchRequest setReturnsDistinctResults:YES]; 
[fetchRequest setResultType:NSDictionaryResultType]; 
[fetchRequest setPropertiesToFetch:propertiesToFetch]; 
[fetchRequest setFetchBatchSize:20]; 

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:strFieldName ascending:YES]; 
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil]; 
[fetchRequest setSortDescriptors:sortDescriptors]; 

NSFetchedResultsController* aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[self managedObjectContext] sectionNameKeyPath:nil cacheName:nil]; 

... 

NSError *error = nil; 
if (![self.fetchedResultsController performFetch:&error]) 
{ 
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
    abort(); 
} 

Respuesta

14

Esto es posible con NSExpression como se propuso en un principio, en mi ejemplo contribuido He una entidad llamada "persona" con una propiedad llamada "emailAddress" que deseo para obtener el cuenta para.

NSPropertyDescription *propDesc = [[[[model entitiesByName] objectForKey:@"Person"] propertiesByName] objectForKey:@"emailAddress"]; 
NSExpression *emailExpr = [NSExpression expressionForKeyPath:@"emailAddress"]; 
NSExpression *countExpr = [NSExpression expressionForFunction:@"count:" arguments:[NSArray arrayWithObject:emailExpr]]; 
NSExpressionDescription *exprDesc = [[NSExpressionDescription alloc] init]; 
[exprDesc setExpression:countExpr]; 
[exprDesc setExpressionResultType:NSInteger64AttributeType]; 
[exprDesc setName:@"count"]; 

NSFetchRequest *fr = [NSFetchRequest fetchRequestWithEntityName:@"Person"]; 
[fr setPropertiesToGroupBy:[NSArray arrayWithObject:propDesc]]; 
[fr setPropertiesToFetch:[NSArray arrayWithObjects:propDesc, exprDesc, nil]]; 
[fr setResultType:NSDictionaryResultType]; 
NSArray *results = [moc executeFetchRequest:fr error:&error]; 

me escribió un fragmento de código para rellenar previamente la base de datos con 1.000 registros:

NSArray *emailAddresses = [NSArray arrayWithObjects:@"[email protected]", @"[email protected]", @"[email protected]", @"[email protected]", @"[email protected]", nil]; 
    for (int i = 0; i < 1000; i++) { 
     NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:moc]; 
     [person setValue:[NSNumber numberWithInt:i] forKey:@"aNumber"]; 
     [person setValue:[[NSUUID UUID] UUIDString] forKey:@"aUUID"]; 
     [person setValue:[emailAddresses objectAtIndex:(i % [emailAddresses count])] forKey:@"emailAddress"]; 
    } 

El código anterior se inserte cada dirección de correo electrónico 200 veces, aquí están los resultados:

2012-05-31 15:17:42.160 Scratch[16084:10d03] CoreData: sql: SELECT t0.ZEMAILADDRESS, COUNT(t0.ZEMAILADDRESS) FROM ZPERSON t0 GROUP BY t0.ZEMAILADDRESS 
2012-05-31 15:17:42.162 Scratch[16084:10d03] CoreData: annotation: sql connection fetch time: 0.0024s 
2012-05-31 15:17:42.163 Scratch[16084:10d03] CoreData: annotation: total fetch execution time: 0.0029s for 5 rows. 
(gdb) po results 
(NSArray *) $2 = 0x0f811280 <_PFArray 0xf811280>(
{ 
    count = 200; 
    emailAddress = "[email protected]"; 
}, 
{ 
    count = 200; 
    emailAddress = "[email protected]"; 
}, 
{ 
    count = 200; 
    emailAddress = "[email protected]"; 
}, 
{ 
    count = 200; 
    emailAddress = "[email protected]"; 
}, 
{ 
    count = 200; 
    emailAddress = "[email protected]"; 
} 
) 
+1

Veo que me faltaba NSPropertyDescription, lo que habría permitido que NSFetchRequest se haya interpretado correctamente como una consulta SQL agregando la línea "Agrupar por elemento". No estaba al tanto de ese objeto en el momento de la publicación. Podría haber usado tu respuesta en febrero ... jaja. Esto es bueno saberlo, en caso de que decida usar esta solución en el futuro. Tu respuesta es apreciada :-) –

+0

Una nota, el autor original mencionó usar NSFetchedResultsController con la agrupación. Desafortunadamente para que funcione 'setPropertiesToGroupBy' usted necesita' setResultType: NSDictionaryResultType' que deshabilita los cambios de seguimiento. Ver http://stackoverflow.com/a/4361198/287403 –

+0

¿Es posible clasificarlos por conteo? – Andy

Cuestiones relacionadas