Sunday, January 11, 2009

Tremendous speedup for NSMetadataQuery

In QNote I am using a NSMetadataQuery to allow the user to search for notes. Even with a notes database of a trivial size, it took ages to find notes (it is basically faster to open a terminal and issue a grep command ). And this even when
I have already restricted the searchScope to the directory with the notes in them.

Today I was experimenting with Spotlight again (is there really good documentation for it out there?).

When I add the type of the data I am expecting (plain text), the search gets real quick - exactly as expected. So the code now looks like this:

// Predicate expression for the search string
NSString *predicateFormat = @"kMDItemTextContent like[cd] %@";

// Predicate expression for
NSString *textType = @"kMDItemContentType == 'public.plain-text'";

// If there is something to search for ..
if (_searchKey != nil )
// Create the predicate for the key
NSPredicate *p = [NSPredicate predicateWithFormat:predicateFormat,
[_searchKey stringByAppendingString:@"*"]];

// And one for the type
NSPredicate * subPredicate = [NSPredicate predicateWithFormat:

// Combine the two
p = [NSCompoundPredicate andPredicateWithSubpredicates:
[NSArray arrayWithObjects:p, subPredicate, nil]];

// And set on the query
[_query setPredicate:p];

This simple addition of kMDItemContentType == 'public.plain-text' provides the speedup.
It is unfortunately not clear to me why, as I don't have any other files of other types in the searchScope.

Next thing for me to investigate is not why this spinning thingy (they are called async arrows :) does not stop spinning when spotlight does no longer return results. Perhaps I need to programatically set this and can't rely on the cocoa bindings here - does anyone know?


Anonymous said...

Contrary to what most developers expect, setting a path scope on a query does NOT speed it up. In fact quite the opposite! What you are forcing Spotlight to do is fetch the path of every matching object in the index, and then check to see if it falls within the scope... With this in mind, you can see why making your query more specific (by adding predicate terms) speeds it up so much.

Heiko said...

Thanks for the comment.

But wouldn't I get too much results when not restricting the path? I mean there are lots more text files on the system. Or is the only solution here to create a custom type for each of my objects even when they are just plain text?