文件检索(Spotlight)

1,913 阅读6分钟

前言

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.png 用户提供的导入器位于/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.定义和初始化搜索

  1. 创建一个NSMetadataQuery实例。
  2. 注册接收NSMetadataQueryDidUpdateNotification批量搜索内容返回时发送的通知根据批次值,可能不会生成此通知。
  3. 注册以接收NSMetadataQueryDidFinishGatheringNotification初始搜索完成时发送的通知。

2.设置搜索

2.1 设置查询语句

NSPredicate使用适当的 Spotlight 查询表达式创建一个实例。

2.2 设置排序顺序

可以通过提供排序描述符数组来指定结果的排序顺序。排序基于每个返回NSMetadataItem对象的元数据属性键。使用所需的元数据键创建一个 NSSortDescriptor 进行排序,在本例中为kMDItemDisplayName

2.3 设置搜索范围

应用程序通过指定搜索范围来限制从何处收集搜索结果。 搜索范围指定元数据查询搜索文件的位置。

范围常数支持的操作系统描述
NSMetadataQueryUbiquitousDocumentsScopeiOS 和 OS X搜索应用程序 iCloud 容器目录的 Documents 目录中的所有文件。
NSMetadataQueryUbiquitousDataScopeiOS 和 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;
}

文件查询语句规则

文件元数据查询语句规则