前言
Spotlight 是苹果在Tiger(10.4)引入的一项快速搜索技术,在Leopard中,Spotlight已经无缝的整合进入了Finder。从iOS3.0开始,Spotlight被移植到了iOS。在OSX中,用户点击系统菜单栏右上角的图标就可以使用Spotlight。在iOS中,用户手指滑向主屏幕画面左侧就可以打开类似的窗口。
Spotlight背后实现机制是它有一个索引服务器mds
,mds在MetaData框架中,MeteData框架是系统核心服务的一部分,其路径是在
/SystemLibrary/Frameworks/CoreServices.framework/Frameworks/Metadata.framewrk.
mds是一个后台服务的程序,每当有文件被操作时(创建、修改和删除)发生时,内核都会通知这个mds程序,这个通知机制叫做FSEvents
,由于工作原因,我对相应的api进行的粗略的翻译CoreServer文件系统监控
。感兴趣可以看一下。
当mds收到FSEvents通知时,mds会通过工作进程(mdworker)将各种元数据信息导入数据库。mdworker进程可以加载一个具体的Spotlight Importer(Spotlight导入器)从文件中提前元数据。系统提供的导入器位于/System/Library/Spotlight目录
[图片上传失败...(image-70d3f9-1632396718794)]
用户提供的导入器位于/Library/Spotlight目录。我们可以通过构建MeteData Importer模块构建,自定义Spotlight导入器,其官方文档Spotlight Importer编程指南
通过命令行访问Spotlight
mdutil
:管理元数据数据库
mdfind
:发出spotlight查询
mdfind -name 1398
/Users/xxx/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9/cd699567a64b554ba1d26d52add8b8db/Message/MessageTemp/8198a01c1bfa503859f285a1e1ef44af/Image/13981631266198_.pic_thumb.jpg
/Users/xxxx/Desktop/文件读取测试/test221/1398.txt
/Users/xxx/Desktop/文件读取测试/test221.bundle/1398.txt
/System/Library/PrivateFrameworks/Memories.framework/Versions/A/Resources/FlexAudio/18f501e0-4e5d-4af0-95ba-7ad429ee44d8.smsbundle/Summaries/139.83.summary
mdls
:列出文件的元数据属性
mdls /Users/xxxx/Desktop/文件读取测试/test221.bundle
_kMDItemDisplayNameWithExtensions = "test221.bundle" //文件扩展名
kMDItemContentCreationDate = 2021-08-30 07:15:22 +0000 //文件内容创建时间
kMDItemContentCreationDate_Ranking = 2021-08-30 00:00:00 +0000
kMDItemContentModificationDate = 2021-09-10 07:52:30 +0000 //文件内容修改时间
kMDItemContentModificationDate_Ranking = 2021-09-10 00:00:00 +0000
kMDItemContentType = "com.apple.generic-bundle" //文件内容类型
kMDItemContentTypeTree = (
"com.apple.generic-bundle",
"com.apple.bundle",
"public.directory",
"public.item",
"com.apple.package"
)
kMDItemDateAdded = 2021-09-10 11:26:13 +0000 //文件添加时间
kMDItemDateAdded_Ranking = 2021-09-10 00:00:00 +0000
kMDItemDisplayName = "test221.bundle"
kMDItemDocumentIdentifier = 0
kMDItemFSContentChangeDate = 2021-09-10 07:52:30 +0000 //内容修改时间
kMDItemFSCreationDate = 2021-08-30 07:15:22 +0000
kMDItemFSCreatorCode = ""
kMDItemFSFinderFlags = 0
kMDItemFSHasCustomIcon = (null)
kMDItemFSInvisible = 0=
kMDItemFSIsExtensionHidden = 0
kMDItemFSIsStationery = (null)
kMDItemFSLabel = 0
kMDItemFSName = "test221.bundle"
kMDItemFSNodeCount = 8
kMDItemFSOwnerGroupID = 20
kMDItemFSOwnerUserID = 501
kMDItemFSSize = 278040
kMDItemFSTypeCode = ""
kMDItemInterestingDate_Ranking = 2021-09-10 00:00:00 +0000 //文件种类
kMDItemKind = "捆绑包"
kMDItemLogicalSize = 278040 //文件大小 字节
kMDItemPhysicalSize = 303104
mdimport
:配置和测试spotlight插件
代码实现Spotlight检索
两种实现方式
- 使用
MDQuery
(CoreSerive.framework)类,里面提供的都是C语言编写的,并且仅提供在OSX上使用 - 使用
NSMetaDataQuery
(CoreFoundation中)类。是苹果提供的一层对MDQuery封装的高级API。相比MDQuery不支持同步查询。并在收集数据时提供最少的更新通知。
使用NSMetaDataQuery实现文件检索
执行异步元数据查询主要有四个步骤:
1.定义和初始化搜索
- 创建一个
NSMetadataQuery
实例。 - 注册接收
NSMetadataQueryDidUpdateNotification
批量搜索内容返回时发送的通知根据批次值,可能不会生成此通知。 - 注册以接收
NSMetadataQueryDidFinishGatheringNotification
初始搜索完成时发送的通知。
2.设置搜索
2.1 设置查询语句
NSPredicate
使用适当的 Spotlight 查询表达式创建一个实例。
2.2 设置排序顺序
可以通过提供排序描述符数组来指定结果的排序顺序。排序基于每个返回NSMetadataItem
对象的元数据属性键。使用所需的元数据键创建一个 NSSortDescriptor 进行排序,在本例中为kMDItemDisplayName
。
2.3 设置搜索范围
应用程序通过指定搜索范围来限制从何处收集搜索结果。
搜索范围指定元数据查询搜索文件的位置。
范围常数 | 支持的操作系统 | 描述 |
---|---|---|
NSMetadataQueryUbiquitousDocumentsScope |
iOS 和 OS X | 搜索应用程序 iCloud 容器目录的 Documents 目录中的所有文件。 |
NSMetadataQueryUbiquitousDataScope |
iOS 和 OS X | 搜索不在应用程序 iCloud 容器目录的 Documents 目录中的所有文件。 |
NSMetadataQueryNetworkScope |
操作系统 | 搜索所有用户安装的远程卷。 |
NSMetadataQueryLocalComputerScope |
操作系统 | 搜索所有本地安装的卷,包括用户主目录。即使是远程卷,也会搜索用户的主目录。 |
NSMetadataQueryUserHomeScope |
操作系统 | 搜索用户的主目录。 |
注意
:在 OS X 上,虽然文件系统元数据在所有卷上可用,但其他元数据属性不可用。Spotlight 不会为 CD、DVD、磁盘映像和系统目录编制索引。
3.启动搜索
创建并配置查询对象后,您可以通过调用startQuery
函数执行查询。
运行时,查询通常有两个阶段:初始结果收集阶段和实时更新阶段。
在初始结果收集阶段,会在现有 Spotlight系统存储中搜索与搜索表达式匹配的文件。当结果使用NSMetadataQueryDidUpdateNotification
. 在单个查询中,这对于指示搜索进度的状态很有用,而在实时搜索中它变得更加重要。
NSMetadataQueryDidFinishGatheringNotification
当初始结果收集阶段完成时,查询向应用程序发送通知。
4.访问返回结果
在您的应用程序与返回的结果交互之前,它必须首先停止查询。
您可以在搜索的初始收集阶段或实时更新阶段禁用更新。应用程序通过调用NSMetadataQuery
实例方法确定已返回的结果数resultCount
。然后,应用程序使用resultAtIndex:
方法请求所需索引处的结果项。
结果项作为类型的对象实例返回NSMetadataItem
。每个对象都封装了文件的元数据属性。
然后,您的应用程序通过向每个实例valueForAttribute:
传递带有所需元数据属性名称的消息,从这些项目中检索元数据属性。
5.NSMetadataQuery代码示例
// 初始化搜索方法
- (void)initiateSearch {
//1.初始化创建搜索
self.metadataSearch = [[NSMetadataQuery alloc] init];
//注册通知监听
[self addQueryObserver];
//2.设置查询
//2.1 设置查询语句
NSPredicate *searchPredicate =[NSPredicate predicateWithFormat:@"kMDItemDisplayName == 'fileName'"];
[metadataSearch setPredicate:searchPredicate];
//2.2 设置排序顺序,以便对查询结果进行排序
NSSortDescriptor *sortKeys = [[[NSSortDescriptor alloc] initWithKey:(id)kMDItemDisplayName
升序:是] 自动释放];
[metadataSearch setSortDescriptors:[NSArray arrayWithObject:sortKeys]];
//2.3 设置搜索范围
NSArray *searchScopes = [NSArray arrayWithObjects:NSMetadataQueryUserHomeScope,
NSMetadataQueryUbiquitousDocumentsScope,nil];
[metadataSearch setSearchScopes:searchScopes];
//3.开启异步查询
[metadataSearch startQuery];
}
- (void)addQueryObserver {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(initalGatherComplete:) name:NSMetadataQueryDidFinishGatheringNotification object:_metaDataSearch];
}
- (void)removeQueryObserver {
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:_metaDataSearch];
}
// 初始查询收集完成时调用的方法
- (void)initalGatherComplete:sender;
{
// 停止查询,单遍完成。
[_metaDataSearch stopQuery];
// 处理内容。在这种情况下,应用程序只需迭代内容,打印显示名称键
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);
}
// 删除通知以在我们自己之后清理。
// 同时释放metadataQuery。
// 当 Query 被移除时,查询结果也会丢失。
[self removeQueryObserver];
self.metaDataSearch = nil;
}
文件查询语句规则
文件元数据查询语句规则