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:
- 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.
- 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.
- 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.
- El límite superior en el siguiente valor se agregó no porque lo necesitamos, sino porque acelera un poco la búsqueda.
- 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.
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. –
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. –
Ah, eso está muy mal, entonces. Realmente no entiendo por qué el punto n. ° 1 es el caso si la propiedad está indexada. –