2011-05-07 14 views
8

Estoy trabajando en una aplicación iphone y tengo una relación sencilla de muchos a muchos configurada con objetos de grupo y contactos. Un grupo puede tener muchos contactos y los contactos pueden pertenecer a múltiples grupos.Fallo al usar la operación agregada: "TODO" en una aplicación Core Data iOS

Estoy intentando seleccionar todos los grupos a los que un contacto en particular NO pertenece ya con el siguiente predicado. (Nota: el campo UID es un campo de cadena que he utilizado para identificar de forma única las entidades de contacto)

[NSPredicate predicateWithFormat:@"ALL contacts.uid != %@", contactUId] 

acuerdo con la Guía de Programación de predicados de Apple, la operación de todo el agregado es válida pero tengo la siguiente excepción que indica que se trata de una predicado sin soporte:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unsupported predicate (null)' 

que puede utilizar un predicado similar a seleccionar todos los grupos que un contacto no pertenecen ya a la utilización de este predicado por lo que parece que tengo todas las relaciones y campos definidos correctamente.

[NSPredicate predicateWithFormat:@"ANY contacts.uid == %@", contactUId] 

La excepción se produce cuando se construye el predicado y no cuando estoy tratando de ejecutar realmente la solicitud de búsqueda por lo que parece estar relacionado con la sintaxis que estoy usando en lugar de apoyo de Datos Básicos. ¿Qué estoy haciendo mal?

+0

Ambrosio, la bienvenida a SO. ¿Puedes incluir un poco más de código alrededor de la definición de tu predicado? – makdad

+0

No estoy seguro de qué más incluir. La excepción se produce en el predicateWithFormat: call (a diferencia de durante la ejecución de la búsqueda) por lo que es bastante claro que el problema está relacionado con el predicado y no con la búsqueda. – akrapacs

+0

I recreado el escenario en una aplicación de prueba/muestra y la excepción se está lanzando en la llamada a [NSManagedObjectContext executeFetchRequest: error:] llamada de método y no en el [NSPredicate predicateWithFormat:] llamar como inicialmente indicado. – akrapacs

Respuesta

0

La sintaxis básica está bien. Esto compila y se ejecuta en mi prueba:

NSString *[email protected]"steve"; 
NSPredicate *p=[NSPredicate predicateWithFormat:@"ALL contacts.uid != %@", contactUId]; 
NSLog(@"p = %@",p); 

//=> p = ALL contacts.uid != "steve" 

más probable es que el problema es con la variable contactUid. Si no tiene ningún valor o un valor que no se convierta limpiamente en una cadena que entiende NSPredicate, provocará el bloqueo. P.ej.

NSString *contactUId; 
NSPredicate *p=[NSPredicate predicateWithFormat:@"ALL contacts.uid != %@", contactUId]; 
NSLog(@"p = %@",p); 

... causa exactamente el bloqueo que usted describe.

Me gustaría registrar contactUid inmediatamente antes de asignarlo al predicado para ver cuál es su valor real convertible de cadena. La forma en que se muestra en NSLog es la forma en que aparecerá en el predicado porque ambos usan el mismo formato de cadena.

+0

Yo verifico que el valor de la variable es correcto Incluso traté de usar una cadena codificada como el parámetro en lugar de la variable. Además, simplemente cambiando ALL a ANY en el formato de predicado funciona bien, simplemente no está seleccionando lo que quiero. Voy a crear un proyecto de prueba fuera del proyecto principal e intentar recrear el mismo escenario. Quizás eso arroje algo de luz sobre lo que está mal. – akrapacs

+0

¿Cómo se convierte 'contactUid' en una cadena? Debes asegurarte de que no tenga caracteres extraños. Como un ejemplo extremo, si guarda un formato predicado como cadena y luego intenta insertar esa cadena en una variable determinante, que hará que el predicado a fallar para inicializar porque el predicado intentará analizar la cadena como el predicado que representa los caracteres es decir, como '=', '>' '%' en la cadena se interpretan como operadores de comparación y no solo como caracteres. – TechZen

+0

UID Las cadenas se ven así: 10474FD7-2FC8-44AA-85FA-3C7BA0773AC4. Intenté usar un formato sin guiones (ejemplo: 1001595484) y los resultados son los mismos. Recreé el escenario en una aplicación de muestra y recibo el mismo bloqueo independientemente del formato de UID. – akrapacs

5

La Guía de Programación de Datos Básicos dice

There are some interactions between fetching and the type of store. In the XML, binary, and in-memory stores, evaluation of the predicate and sort descriptors is performed in Objective-C with access to all Cocoa's functionality, including the comparison methods on NSString. The SQL store, on the other hand, compiles the predicate and sort descriptors to SQL and evaluates the result in the database itself.

Se va a describir algunas otras limitaciones en el uso de NSPredicate con NSSQLiteStoreType, pero su uso de "ALL" es una limitación (no documentado) que tiene que ver con cómo la solicitud de búsqueda emite SQL.

Bajo el capó, CoreData genera tres mesas para su esquema:

  • una mesa para contacto
  • una mesa para Grupo
  • una tabla de unión que los relaciona

Y así cuando llama a myGroup.contacts, se ejecuta algo como esto:

select * from Group join JOIN_TABLE on Group.pk == JOIN_TABLE.group_pk join Contact on JOIN_TABLE.contact_pk == Contact.pk where Group.pk == 12 

¡Hay muchas cosas detrás de un personaje de puntos!

De todos modos, para cumplir realmente su consulta, lo que se necesita algo como esto. He probado esto en una base de datos real SQLite CD, por lo que los nombres de las tablas un aspecto extraño, pero aún así debería ser comprensible:

select ZGROUP.Z_PK as outer_pk from ZGROUP where "myUID" not in 
(select ZCONTACT.ZUID as contact_uid from ZGROUP join Z_1GROUPS on Z_1GROUPS.Z_2GROUPS == ZGROUP.Z_PK join ZCONTACT on Z_1GROUPS.Z_1CONTACTS == ZCONTACT.Z_PK where ZGROUP.Z_PK == outer_pk) 

No soy un experto SQL, pero mis observaciones son en primer lugar que esta consulta se va ser lento, y segundo, es un poco alejado de NSPredicate con el que comenzamos. Por lo tanto, sería solo mediante un gran esfuerzo que CD podría subir con una consulta SQL para lo que desea hacer, y la consulta que surgiría no sería mucho mejor que una implementación ingenua en ObjC.

Para lo que sea, un desarrollador de Apple dice here que ALL no está soportado en SQLite y que la documentación en contrario es incorrecta. Esa documentación todavía está allí en 2013, por lo que nadie parece haber hecho nada al respecto.

De todos modos, lo que realmente debe hacer es algo como esto:

NSFetchRequest *fetchRequest = ... 
NSArray *result = [moc executeFetchRequest:fetchRequest error:&err]; 
result = [result filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"ALL contacts.uid != %@", contactUId]]; 

Este evaluará el predicado en el software.

0

Aggregate expressions are not supported by Core Data.

Para el registro, la advertencia anterior es de la (2013) NSExpression Referencia de las clases, en the paragraph relative to Aggregate Expressions. De hecho, algunos operadores son compatibles (ANY, NONE) cuando son traducibles en SQL.

Cuestiones relacionadas