Optimizing text searching using NSFetchedResultsController & Core Data - iphone

I have a tableview with a NSFetchedResultsController data source, displaying a list of names from the underlying Core Data SQLite store. I have implemented a search bar. When the first character is entered in the search bar, a fetch request in the following form is executed:
NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:#"name beginswith[cd] %#", searchText];
However, when the second search character is entered, I would like to filter the fetched objects of the fetchedResultsController, rather than executing another fetch request (implementing another fetch request, similar to the 1 above, results in another trip to the store which I had hoped would not occur as the results of the second fetch would just be a subset of the first). Is there anyway to filter the fetchedResultsController so that another trip to the store is avoided?

Yes, set up a search "state" and then switch your NSTableViewDatasource to point at an array that is filtered from the -fetchedObjects returned from your NSFetcResultsController.
You can then update the filter on that array as the user types more information in and it will not go back to the store. This will even allow you to filter on the first character entered and avoid even that unnecessary trip to the store.

You could always store the results of the first fetch into an array and when text in the search bar changes, filter the contents of the array with another predicate.

Related

NSFetchedResultsController predicate to eliminate duplicates of several properties

I am using an NSFetchedResultsController in my UITableViewController.
Is it possible to specify a predicate that will not retrieve items which have duplicate fields in x number of fields that I specify.
For example, I want to search all results for items but if the itemName AND itemDescription AND itemQuantity are the same, I want only one of these items.
Option 1
When the page loads do a single run through the data and keep a list of objectID that are duplicate. For duplicate object set the row height of the cell to be 0. So they are technically still there, but you can't see it. This make dealing with the NSFetchedResultsControllerDelegate calls easy because no indexPaths have changed
Option 2
If the dataset is always selected in the same way and an object that is a duplicate is always a duplicate you can set an 'isDuplicate' in the object and filter it out in the predicate. Or you can not store at all in the first place. If objects are displayed in different sets and in different way and sometime should be displayed and sometime not be displayed this is not a good solution
Option 3
If you are sorting by the same criteria that make an object duplicate (that is duplicates always appear right next to a non-duplicate) and you are NOT using sections, then you can use sectionKeyPath. SectionKeyPath groups items together into sections. Group the duplicate and non duplicate together and then display every section as a single row (use the first item in each section). The indexPaths of the fetchedResultsController will not match the indexPaths of the tableview so you have to careful to convert them.
Option 4
Instead of accessing the objects from a fetchedResultsController do a fetch and and filter the array. Then use the array to display the objects. The downside is that you don't get updates on when objects change. This can be especially problematic is objects are deleted, as accessing a managedObject that's entity was delete can lead to a crash.
I recommend option 1

Re sorting fetchedresultscontroller once the data is fetched

I have a situation where i need to sort the data using multiple sort descriptors. I want to sort the data initially with two components which are numbers in descending order. Then I should show the data on the table view sorted with name component and use the sectionNameKeyPath to get the sections.
I want the data should be sorted out first with the two specified components and then the result with name. Is this possible using NSfetchedResultsController. I am using this fetchedresultscontroller in data source for tableview. Is there anyway to re sort the fetchedresultscontroller?
I can sort it by taking an array and using sortdescriptor on that, but I want fetchedresultscontroller to show the section details and etc.
Is there any way?
I don't really sure if I have understood your answer.
But there is a property called sortDescriptors in NSFetchRequest which you may use it to sort.
And it's an array so you can supply multi NSSortDescriptor objects
And if you want to resort it.
I know two ways:
Store the result in an array like you said and resort it.
Change the sortDescriptors and re performFetch of the FRC

How to iterate through an NSArray of objects and create a new NSArray containing a subset of the original?

I am working on a basic iPhone app that pulls restaurant objects from a SQLite table, and displays them for the user in a table. My app is navigation based. I have no problems pulling the data from the SQLite database, and then sorting them. That I am able to do no problem. Each restaurant object has various properties, like "name", "address", "category" (i.e. the type of restaurant like greek, italian), and others. Now, after retrieving the records from the database, I am able to successfully put them into an NSMutableArray, and then sort them into an NSArray.
My question is, using this NSArray (or the original NSMutableArray), I'd like to create another array, with the first index containing "All", and then each remaining index contain the unique name of the category of restaurant from the original list, sorted in alphabetical order, and then present this as a table to the user on the next screen. From this table, if the user selects "All", the user will be taken to another screen displaying another table where they can see all the restaurants, regardless of genre.
If however, the user selects a particular category from the available list, the user will be taken to a screen that displays a table composed of only those restaurants of that type (e.g. only Italian restaurants, only Chinese restaurants, only Thai restaurants). How would I go about doing this? Any ideas or suggestions?
One possible algorithm outline:
Iterate over your NSMutableArray (e.g. for(item in array)) inserting the category property of each item into an NSMutableSet (or an NSCountedSet if you'd also like the number of times each category occurs)
Use the NSSet sortedArrayUsingDescriptors: method to obtain a sorted array of the categories.
Add the All entry in whatever way suits

Loading data objects into an NSArray causes a painfully slow startup

I have a Core Data database of about 500 objects. These objects are 'cards' that will be viewed and modified by the user. When a user modifies the card, an attribute called 'groupNumber' will change.
The order of these cards in each group is very important and is determined by the user. I load the database objects into an array. When a user makes a change, I save the order of the array into a plist using the 'title' attribute.
My problem comes when the app is relaunched. I need to load the group array in the order it was saved in. But when I use the plist to do a fetch request, it is painfully slow.
The slow code is:
// get array from plist sorted by 'title'
NSMutableArray *group1Temp = [plistData objectForKey:#"group1ArrayData"];
for (int i = 0; i < [group1Temp count]; i++) {
// set predicate to 'title' attribute
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title == %#", [group1Temp objectAtIndex:i]];
// load filtered database object into temp array
NSArray *temp = [self.vocabDeckArray filteredArrayUsingPredicate:predicate];
[self.group1Array addObjectsFromArray:temp];
}
When this executes 500 times, it is just too slow. If I could save an NSArray of the database objects themselves into a plist, then I wouldn't need to do a predicate search, but it seems I can't do that.
I'm thinking my entire approach to this has been wrong. How can I save/load the order of an array of database objects in a faster way? Is loading database objects into an array itself bad practice?
Thanks for any help!
Traversing the entire vocabDeckArray and filtering it once for each object in group1Temp is very inefficient. There are any number of ways you could rebuild this sorted data set in less than O(n^2) time.
One easy option might be to store a dictionary with the object's titles as keys and their position in the array as values. That way you can construct an array of known length and put every object in vocabDeckArray into the correct position in a single pass (get first object from vocabDeckArray, lookup where in belongs in group1Array from the dictionary, insert into group1Array, move on to next object). That's still not particularly fast but it seems like a minimal change to your current behavior.
In addition consider the number of method calls within your loop. self.vocabDeckArray and self.group1Array are method calls which you make on every iteration of your loop even though they always return the same objects. Keeping local variables which reference those objects instead would save you the overhead of 2 method calls on every iteration.

Debugging Core Data managed objects with predicates

My application has the data models a little bit complicated. I need to debug a fetch request with different predicates.
Is there any fast way to see different results for different predicates? I am tired with changing only one predicate and I have to start again my navigation application with nearly 10 steps before.
An example of these predicates that I would like to see the results:
item = %#
item = %# AND quantity = %#
item = %# OR (startdate >= %# AND enddate <= %#)
etc...
As using Core Data, I can not see the database with its' values to do some SELECTs.
You can add logic in your code to change the predicate and fetch again (maybe add a temporary button to trigger this and cycle through your various predicates).
You may also be interested in viewing the data in your SQLite file. Check out this answer to How view data stored in Core Data?
I'm not sure if it is any help, but if you want to quickly see the return results, go into gcc command line and write
po <name of array with results>
so if the array is items
po items
Will give all the returned results printed nicely in the console
To view your data inside of SQLite for free, just get FireFox. Then install SQLite Manager.
Cheers.
-RoLYroLLs
http://iphone.rolyrolls.com