2011-11-19 12 views
9

Estoy trabajando en una aplicación de aprendizaje con estilo de tarjeta de memoria iOS que, al cargar, necesita tomar un montón de datos de Core Data. Pero los datos que necesito son un subconjunto bastante específico de la entidad, en función de la configuración del usuario, por lo que existen múltiples predicados relacionados con la prueba de equivalencia. Estoy descubriendo que estas búsquedas son súper lentas y, en base a la investigación en SQLite, creo que un índice sería una buena opción aquí.¿Son posibles índices complejos cuando se utilizan datos centrales?

Ahora, entiendo (en gran medida, por leer otras preguntas de stackoverflow) que SQLite y Core Data son dos cosas diferentes, básicamente ortogonales, que no deben confundirse. Pero también tengo entendido que debes trabajar con Core Data para realizar cualquier tipo de trabajo y ajuste de la base de datos; no debe intentar eludir y trabajar directamente con SQLite al optimizar o diseñar la permanencia del objeto en su aplicación.

Pero lo único que puedo encontrar para los índices en Core Data es esa casilla de verificación "indexada" para cada atributo en un modelo. Y eso simplemente no está haciendo el tipo de optimización que estoy buscando.

Aquí está la solicitud de búsqueda, en la actualidad:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
NSEntityDescription *entity = [NSEntityDescription entityForName:@"SKUserItem" inManagedObjectContext:context]; 
fetchRequest.entity = entity; 

NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"next" ascending:YES] autorelease]; 
fetchRequest.sortDescriptors = [NSArray arrayWithObject:sortDescriptor]; 

NSMutableArray *predicates = [NSMutableArray arrayWithCapacity:6]; 
[predicates addObject:[NSPredicate predicateWithFormat:@"next < %f", now() + (60.0*60.0*24.0)]]; 
[predicates addObject:[NSPredicate predicateWithFormat:@"next > %f", nextOffset]]; 
[predicates addObject:[NSPredicate predicateWithFormat:@"user == %@", user]]; 
[predicates addObject:[NSPredicate predicateWithFormat:@"langRaw == %d", lang]]; 

NSArray *stylePredicates = [NSArray arrayWithObjects:[NSPredicate predicateWithFormat:@"styleRaw == %d", SK_SIMP_AND_TRAD], [NSPredicate predicateWithFormat:@"styleRaw == %d", self.style], nil]; 
[predicates addObject:[NSCompoundPredicate orPredicateWithSubpredicates:stylePredicates]]; 

if([self.parts count] == 4 || (self.lang == SK_JA && [self.parts count] == 3)) 
    ; // don't have to filter by parts; they're studying all of them 
else { 
    NSMutableArray *partPredicates = [NSMutableArray arrayWithCapacity:[self.parts count]]; 
    for(NSString *part in self.parts) 
     [partPredicates addObject:[NSPredicate predicateWithFormat:@"partRaw == %d", partCode(part)]]; 
    [predicates addObject:[NSCompoundPredicate orPredicateWithSubpredicates:partPredicates]]; 
} 

NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates]; 
fetchRequest.predicate = compoundPredicate; 

Así que, esencialmente lo que esto traiga hace es ordenar por lado (el momento en el que el elemento dado es debido) y el filtro para el nombre de usuario, se estudia el lenguaje, el ser estilo estudiado (en chino es simplificado y tradicional) y las partes que se estudian (escritura, tono, lectura o definición), y solo buscando dentro de un "próximo" rango. Aquí hay una breve lista de cosas que aprendí al ajustar y manipular esto:

  1. Siempre escanea toda la tabla, o parece hacerlo. Aunque el siguiente está indexado, incluso si lo obligo a buscar un rango que sé que no devolverá nada, aún tardará varios segundos en completarse.
  2. Los predicados, cualquier número de predicados, lo hacen lento. Si elimino algunos pero no todos, es casi tan lento. Si elimino todos los predicados (rompiendo así la aplicación), entonces es mucho más rápido.
  3. La velocidad depende en gran medida de cuántos UserItems haya en total en la tabla. Cuantos más artículos hay, más lento es esto. Algunas personas pueden tener decenas de miles de elementos, y es cuando esta búsqueda puede tomar tanto como 10 segundos para completar. Esto está dando lugar a pausas incómodas en mi aplicación.
  4. El límite superior en el siguiente valor se agregó no porque lo necesitamos, sino porque acelera un poco la búsqueda.
  5. Hacer que la consulta devuelva un subconjunto de las propiedades en un diccionario (en lugar de un objeto administrado completo) y recuperar el resto perezosamente es más rápido, pero aún no lo suficientemente rápido.

Vengo de Google App Engine aquí, así que estoy acostumbrado a los índices que ofrecen allí. Básicamente, quiero ese tipo de índice, pero aplicado a SQLite a través de Core Data. Encontré información sobre cómo agregar índices en SQLite, del tipo que me gustaría, pero haciendo este tipo de indexación a través de Core Data, no puedo encontrar ninguna información sobre eso.

+0

No he encontrado problemas de rendimiento con los datos centrales que aparecen con 1 predicado, pero si solo habla de decenas de miles de elementos, es posible que encuentre un mejor rendimiento utilizando una tienda Core Data no basada en SQL. Por ejemplo, el tipo de tienda binaria puede ser más rápido. –

+0

Gracias por la sugerencia. Lo probé pero parece, probando en mi 3GS, que el tipo de tienda binaria no puede manejar tanta información. Llegué a la mitad de la carga de una cuenta con más de 30,000 elementos antes de que el programa dejara de usar demasiada memoria. Probablemente porque hay 26 atributos en la entidad SKUserItem. –

+0

Ah, eso está muy mal, entonces. Realmente no entiendo por qué el punto n. ° 1 es el caso si la propiedad está indexada. –

Respuesta

18

Lo que quiere es un Índice Compuesto que Core Data admite en iOS 5.0 y posterior.

Usted puede configurarlo en Xcode: La Entidad inspector tiene un índices sección, o si va a crear el NSEntityDescription en código, utilice -setCompoundIndexes:.

Si usa Xcode, deberá añadir una línea en los índices sección que dice

next,user,langRaw 

De esa manera SQL puede utilizar un índice para su búsqueda.

2

Core Data tiene un back-end SQL. De la forma en que lo ha hecho, tendrá todos los datos en una tabla (parte de una entidad) y para encontrar los objetos que busca requerirá buscar en todas las filas, como usted dice que está sucediendo.

En su Modelo de datos necesita dividir algunos de los atributos que está buscando en otras entidades. Intenta hacerlo más basado en objetos y piensa en lo que estarás buscando.

E.g. tiene una entidad para el usuario, el idioma y tal vez una para las lecciones o cualquier cosa basada en el tiempo que es lo que está buscando.

La entidad Lesson tiene una relación de muchos para el idioma y una relación única con el usuario. (o para muchos si más de un usuario toma una clase)

Luego, para buscar los datos de un usuario, busca ese usuario e investiga su propiedad de Idioma o Lecciones para obtener más información.

Para buscar una lista de usuarios que estudian un idioma, busque la entidad de idioma que está buscando e investigue la propiedad de los usuarios.

+0

No creo que las relaciones resuelvan esto, a menos que no entienda sus capacidades. No son lecciones que estoy buscando, es más como una pila gigante de tarjetas. Simplificamos; Di que solo quiero las tarjetas chinas para Bob, que deben entregarse antes. Pero también hay tarjetas japonesas para Bob. Y tarjetas chinas para Dan. Hay miles de cada uno, y sus valores "próximos" (debidos) están todos entremezclados. Si obtuviera todas las cartas relacionadas con Bob, las tendría tanto para chino como para japonés. O si obtuviera todas las cartas relacionadas con el chino, obtendría las de Dan y Bob. ¿Derecha? ¿Y cómo serían ordenados/atados por el siguiente? –

Cuestiones relacionadas