2011-06-30 9 views
8

Dado el siguiente ejemplo artificioso:Consulta para todos los objetos de varios tipos de entidades infantil en la base de datos

Core Data entity diagram for my Awesome Pet Shop App (patent pending)

me gustaría consultar mis datos para todos los objetos que son gatos o perros. Quiero que el conjunto de resultados esté ordenado por nombre, independientemente de la especie, por lo que ir a buscar a todos los gatos y luego buscar a todos los perros no servirá. Quiero hacer esto en una sola consulta.

Una forma de hacer esto sería añadir un campo petType para mascotas, dar a cada registro un valor petType que identifica el sub-entidad que pertenece, entonces se le pregunta de esta manera:

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Pet" 
              inManagedObjectContext:myMOC]; 
[fetchRequest setEntity:entity]; 

// petType values: 1 = dog, 2 = cat, 3 = goldfish. Yuk. 
NSPredicate *p = [NSPredicate predicateWithFormat:@"petType = 1 OR petType = 2"] 
[fetchRequest setPredicate:p]; 

// etc... 

Pero el mero pensar en hacerlo de esa manera me hace estremecer. ¿Hay una mejor manera?


actualización: Gracias a todos los que han respondido - hay algunos muy buenos, bien pensadas soluciones aquí y agradezco a todos ellos.

Para dar un poco de contexto, el modelo de datos real es un poco más complejo que esto (¿no es así?), Pero está bastante bien organizado. He diseñado más de mi parte de esquemas de datos en mi tiempo y estoy feliz de que las entidades y sus relaciones sean bien consideradas. Este problema se ha producido porque (para extender el ejemplo artificial ya inestable) el cliente originalmente quería:

  • una vista que muestra una lista de todas las mascotas
  • una vista que muestra una lista de peces de colores
  • una vista que muestra una lista de los gatos
  • una vista que muestra una lista de los perros

Hasta ahora, todo bien. Pero también quieren una vista que muestre una lista combinada de todos los gatos y perros "porque a las niñas les gustan los gatos y los perros". (Inicialmente se trataba de gatos y peces dorados, por la misma razón). No hay realmente una forma de agrupar de forma natural ese subconjunto de las entidades concretas; es realmente bastante arbitrario.

Hasta ahora, el enfoque de "entidad intermedia abstracta" de Dave Dribin parece ser la solución más limpia, aunque en mi caso creo que se sentiría algo artificial; ¡Realmente la única forma en que podría etiquetar con veracidad a la entidad intermedia sería como "ThingLittleGirlsLike"! :)

Respuesta

3

Como has encontrado a cabo, no se puede utilizar en su entity.name predicado ir a buscar para las tiendas de SQLite (que puede lo usa en otros tipos de tiendas). Puede agregar un petType, como sugiere, que funciona bastante bien, pero también me produce escalofríos. Como alternativa, puede buscar todos Pets y luego filtrar los resultados obtenidos según entity.name. Pero eso es un poco ineficiente, ya que estás cargando más entidades de las que necesitas y haciendo filtros en la memoria que SQLite podría estar haciendo en tu nombre.

Creo que la verdadera pregunta es: ¿qué estás tratando de hacer? Si realmente necesita obtener Cats y Dogs sin Goldfish, entonces su modelo debería reflejar eso. Puede insertar una entidad abstracta FourLeggedPet como padre de Cat y Dog. Luego, en su solicitud de búsqueda, use FourLeggedPet como la entidad con setIncludesSubentities:YES.

+1

+1 La mayoría de las veces, problemas como este resultan del diseño deficiente del modelo. En este caso, parecería que los objetos de mascotas estarían relacionados con otra cosa (actualmente indefinida), p. un dueño. Si tiene una relación de propietario, puede pedir las mascotas del propietario y listo. Los modelos de datos pueden ser muy complejos y debes aprovecharlos para modelar/simular de cerca los objetos, condiciones y eventos del mundo real que tu aplicación maneja. Una vez que haces eso, generalmente encuentras que tus géneros, predicados y búsquedas se simplifican automáticamente. – TechZen

+0

Gracias! Mi ejemplo artificial solo va tan lejos como para reflejar mi caso de uso del mundo real, pero la idea de la entidad intermedia FourLeggedPet me funcionaría bien, creo. ('includesEntities' es' SÍ' por defecto, por cierto, no es necesario configurarlo.) –

+0

@SimonWhitaker, en mi caso, me gustaría obtener todas las subentidades (GoldFish, gatos y perros), pero filtrar los gatos en función de la "maldad". ¿Hay alguna manera de lograr eso? Estoy usando un fetchedresultsController, así que tengo que establecer un fetchRequest. – akshaynhegde

2

Sus objetos administrados ya tienen un tipo de animal doméstico: tienen su entity.name. Si busca todas las entidades Pet que tienen el nombre de la entidad @"Cat" o @"Dog", eso debería hacerlo, creo.

Ejemplo (mecanografiado rápida y sucia en Xcode, por lo mal diseñado):

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{ 
    // Insert code here to initialize your application 
    NSEntityDescription *catEntity = [NSEntityDescription entityForName: @"Cat" inManagedObjectContext: [self managedObjectContext]]; 
    NSEntityDescription *dogEntity = [NSEntityDescription entityForName: @"Dog" inManagedObjectContext: [self managedObjectContext]]; 
    NSEntityDescription *goldfishEntity = [NSEntityDescription entityForName: @"Goldfish" inManagedObjectContext: [self managedObjectContext]]; 

    id cat = [[NSManagedObject alloc] initWithEntity: catEntity insertIntoManagedObjectContext: [self managedObjectContext]]; 
    [cat setName: @"Mittens"]; 

    id dog = [[NSManagedObject alloc] initWithEntity: dogEntity insertIntoManagedObjectContext: [self managedObjectContext]]; 
    [dog setName: @"Rover"]; 

    id fish = [[NSManagedObject alloc] initWithEntity: goldfishEntity insertIntoManagedObjectContext: [self managedObjectContext]]; 
    [fish setName: @"Goldie"]; 

    [[self managedObjectContext] save: NULL]; 
    [fish release]; 
    [dog release]; 
    [cat release]; 

    NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName: @"Pet"]; 
    NSPredicate *pred = [NSPredicate predicateWithBlock: ^(id evaluatedObject, NSDictionary *bindings) { 
     BOOL result = ![[[evaluatedObject entity] name] isEqualToString: @"Goldfish"]; 
     return result; 
    }]; 
    [fetch setPredicate: pred]; 

    NSArray *entities = [[self managedObjectContext] executeFetchRequest: fetch error: NULL]; 
    NSLog(@"Entities: %@", entities); 
} 

Esto registra el siguiente:

2011-06-30 14:46:57.435 CDSubentityTest [5626: 407] Entidades: ( "(entidad: Cat; id: 0x10025f740 ; datos: {\ n name = manoplas; \ n})", "(entidad: perro; id: 0x1002914e0 ; datos: {\ n nombre = Rover; \ n}) ")

Observe la esperada desaparición del pez dorado.

+0

NSInvalidArgumentException ', razón:' keypath entity.name no encontrado en entidad

+0

Ah, bien - gracias. Lamentablemente, no puede usar predicados de bloque con una tienda SQLite. :( –

+0

No puede usar entity.name con una tienda SQLite, incluso con predicados que no sean de bloque. –

0

Creo que el enfoque que sugirió funcionaría bien. Tener un petType también podría permitirle definir subtipos de Pet s (es decir, "mamíferos", "aves", "reptiles", "peces", etc.). Por supuesto, podría tener entidades abstractas para cada uno de estos en su lugar, pero tampoco está claro cómo se beneficiaría de eso. En última instancia, depende de cómo su aplicación utiliza el modelo. ¿Alguna vez necesita obtener todos los Pet s? ¿Planea la posibilidad de obtener solo Cat sy Fish en una sola extracción? ¿Te importa obtener datos que no usarás inmediatamente?

Una vez que defina las clasificaciones requeridas en función de cómo desea mostrar/usar los datos, adapte el modelo a esas clasificaciones. Tener un buen aspecto, modelo genérico, a menudo puede ser un dolor en el trasero de usar ...

0

Los predicados no reconocen las entidades por lo que no puede construir un predicado para encontrarlas. (Actualización: esto solo es cierto para las tiendas SQL.)

Si tiene su corazón puesto en buscar por tipo de mascota, entonces no tiene más remedio que proporcionar un atributo que proporcionará el valor de tipo mascota y que le permitirá predicados y recuperaciones operan. Sin embargo, puede hacer una versión mucho más limpia y segura que la que propuso.

  1. crear un atributo petType para cada entidad (que no puede ser heredada en la entidad, pero puede ser heredada en subclases personalizados NSManagedObject.)
  2. A continuación, establezca el valor por defecto en el editor de modelos de datos para el nombre de especie por ejemplo cat, dog, goldfish, etc. (Si usa un atributo heredado, también hereda el valor predeterminado).
  3. Reemplace el método setter para no hacer nada de manera efectiva haciendo que el atributo sea de solo lectura. (Esto puede ser heredado de una superclase común.)

    -(void) setPetType:(NSString *) petType{ 
        return; 
    } 
    

Ahora, encontrar todos los perros y gatos simplemente se convierte en una cuestión de establecer la entidad ha podido ir a Pet y luego la provisión y selección de nombres de tipo de mascota para el operador IN.

NSArray *petTypes=[NSArray arrayWithObjects:@"cat",@"dog",nil]; 
NSPredicate *p=[NSPredicate predicateWithFormat:@"petType IN %@", petTypes]; 

Mientras que esto va a funcionar, creo que Dave Dribin made the best point. Este tipo de corte es generalmente necesaria cuando se tiene refinados adecuadamente el modelo de datos. Si necesita agrupar varias mascotas por tipo de mascota, esa agrupación probablemente pertenezca a otro objeto, condición o evento del mundo real, p. propietario, que a su vez debe modelarse con relaciones con instancias de mascotas específicas. Si haces eso, entonces tu agrupación ocurre automáticamente y no tienes que perder el tiempo con todo lo anterior.

Cuestiones relacionadas