user1178952
user1178952

Reputation:

Optimizing this Core Data request

I have an entity in Core Data named MusicInterest. I have to add 5000 or so of these at a time and my current process is to query to see if the MusicInterest exists already, if not create a new one.

It seems this requires 5000 trips to the store to see if each title exists. There are also, of course, insert trips, but the 5000 queries is what's slowing me down.

Each FacebookFriend will have multiple music interests, and I enumerate through each one using an array of string titles, calling the following code.

Any ideas how to optimize this?

+ (MusicInterest*) musicInterestForFacebookFriend:(FacebookFriend*)facebookFriend WithTitle:(NSString*)musicTitle UsingManagedObjectContext:(NSManagedObjectContext*)moc
{
    // query to see if there
    NSArray *matches = [self queryForMusicTitle:musicTitle moc:moc];

    if (([matches count] >= 1)) {
        // NSLog(@"Music already in database");
        MusicInterest *existingMusic = [matches lastObject];
        [existingMusic addLikedByObject:facebookFriend];
        return [matches lastObject];
    } else {
        // create new Music Interest
        MusicInterest *newMusic = [NSEntityDescription insertNewObjectForEntityForName:@"MusicInterest" inManagedObjectContext:moc];
        newMusic.title = musicTitle;
        [newMusic addLikedByObject:facebookFriend];
        return newMusic;
    }
}

+ (NSArray *)queryForMusicTitle:(NSString *)MusicTitle moc:(NSManagedObjectContext *)moc
{
    // query to see if there
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"MusicInterest"];
    request.predicate = [NSPredicate predicateWithFormat:@"title == %@", [NSString stringWithFormat:@"%@", MusicTitle]];

    NSError *error = nil;
    NSArray *matches = [moc executeFetchRequest:request error:&error];
    if (error) {
        NSLog(@"Error querying title in Music interest. Error = %@", error);
    }
    return matches;
}

UPDATE:

I employed the design suggested in the Core Data programming guide and it reduced my time from 12 seconds to 4 seconds (still needs some optimization in other areas :)

The guide only includes half the sample code - I thought I would share my complete implementation:

musicArray = [[music componentsSeparatedByString:@", "] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
                if (obj1 > obj2)
                    return NSOrderedDescending;
                else if (obj1 < obj2)
                    return NSOrderedAscending;
                return NSOrderedSame;
            }];

            if (musicArray) {

                NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"MusicInterest"];
                [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"title IN %@", musicArray]];
                [fetchRequest setSortDescriptors:
                 @[[[NSSortDescriptor alloc] initWithKey: @"title" ascending:YES]]];

                NSError *fetchError = nil;
                NSArray *musicInterestMatchingTitles = [backgroundContext executeFetchRequest:fetchRequest error:&fetchError];

                if ([musicArray count] > 0) {
                    // walk musicArray and musicInterestsMatchingTitles in parallel
                    for (int i = 0; i < [musicArray count]; i++) {
                        NSString *title = musicArray[i];
                        if (i < [musicInterestMatchingTitles count]) {
                            MusicInterest *comparingMusicInterest = musicInterestMatchingTitles[i];
                            // compare each title
                            if (![title isEqualToString:comparingMusicInterest.title]) {
                                // if it doesn't exist as a ManagedObject (a MusicInterest), create one
                                MusicInterest *musicInterest = [MusicInterest createNewMusicInterestUsingManagedObjectContext:backgroundContext];
                                musicInterest.title = title;
                                [musicInterest addLikedByObject:friend];
                            } else {
                                // otherwise, just establish the relationship
                                [comparingMusicInterest addLikedByObject:friend];
                            }
                        } else {
                            // if there are no existing matching managedObjects, create one
                            MusicInterest *musicInterest = [MusicInterest createNewMusicInterestUsingManagedObjectContext:backgroundContext];
                            musicInterest.title = title;
                            [musicInterest addLikedByObject:friend];
                        }
                    }
                }
            }

        }];
        [self saveBackgroundContext:backgroundContext];

Upvotes: 1

Views: 228

Answers (1)

Martin R
Martin R

Reputation: 540075

Implementing Find-or-Create Efficiently in the "Core Data Programming Guide" describes a pattern that might be useful here. The basic idea is:

  • Sort your list of items that you want to insert/update by some unique id that is also stored in the database.
  • Perform a single fetch request that fetches all objects from the database that have an id from your list, sorted by the same id.
  • Now traverse your list and the array of fetched items in parallel, to find which items have to be inserted and which items already exist and can be updated.

Upvotes: 3

Related Questions