本文翻译整理自:File Metadata Search Programming Guide(更新日期:2011-09-28
文章目录
一、关于文件元数据查询
文件元数据提供了几个应用程序编程接口,允许it应用程序根据文件或文件系统的数据搜索文件。
您的应用程序与搜索结果所需的交互级别通常会决定您选择的API。
在Mac应用程序中提供元数据支持的最简单方法是使用Spotlight搜索窗口。
使用此API,应用程序可以显示标准Spotlight搜索窗口,可选地提供搜索字符串。
搜索结果直接呈现给用户,应用程序无法使用。
如果您的应用程序不是面向搜索的,但您希望允许用户使用Spotlight搜索上下文术语,这是一个不错的选择。
对于需要创建查询并与结果交互的应用程序,有两种API可用。
元数据框架提供了一个低级查询APIMDQuery
,它允许应用程序根据元数据值搜索文件。
MDQuery
是完全可配置的,允许您运行同步和异步查询,并提供对结果批处理频率的细粒度控制。
Cocoa框架的NSMetadataQuery
类为MDQuery
API提供了一个高级Objective-C接口。
此类允许您使用NSPredicate
类的子集构建查询,并异步执行查询。
NSMetadataQuery
类允许应用程序指定将结果分组为多个子类别。
NSMetadataQuery
不支持同步查询,并在收集数据时提供最少的更新通知。
在OS X上NSMetadataQuery
支持Cocoa绑定,允许您显示结果而无需编写任何大量的胶水代码。
注意:iOS仅提供Objective-C元数据搜索接口。
过程C版本仅在OS X上可用。
1、谁应该阅读此文档
Spotlight是OS X的一个基本特性,所有开发人员都应该熟悉它的功能。
许多应用程序至少应该为用户提供使用Spotlight搜索窗口搜索选定文本的能力。
2、本文件的组织
以下文章介绍了了解如何使用Spotlight查询元数据的关键概念:
- 使用NSMetadataQuery搜索文件元数据提供了使用文件元数据搜索文件的概念性概述。
- 显示Finder的Spotlight搜索窗口描述了如何显示标准的Spotlight搜索窗口。
- 文件元数据查询表达式语法描述了元数据查询语言。
3、另见
对于将元数据集成到您的应用程序中,还有其他技术,本文档没有完全介绍。
有关更多详细信息,请参阅这些文档:
- Spotlight概述 涵盖了围绕Spotlight元数据使用的概念细节。
- Spotlight导入器编程指南 描述了从文档文件中提取元数据的插件。
- 文件元数据属性参考 描述了苹果提供的元数据属性。
以下示例代码可用于显示如何生成Spotlight查询。
- Spotlight 示例展示了如何使用Spotlight搜索。
- PredicateEditorSample 演示如何使用规则编辑器和Spotlight。
- PhotoSearch 允许根据名称搜索图像。
它允许同时运行多个搜索。
二、搜索iCloud和桌面
Spotlight搜索功能可在iOSv5.0和OS X上使用。
使用这些搜索查询iOS可以搜索iCloud内容,OS X可以搜索iCloud、桌面和网络文件。
1、OS X搜索功能
Spotlight在OS X上无处不在。
Finder使用它来搜索允许用户搜索文件。
Mail在搜索过程中使用Spotlight查找消息,任何应用程序都可以使用Spotlight搜索用户的主目录、本地文件系统、任何附加的网络文件系统和iCloud。
Spotlight API的OS X Objective-C接口包括用于搜索(NSMetadataQuery
)、检查文件元数据(NSMetadataItem
)的类,以及让NSMetadataQuery
使用NSMetadataQueryAttributeValueTuple
类返回更复杂的数据集的能力。
此外,存在一个对应于每个Objective-C类的Core Foundation API(事实上,这些类是基于Core Foundation基础构建的)。
注意:依靠文件元数据非常有用。
它允许您检索有关各种文件属性的信息,例如,视频帧大小、声音文件压缩、Finder注释、文件作者等等,而无需知道如何读取文件格式。
请注意,用户可以根据每个卷或文件夹禁用Spotlight。
2、iOS搜索功能
iOS允许在iCloud内元数据搜索以查找文件对应的文件。
它仅提供文件元数据查询的Objective-C接口NSMetadataQuery
和NSMetadataItem
,以及仅支持搜索iCloud的搜索范围。
与桌面不同,iOS应用程序的沙盒无法使用元数据类进行搜索。
为了搜索应用程序的沙盒,您需要使用NSFileManager
类递归遍历沙盒文件系统中的文件。
一旦找到匹配的文件,您可以按照需要的方式访问该文件。
您还可以使用NSMedatataItem
类来检索该特定文件的元数据。
三、使用NSMetadataQuery搜索文件元数据
为了让您的应用程序搜索Spotlight元数据,您必须使用Foundation框架提供的NSMetadataQuery类创建查询。
查询可以在两种模式下运行:异步和异步实时更新。
第一种模式只是对初始搜索时存在的文件执行搜索。
后者继续搜索。
将数据更新为满足或不再满足搜索参数更新的文件。
执行异步元数据查询有四个主要步骤:
- 定义和初始化搜索。
- 启动搜索。
- 侦听批处理通知。
- 监听完成通知。
- 停止查询。
- 处理结果。
实时异步Spotlight查询允许您的应用程序监视指定范围内动态发生的更改。
代码中唯一重要的区别是,您只需在处理结果时暂停查询,然后在处理完成后恢复搜索,而不是停止查询。
1、创建静态文件元数据搜索
静态Spotlight搜索是一种简单运行、返回结果并退出的搜索。
它旨在作为不监控更改的一次性搜索(这可以使用实时搜索,在创建实时搜索中讨论。
定义搜索
创建查询的第一步是定义返回所需结果的搜索表达式。
如果您使用MDMetadataQuery
执行查询,请使用文件元数据查询表达式语法中描述的语法创建搜索表达式谓词。
您必须注册通知,以便在返回数据批次和初始搜索完成时通知您。
您还可以选择注册范围、排序和委托。
定义搜索
- 创建一个
NSMetadataQuery
实例。 - 注册以接收返回批量搜索内容时发送的
NSMetadataQueryDidUpdateNotification
通知。
根据批次值,可能不会生成此通知。 - 注册以接收初始搜索完成时发送的
NSMetadataQueryDidFinishGatheringNotification
通知。
设置查询搜索
使用文件元数据查询表达式语法中指定的语法创建搜索谓词。
可以搜索的字段在*文件元数据属性引用*中定义。
属性引用列出了可用的元数据键和搜索该属性必须提供的数据类型(字符串、数字、字符串数组、日期或统一类型标识符)。
创建查询
- 使用适当的Spotlight查询表达式创建
NSPredicate
实例。
设置排序顺序
如果使用的是NSMetadataQuery
,可以通过提供排序描述符数组来指定结果的排序顺序,排序基于每个返回的NSMetadataItem
对象的元数据属性键。
设置搜索顺序
- 使用所需的元数据键创建NSSortDescriptor进行排序,在本例中为
kMDItemDisplayName
。
限制搜索范围
应用程序通过指定搜索范围来限制搜索结果的收集位置。
搜索范围作为预定义位置常量、URL和目录路径的数组提供给查询。
预定义位置常量提供方便的值,用于将查询限制在用户的主目录、本地安装的卷和用户的主目录或远程安装的卷。
搜索范围指定元数据查询搜索文件的位置。
表2-1支持的搜索范围
范围常数 | 支持的操作系统 | 描述 |
---|---|---|
NSMetadataQueryUbiquitousDocumentsScope | iOS和OS X | 搜索应用程序iCloud容器目录的Documents目录中的所有文件。 |
NSMetadataQueryUbiquitousDataScope | iOS和OS X | 搜索不在应用程序iCloud容器目录的Documents目录中的所有文件。 |
NSMetadataQueryNetworkScope | OS X | 搜索所有用户安装的远程卷。 |
NSMetadataQueryLocalComputerScope | OS X | 搜索所有本地挂载的卷,包括用户主目录。 即使是远程卷,也会搜索用户的主目录。 |
NSMetadataQueryUserHomeScope | OS X | 搜索用户的主目录。 |
搜索范围被指定为范围常量的数组。
指定搜索范围
- 向
NSMetadataSearch
实例发送setSearchScopes:
消息,传递适当范围的数组。
此查询将搜索计算机上的用户目录以及iCloud文档文件夹。只需删除不受支持的NSMetadataQueryUserHomeScope
范围常量,即可在iOS上运行相同的搜索代码。
注意:重要的是要记住,在OS X上,虽然文件系统元数据在所有卷上都可用,但其他元数据属性不可用。
CD、DVD、磁盘映像和系统目录不会被Spotlight索引。
用户还可以使用Spotlight首选项显式排除特定目录和文档类型的返回结果。
iOS只能使用常规->Spotlight搜索下的设置应用程序中的Spotlight搜索选项从搜索结果中删除文档类型。
运行搜索
创建和配置查询对象后,您可以自行执行查询。
运行时,查询通常有两个阶段:初始结果收集阶段和实时更新阶段。
在初始结果收集阶段,将在现有的Spotlight系统存储中搜索与搜索表达式匹配的文件。
当使用NSMetadataQueryDidUpdateNotification
批量返回结果时,查询会发送通知。
在单个查询中,这对于指示搜索进度状态很有用,而在实时搜索中,这变得更加重要。
初始结果收集阶段完成后,查询会向应用程序发送NSMetadataQueryDidFinishGatheringNotification
通知。
要运行搜索,请向NSMetadataSearch
实例 发送startQuery
消息。
访问返回的结果
在您的应用程序与返回的结果交互之前,它必须首先停止查询。
您可以在搜索的初始收集阶段或实时更新阶段禁用更新。
应用程序通过调用NSMetadataQuery
实例方法resultCount
来确定返回的结果数。
然后,应用程序通过索引位置访问单个结果项。
与其遍历results
(用于Cocoa Bindings),不如使用结果resultAtIndex:
方法在所需索引处请求结果项。
结果项作为NSMetadataItem
类型的对象实例返回。
每个对象都封装了文件的元数据属性。
然后,应用程序通过向每个实例传递带有所需元数据属性名称的valueForAttribute:
消息来从这些项中检索元数据属性。
访问结果
- 停止正在进行的查询。
- 遍历结果,执行适合您的应用程序的任何操作。
- 删除通知的观察者。
如果您打算多次运行查询,则此步骤是可选的。但是,如果您打算使用相同的设置代码,您可能希望无论如何都删除观察者。
完成的静态搜索
创建静态文件元数据搜索显示实现静态搜索所需的代码。
例2-1静态Spotlight搜索实现
// 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
2、创建实时搜索
实时搜索以虚拟的方式配置为仅进行少量更改的相同搜索。
- 在访问更新的数据之前,必须使用
disableUpdates
暂停查询。 - 数据通常以某种形式在
queryDidUpdate:
方法中处理。 - 然后使用
enableUpdates
重新启动查询以继续搜索。
四、文件元数据查询表达式语法
文件元数据查询使用作为谓词字符串格式子集的查询语言构建。
元数据搜索表达式语法允许应用程序“即时”构建搜索,允许高级用户构建自己的查询,或者您的应用程序存储常用查询以便于使用。
语法相对简单,包括比较、与语言无关的选项以及时间和日期变量。
比较语法
文件元数据查询表达式语法是shell用户熟悉的文件名全局化的简化形式。
查询具有以下格式:
attribute == value
其中attribute
是标准元数据属性(参见*文件元数据属性参考*)或由导入器定义的自定义元数据属性。
例如,要查询Spotlight以获取“Steve”创作的所有文件,查询如下所示:
kMDItemAuthors ==[c] "Steve"
表3-1列出了可用的比较运算符。
Table 3-1 Comparison operators
算子 | 描述 |
---|---|
== | 相等 |
!= | 不相等 |
< | 小于(仅适用于数值和日期) |
> | 大于(仅适用于数值和日期) |
<= | 小于或等于(仅适用于数值和日期) |
>= | 大于或等于(仅适用于数值和日期) |
InRange(attributeName,minValue,maxValue) | 指定属性名称中minValue到maxValue范围内的数值 |
值字符串中的“
和‘
等字符应使用\
字符进行转义。
示例中的搜索值有修饰符“c
”。
这些修饰符指定如何进行比较。
表3-2描述了可用的比较修饰符。
搜索修饰符应紧跟在比较运算符之后,并用方括号[…]
括起来。
Table 3-2 Value Comparison modifiers
修改 | 描述 |
---|---|
c | 比较不区分大小写。 |
d | 这种比较对变音符号不敏感。 |
表3-3显示了几个使用比较修饰符的示例。
Table 3-3 Value Comparison modifier examples
查询字符串 | 结果 |
---|---|
kMDItemTextContent == "Paris" | 匹配“巴黎”但不匹配“巴黎”。 |
kMDItemTextContent ==[c] "Paris" | 匹配“巴黎”和“巴黎”。 |
kMDItemTextContent ==[c] "*Paris*" | 匹配“Paris”、“paris”、“I love Paris”和“paris-French. jpg”。 |
kMDItemTextContent == "Frédéric" | 匹配“Frédéric”但不匹配“Frederic”。 |
kMDItemTextContent ==[d] "Frédéric" | 无论单词case如何,都匹配“Frédéric”和“Frederic”。 |
使用通配符(*
和?
),您可以在字符串的开头、结尾或字符串中的任何位置匹配子字符串。
表3-4显示了几种常见用法。
字符*
匹配多个字符,而?
通配符匹配单个字符。
Table 3-4 Using wildcards
查询字符串 | 结果 |
---|---|
kMDItemTextContent == "paris*" | 匹配以“paris”开头的属性值。 例如,匹配“paris”,但不匹配“比较”。 |
kMDItemTextContent == "*paris" | 匹配以“paris”结尾的属性值。 |
kMDItemTextContent == "*paris*" | 匹配值中任何位置包含“paris”的属性。 例如,匹配“paris”和“比较”。 |
kMDItemTextContent == "paris" | 匹配完全等于“paris”的属性值。 |
可以使用类似C的语法组合查询AND
(&&
)和OR
(||
)。
例如,要将查询限制为“史蒂夫”创作的音频文件,查询将是:
kMDItemAuthors ==[c] "Steve" && kMDItemContentType == "public.audio"
括号可用于进一步组查询匹配。
例如,要搜索由“Steve”或“Daniel”创作的音频文件,查询将是:
(kMDItemAuthors ==[c] "Daniel" || kMDItemAuthors[c] == "Steve") &&
kMDItemContentType == "public.audio"
您可以使用以下查询扩展此搜索以包含视频文件:
(kMDItemAuthors ==[c] "Daniel" || kMDItemAuthors ==[c] "Steve" ) &&
(kMDItemContentType == "public.audio" || kMDItemContentType == "public.video")
时间和日期变量
您还可以创建使用日期和时间作为搜索值的查询。
日期和时间值被格式化为与CFDate兼容的浮点值,相对于2001年1月1日为秒。
此外,还提供了$time
变量,可用于指定相对于当前时间的值,如表3-5所示。
Table 3-5 $time variable expressions
时间变量 | 描述 |
---|---|
$time.now | 当前日期和时间。 |
$time.today | 当前日期。 |
$time.yesterday | 昨天的日期。 |
$time.this_week(-1) | 前一周的日期。 |
$time.this_week | 本周的日期。 |
$time.this_month | 当前月份的日期。 |
$time.this_year | 当年的日期。 |
$time.now(NUMBER) | 通过将正值或负值(以秒为单位)添加到当前时间的日期和时间。 |
$time.today(NUMBER) | 通过将正值或负值(以天为单位)添加到当前日期的日期 |
$time.this_week(NUMBER) | 通过向当前周添加正值或负值(以周为单位)来确定日期。 |
$time.this_month(NUMBER) | 通过将正值或负值(以月为单位)添加到当前月份的日期。 |
$time.this_year(NUMBER) | 通过在当前年份中添加正值或负值(以年为单位)来确定日期。 |
$time.iso(ISO-8601-STR) | 通过解析指定的ISO-8601-STR兼容字符串的日期。 |
使用$time
变量,您可以使用以下查询将搜索限制为仅查找上周更改的文件:
((kMDItemAuthors ==[c] "Daniel" || kMDItemAuthors[c] == "Steve") &&
(kMDItemContentType == "public.audio" || kMDItemContentType == "public.video")) &&
(kMDItemFSContentChangeDate == $time.this_week(-1))
注意: 第一次执行查询时设置了$time
变量的值。
随着查询继续运行,它不会更新到当前时间。
五、显示Finder的Spotlight搜索窗口
应用程序可以通过显示标准的Finder搜索界面为用户提供与Spotlight的直接交互。
该NSWorkspace
方法showSearchResultsForQueryString:
为Finder搜索窗口提供了一个简单的界面。
这在编程上相当于用户切换到Finder,创建一个新窗口,并在搜索字段中键入搜索字符串。
显示Finder的Spotlight Search Window中的代码片段演示了提取字符串值并显示搜索界面。
例4-1显示Finder搜索窗口
resultCode=[[NSWorkspace sharedWorkspace] showSearchResultsForQueryString:[sender stringValue]];
if (resultCode == NO) {
// failed to open the panel
// present an error to the user
}
2024-06-16(日) |