Search code examples
searchcore-datalarge-data

search core data without fetch all rows


I have Core Data with +2 millions of rows and i want to search for two specific fields: name & phone (for example). I insert scopes for each field in the Search Bar. Everything go fine if I haven't large data set. That's why I want to search in my core data without load all rows in memory before go search controller. Just need a result when my search text length > 3 or when the Search Button Clicked.

  1. I have just one Table View with Search Bar
  2. I populate when AppDidFinish with Call history plist
  3. When the search bar isActive my App frezes until 2 millions rows loads. I need Avoid this step and move forward to step 4
  4. Enter the search text. Then the result filteredArray has shown in the Table View

...

If have any idea I will appreciated.

Here you have some codes:

- (NSFetchedResultsController *)fetchedResultsController
{
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Guia" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    [fetchRequest setFetchBatchSize:50];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO];
    NSArray *sortDescriptors = @[sortDescriptor];

    [fetchRequest setSortDescriptors:sortDescriptors];

    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"GuiaCache"];
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

    NSError *error = nil;
    if (![self.fetchedResultsController performFetch:&error]) {
        abort();
    }

    return _fetchedResultsController;
}

Maybe in this method I need to improve with some Predicate

- (NSFetchRequest *)searchFetchRequest
{
    if (_searchFetchRequest != nil) {
        return _searchFetchRequest;
    }

    _searchFetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Guia" inManagedObjectContext:self.managedObjectContext];
    [_searchFetchRequest setEntity:entity];

    [_searchFetchRequest setFetchBatchSize:50];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO];
    NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
    [_searchFetchRequest setSortDescriptors:sortDescriptors];

    return _searchFetchRequest;
}

Finaly the two functions to search in Core Data

- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
    if ([searchText length] > 3)
        [self searchForText:searchText scope:_scopeKeyIndex];
}

- (void)searchForText:(NSString *)searchText scope:(NSUInteger)scopeOption
{
    if (self.managedObjectContext)
    {
        NSString *predicateFormat = @"%K BEGINSWITH[cd] %@";
        NSString *searchAttribute = @"telephone";

        if (scopeOption == 1)
        {
            searchAttribute = @"name";
        }

        NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateFormat, searchAttribute, searchText];
        [self.searchFetchRequest setPredicate:predicate];

        NSError *error = nil;
        _filteredList = [self.managedObjectContext executeFetchRequest:self.searchFetchRequest error:&error];
    }
}

If you need some other piece of code, just comment.


Solution

  • First, run Instruments against your application, specifically Time Profiler. Where does it say the time is being spent?

    Do you have a predicate in your search? What does the predicate look like? You need to show some code for people to help.

    Doing a fetch against against 2 million records will take a bit of time but I suspect it is not loading 2 million rows into memory as that would most likely cause memory problems as well as speed problems.

    Post the results from Time Profiler and lets see where the time is being spent.

    Update

    First your predicate is very expensive. BEGINSWITH should be avoided if at all possible. Making it both case and diacritic insensitive increases that cost dramatically.

    Did you run Instruments against your code? Did you run the time profiler? Without running that you are just guessing. You NEED to run the time profiler and at least show the results if not link to the entire profile.