2011-12-11 7 views
5

Estoy intentando implementar la búsqueda en mi aplicación. Hay dos entidades de Datos centrales, "Etiqueta" y "DvarTorah". Una etiqueta solo tiene una cadena. Un "DvarTorah" tiene un título, contenido textual y algunas otras propiedades. Estoy tratando de descubrir la mejor manera de buscarlos rápidamente. La aplicación se envía con aproximadamente 1200 entidades DvarTorah, e incluso más etiquetas. En este momento, cargo un NSFetchedResultsController cuando mi controlador de vista de búsqueda llama a viewDidLoad. Luego, cuando el usuario escribe en el cuadro de búsqueda o cambia el alcance, llamo a un método que toma tanto el valor de la barra de alcance como un término de búsqueda, y filtra mi matriz de objetos. Así es como se ve:¿Cómo puedo optimizar esta búsqueda basada en Core Data?

- (void) filterArrayWithSearchTerm:(NSString *)searchString andScopeIndex:(NSInteger)scopeIndex{ 

    if ([searchString isEqualToString:@""]) { 
     return; 
    }  

    NSMutableArray *unfilteredResults = [[[[self.fetchedResultsController sections] objectAtIndex:0] objects] mutableCopy]; 

    if (self.filteredArray == nil){ 
     self.filteredArray = [[[NSMutableArray alloc ] init] autorelease]; 
    } 

    [filteredArray removeAllObjects]; 

    NSPredicate *predicate = [[[NSPredicate alloc] init] autorelease]; 

    if (scopeIndex == 0) { 
     predicate = [NSPredicate predicateWithFormat:@"dvarTorahTitle CONTAINS[cd] %@", searchString]; 
    }else if (scopeIndex == 1) { 
     predicate = [NSPredicate predicateWithFormat:@"searchableContent CONTAINS[cd] %@", [searchString canonicalString]];    
    }else if (scopeIndex == 2){ 
     predicate = [NSPredicate predicateWithFormat:@"ANY tags.tagText CONTAINS[cd] %@", searchString]; 
    }else{ 
     predicate = [NSPredicate predicateWithFormat:@"(ANY tags.tagText CONTAINS[cd] %@) OR (dvarTorahTitle CONTAINS[cd] %@) OR (searchableContent CONTAINS[cd] %@)", searchString,searchString,searchString]; 
    } 

    for (DvarTorah *dvarTorah in unfilteredResults) { 
     if ([predicate evaluateWithObject:dvarTorah]) { 
      [self.filteredArray addObject:dvarTorah]; 
     } 
    } 

    [unfilteredResults release]; 
} 

El problema es que mi método de búsqueda es terriblemente lento. Sé que CONTAINS es probablemente un culpable, pero incluso después de almacenar una versión canónica del contenido (como contenido de búsqueda) e intentar optimizar aún más, la búsqueda es tremendamente lenta. ¿Cómo puedo hacer esto más rápido?

Editar:

Sobre la base de las sugerencias iniciales de Jacob, aquí está mi nuevo método:

if ([searchString isEqualToString:@""]) { 
    return; 
} 

if (self.filteredArray == nil) { 
    self.filteredArray = [[[NSMutableArray alloc ] init] autorelease]; 
} 

[filteredArray removeAllObjects]; 

NSPredicate *predicate = nil; 

if (scopeIndex == 0) { 
    predicate = [NSPredicate predicateWithFormat:@"dvarTorahTitle CONTAINS[cd] %@", searchString]; 
}else if (scopeIndex == 1) { 
    predicate = [NSPredicate predicateWithFormat:@"searchableContent CONTAINS[cd] %@", [searchString canonicalString]];    
}else if (scopeIndex == 2){ 
    predicate = [NSPredicate predicateWithFormat:@"ANY tags.tagText CONTAINS[cd] %@", searchString]; 
}else{ 
    predicate = [NSPredicate predicateWithFormat:@"(ANY tags.tagText CONTAINS[cd] %@) OR (dvarTorahTitle CONTAINS[cd] %@) OR (searchableContent CONTAINS[cd] %@)", searchString,searchString,searchString]; 
} 

[self.filteredArray addObjectsFromArray:[[[[[self.fetchedResultsController sections] objectAtIndex:0] objects] mutableCopy] filteredArrayUsingPredicate:predicate]]; 

} 

Edit2:

no copiar la matriz más, sigue siendo lento:

- (void) filterArrayWithSearchTerm:(NSString *)searchString andScopeIndex:(NSInteger)scopeIndex{ 

    if ([searchString isEqualToString:@""]) { 
     return; 
    } 

    if (self.filteredArray == nil) { 
     self.filteredArray = [[[NSMutableArray alloc ] init] autorelease]; 
    } 

    [filteredArray removeAllObjects]; 

    NSPredicate *predicate = nil; 

    if (scopeIndex == 0) { 
     predicate = [NSPredicate predicateWithFormat:@"dvarTorahTitle CONTAINS[cd] %@", searchString]; 
    }else if (scopeIndex == 1) { 
     predicate = [NSPredicate predicateWithFormat:@"searchableContent CONTAINS[cd] %@", [searchString canonicalString]];    
    }else if (scopeIndex == 2){ 
     predicate = [NSPredicate predicateWithFormat:@"ANY tags.tagText CONTAINS[cd] %@", searchString]; 
    }else{ 
     predicate = [NSPredicate predicateWithFormat:@"(ANY tags.tagText CONTAINS[cd] %@) OR (dvarTorahTitle CONTAINS[cd] %@) OR (searchableContent CONTAINS[cd] %@)", searchString,searchString,searchString]; 
    } 

    [self.filteredArray addObjectsFromArray:[[[[self.fetchedResultsController sections] objectAtIndex:0] objects] filteredArrayUsingPredicate:predicate]]; 
} 
+0

son las cuatro versiones igualmente lento? ¿Qué tan grande es el conjunto de resultados que está filtrando? ¿Podría salirse con la suya usando algo que no sea contiene? –

+0

@ DavidRönnqvist - El conjunto de resultados es de aproximadamente 1200 objetos. Tal vez estoy malinterpretando el UISearchResultsController por completo ... En cuanto a la lentitud comparativa, estoy perfilando a ojo, no usando una herramienta, así que no estoy seguro. Parece lo mismo para mí. – Moshe

Respuesta

6

Hay muchas cosas que están masticando los ciclos de la CPU y la memoria aquí:

Primero, está haciendo una copia mutable de los resultados obtenidos del NSFetchedResultsController. ¿Por qué?

Dos, está utilizando una construcción for..in en el resultado de lo anterior y llamando al -[NSPredicate evaluateWithObject:] en cada uno. En su lugar, puede revisar su cadena de búsqueda de predicados para trabajar con -[NSArray filteredArrayUsingPredicate:], que probablemente sea más rápido que su enfoque.

Tres, hay un problema bastante sutil con su variable predicate - siempre lo vuelve a asignar a otra cosa que no sea el vaciado autorellenado al principio. Déle el valor predeterminado de nil.

Cuatro, sus cadenas de predicados son bastante ineficientes, como usted mencionó. Creo que necesita hacer algo llamado indexando o algo así.

Más información sobre la búsqueda de texto completo con datos básicos:

http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/CoreData/Articles/cdPerformance.html

http://cocoawithlove.com/2008/03/testing-core-data-with-very-big.html

http://cocoawithlove.com/2009/11/performance-tests-replacing-core-data.html

http://www.mlsite.net/blog/?page_id=1194

Is SQLite FTS3 still the best way to go for rolling out your full text search?

sqlite Indexing Performance Advice

Full Text Searching in Apple's Core Data Framework

+1

¿Puedo llamar 'filteredArrayUsingPredicate' en un NSFetchedResultsController? Si no, ¿puedo usarlo como un método de clase? Si no, necesito el arreglo extra. Ver mi código actualizado Y sí, CONTIENE mi mayor problema aquí. – Moshe