2011-12-11 15 views
5

Sto cercando di implementare la ricerca nella mia app. Esistono due entità Core Data, "Tag" e "DvarTorah". Un tag ha solo una stringa. Un "DvarTorah" ha un titolo, un contenuto testuale e alcune altre proprietà. Sto cercando di capire il modo migliore per cercarli rapidamente. L'app viene fornita con circa 1200 entità DvarTorah e ancora più tag. In questo momento, carico un NSFetchedResultsController quando il mio controller della vista di ricerca chiama viewDidLoad. Quindi, quando l'utente digita nella casella di ricerca o modifica l'ambito, chiamo un metodo che accetta sia il valore della barra dell'ambito che un termine di ricerca e filtra la mia matrice di oggetti. Ecco come che sembra:Come posso ottimizzare questa ricerca basata su 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]; 
} 

Il problema è che il mio metodo di ricerca è terribilmente lento. So che CONTAINS è un probabile colpevole, ma anche dopo aver archiviato una versione canonica del contenuto (come searchableContent) e aver tentato di ottimizzare ulteriormente, la ricerca è tremendamente lenta. Come posso renderlo più veloce?

Edit:

sulla base dei suggerimenti iniziali di Jacob, ecco il mio nuovo metodo:

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:

Non copiare la matrice più, ancora 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

sono tutti quattro versioni altrettanto lento? Quanto è grande il set di risultati che stai filtrando? Potresti cavartela usando qualcosa di diverso da quello che contiene? –

+0

@ DavidRönnqvist - Il set di risultati è di circa 1200 oggetti. Forse sto fraintendendo il UISearchResultsController del tutto ... Per quanto riguarda la lentezza comparativa, sto profilando a occhio, non usando uno strumento, quindi non ne sono sicuro. Sembra lo stesso per me. – Moshe

risposta

6

Qui ci sono molte cose che masticano i cicli della CPU e la memoria qui:

Uno, si sta facendo una copia mutabile dei risultati recuperati dal NSFetchedResultsController. Perché?

Due, si sta utilizzando un costrutto for..in sul risultato di quanto sopra e chiamando -[NSPredicate evaluateWithObject:] su ciascuno. Puoi invece modificare la stringa di ricerca del predicato per lavorare con -[NSArray filteredArrayUsingPredicate:], che è molto probabilmente più veloce del tuo approccio.

Tre, c'è un problema piuttosto sottile con la tua variabile predicate - la si riassegna sempre in qualcosa di diverso dall'autentato automatico all'inizio. Assegnare il valore predefinito di nil.

Quattro, le stringhe del predicato sono piuttosto inefficienti, come lei ha menzionato. Penso che devi fare qualcosa chiamato indicizzazione o qualcosa del genere.

Maggiori informazioni sulla ricerca full-text con Core Data:

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

Posso chiamare 'filteredArrayUsingPredicate' su un NSFetchedResultsController? In caso contrario, posso usarlo come metodo di classe> In caso contrario, ho bisogno dell'array extra. Vedi il mio codice aggiornato. E sì, il CONTAINS è il mio più grande problema qui. – Moshe

Problemi correlati