Full Text Search (Part 7)

A word about the BEGINSWITH operator of NSPredicate: “Caution”! I was using this operator to drive iterative prompting while developing my Full-Text Search (FTS) demo, and found its performance unsatisfactory.


Iterative Prompting

I want to show the user a menu of indexed search terms which share the prefix he has entered in the search box; you can see the desired effect to the right. I accomplish this with a predictable mix of delegates, table views, fetched results controllers, and so on: The bit I want to focus on is the NSPredicate that selects the keywords to display. Here was my first version of the constructor for that predicate:

[NSPredicate predicateWithFormat:@"keyword BEGINSWITH %@",start]

(The start variable is derived from the search bar, and calculated elsewhere.)


That predicate threw off SQL like the following:

Select 0, t0.Z_PK
Where NSCoreDataStringSearch(t0.ZKEYWORD, ?, 8, 0)
Order By t0.ZKEYWORD
Collate NSCollateLocaleSensitive

This code was taking between 0.2s and 0.3s to execute on my iPhone 3G; that wasn’t quite responsive enough for real-time behavior. I guessed that the problem was the NSCoreDataStringSearch() function, which is obviously defined by Core Data, and not something built in to SQLite.


I decided to change my predicate setup code to the following:

NSString* stop = [start stringByAppendingString:@"zzz"];
NSArray* range = [NSArray arrayWithObjects:[NSExpression expressionForConstantValue:start],[NSExpression expressionForConstantValue:stop],nil];
self.keywordResultsController.fetchRequest.predicate = [NSPredicate predicateWithFormat:@"keyword BETWEEN %@",range];

This produced SQL like the following:

Select 0, t0.Z_PK
Where (t0.ZKEYWORD Between ? AND ?)
Order By t0.ZKEYWORD
Collate NSCollateLocaleSensitive

This code typically ran (on my particular device, against my particular test corpus) in between 0.01s and 0.06s; I had one spike to 0.15s, but that seemed to be a startup anomaly. This isn’t a huge change, but it’s the difference between real-time responsiveness and a clunky, stuttering interface.


One word of warning, on a matter which I think the predicate format documentation makes opaque: The NSArray supplied as the argument to a BETWEEN clause in an NSPredicate format string must contain NSExpressions if you wish to compare strings, and not NSStrings. (I haven't tested what happens if you're comparing, say, numbers or dates.) If you do supply an array containing NSStrings, the executable will crash.

Share and Enjoy:
  • Twitter
  • Facebook
  • Digg
  • Reddit
  • HackerNews
  • del.icio.us
  • Google Bookmarks
  • Slashdot
This entry was posted in iPhone. Bookmark the permalink.

Comments are closed.