Searching File Metadata with NSMetadataQuery
In order for your application to search Spotlight metadata, you must create a query using the NSMetadataQuery class provided by the Foundation framework. Queries can be run in two modes: asynchronous, and asynchronous with live updates. The first simply performs the search on the files that exist at the time of the initial search. The latter continues to search. updating the data as the files that fulfill or no longer fulfill the search parameters update.
There are four main steps for executing an asynchronous metadata query:
Defining and initializing the search.
Initiating the search.
Listen for batch notifications.
Listening for the completion notification.
Stopping the query.
Processing results.
A live asynchronous Spotlight query allows your application to monitor the specified scope for changes that happen ‘on the fly’. The only significant difference in the code is that rather than stopping the query, you simply suspend the query while processing the results, and then resume the search when processing is completed.
Creating a Static File Metadata Search
A static Spotlight search is a search that simply runs, returns the results and quits. it is intended as a one-time search that does not monitor changes (which is possible using live searches, discussed in Creating a Live Search.
Defining a Search
The first step in creating a query is defining a search expression that returns the desired results. If you are using MDMetadataQuery
to execute the query, create your search expression predicate using the syntax described in File Metadata Query Expression Syntax. You must register a notification to inform you as data batches are returned and when the initial search is complete. You can also optionally register the scope, sorting, and a delegate.
Create an
NSMetadataQuery
instance.Register to receive the
NSMetadataQueryDidUpdateNotification
notification which is sent when batches of search content is returned.This notification may not be generated depending on the batch value.
Register to receive the
NSMetadataQueryDidFinishGatheringNotification
notification which is sent when the initial search is completed.
Setting the Query Search
The search predicate is created using the syntax specified in File Metadata Query Expression Syntax. The fields that can be searched are defined in the File Metadata Attributes Reference. The attributes references lists the available metadata keys and the type of data that you must supply to search that attribute (a string, number, an array of strings, a date, or a Uniform Type Identifier.
Setting the Sort Order
If you are using NSMetadataQuery
, you can specify the sort order of the results by providing an array of sort descriptors. The sorting is based on the metadata attribute key of each returned NSMetadataItem
object.
Limiting the Search Scope
An application limits where search results are collected from by specifying a search scope. The search scope is provided to the query as an array of predefined location constants, URLs, and directory paths. The predefined location constants provide convenient values for restricting a query to the user's home directory, locally mounted volumes and the user's home directory, or remote mounted volumes.
The search scopes specify where the metadata query searches for the files. Table 2-1 lists the available scopes.
Scope Constant | Supported Operating Systems | Description |
---|---|---|
iOS and OS X | Search all files in the Documents directories of the application’s iCloud container directories. | |
iOS and OS X | Search all files not in the Documents directories of the application’s iCloud container directories. | |
OS X | Search all user-mounted remote volumes. | |
OS X | Search all local mounted volumes, including the user home directory. The user’s home directory is searched even if it is a remote volume. | |
OS X | Search the user’s home directory. |
The search scopes are specified as an array of the scope constants.
Send your instance of
NSMetadataSearch
asetSearchScopes:
message, passing an array of the appropriate scopes.This query will search the User’s directory on the computer as well as the iCloud Documents folder. This same search code could be run on iOS by simply removing the unsupported
NSMetadataQueryUserHomeScope
scope constant.
Running the Search
Once you have created and configured a query object, you can execute the query itself. When running, a query typically has two phases: an initial results gathering phase and a live-update phase.
During the initial results gathering phase, the existing Spotlight system store is searched for files that match the search expression. The query sends notifications as the results are returned in batches using the NSMetadataQueryDidUpdateNotification
. In a single query this can be useful for indicating the state of the search progress, while in live searches it becomes more important.
The query sends the application a NSMetadataQueryDidFinishGatheringNotification
notification when the initial results gathering phase has completed.
To run the search, send a startQuery
message to your instance of NSMetadataSearch
.
Accessing the Returned Results
Before your application interacts with the returned results, it must first stop the query. You can disable updates during the initial gathering phase of a search or during the live-update phase.
An application determines the number of results that have been returned by invoking the NSMetadataQuery
instance method resultCount
. The application then accesses individual result items by their indexed position. Rather than traversing the results
(which is intended for use with Cocoa Bindings), it is better to request the result item at desired index using the resultAtIndex:
method.
The result items are returned as an object instance of type NSMetadataItem
. Each object encapsulates the metadata attributes for the file. Your application then retrieves the metadata attributes from these items by passing each instance a valueForAttribute:
message with the name of the desired metadata attribute.
Stop the query that is in progress.
Iterate over the results, performing whatever action is appropriate for your application.
Remove the observers for the notifications.
This step is optional if you intend to run the query multiple times. However if you intend to use the same setup code, you may wish to remove the observers regardless.
The Completed Static Search
Creating a Static File Metadata Search shows the code required to implement a static search.
Listing 2-1 Static Spotlight search implementation
// Initialize Search Method |
- (void)initiateSearch |
{ |
// Create the metadata query instance. The metadataSearch @property is |
// declared as retain |
self.metadataSearch=[[[NSMetadataQuery alloc] init] autorelease]; |
// Register the notifications for batch and completion updates |
[[NSNotificationCenter defaultCenter] addObserver:self |
selector:@selector(queryDidUpdate:) |
name:NSMetadataQueryDidUpdateNotification |
object:metadataSearch]; |
[[NSNotificationCenter defaultCenter] addObserver:self |
selector:@selector(initalGatherComplete:) |
name:NSMetadataQueryDidFinishGatheringNotification |
object:metadataSearch]; |
// Configure the search predicate to find all images using the |
// public.image UTI |
NSPredicate *searchPredicate; |
searchPredicate=[NSPredicate predicateWithFormat:@"kMDItemContentTypeTree == 'public.image'"]; |
[metadataSearch setPredicate:searchPredicate]; |
// Set the search scope. In this case it will search the User's home directory |
// and the iCloud documents area |
NSArray *searchScopes; |
searchScopes=[NSArray arrayWithObjects:NSMetadataQueryUserHomeScope, |
NSMetadataQueryUbiquitousDocumentsScope,nil]; |
[metadataSearch setSearchScopes:searchScopes]; |
// Configure the sorting of the results so it will order the results by the |
// display name |
NSSortDescriptor *sortKeys=[[[NSSortDescriptor alloc] initWithKey:(id)kMDItemDisplayName |
ascending:YES] autorelease]; |
[metadataSearch setSortDescriptors:[NSArray arrayWithObject:sortKeys]]; |
// Begin the asynchronous query |
[metadataSearch startQuery]; |
} |
// Method invoked when notifications of content batches have been received |
- (void)queryDidUpdate:sender; |
{ |
NSLog(@"A data batch has been received"); |
} |
// Method invoked when the initial query gathering is completed |
- (void)initalGatherComplete:sender; |
{ |
// Stop the query, the single pass is completed. |
[metadataSearch stopQuery]; |
// Process the content. In this case the application simply |
// iterates over the content, printing the display name key for |
// each image |
NSUInteger i=0; |
for (i=0; i < [metadataSearch resultCount]; i++) { |
NSMetadataItem *theResult = [metadataSearch resultAtIndex:i]; |
NSString *displayName = [theResult valueForAttribute:(NSString *)kMDItemDisplayName]; |
NSLog(@"result at %lu - %@",i,displayName); |
} |
// Remove the notifications to clean up after ourselves. |
// Also release the metadataQuery. |
// When the Query is removed the query results are also lost. |
[[NSNotificationCenter defaultCenter] removeObserver:self |
name:NSMetadataQueryDidUpdateNotification |
object:metadataSearch]; |
[[NSNotificationCenter defaultCenter] removeObserver:self |
name:NSMetadataQueryDidFinishGatheringNotification |
object:metadataSearch]; |
self.metadataSearch=nil; |
} |
@end |
Creating a Live Search
A live search is configured in a manner virtual to an identical search with only a small number of changes.
The query must be suspended using
disableUpdates
before accessing the updated data.Data is typically handled in the
queryDidUpdate:
method in some form.The query is then restarted using
enableUpdates
to continue the search.
Copyright © 2011 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2011-09-28