前言
Spotlight 是蘋(píng)果在Tiger(10.4)引入的一項(xiàng)快速搜索技術(shù),在Leopard中,Spotlight已經(jīng)無(wú)縫的整合進(jìn)入了Finder。從iOS3.0開(kāi)始,Spotlight被移植到了iOS。在OSX中,用戶(hù)點(diǎn)擊系統(tǒng)菜單欄右上角的??圖標(biāo)就可以使用Spotlight。在iOS中,用戶(hù)手指滑向主屏幕畫(huà)面左側(cè)就可以打開(kāi)類(lèi)似的窗口。
Spotlight背后實(shí)現(xiàn)機(jī)制是它有一個(gè)索引服務(wù)器mds,mds在MetaData框架中,MeteData框架是系統(tǒng)核心服務(wù)的一部分,其路徑是在
/SystemLibrary/Frameworks/CoreServices.framework/Frameworks/Metadata.framewrk.
mds是一個(gè)后臺(tái)服務(wù)的程序,每當(dāng)有文件被操作時(shí)(創(chuàng)建、修改和刪除)發(fā)生時(shí),內(nèi)核都會(huì)通知這個(gè)mds程序,這個(gè)通知機(jī)制叫做FSEvents,由于工作原因,我對(duì)相應(yīng)的api進(jìn)行的粗略的翻譯CoreServer文件系統(tǒng)監(jiān)控
。感興趣可以看一下。
當(dāng)mds收到FSEvents通知時(shí),mds會(huì)通過(guò)工作進(jìn)程(mdworker)將各種元數(shù)據(jù)信息導(dǎo)入數(shù)據(jù)庫(kù)。mdworker進(jìn)程可以加載一個(gè)具體的Spotlight Importer(Spotlight導(dǎo)入器)從文件中提前元數(shù)據(jù)。系統(tǒng)提供的導(dǎo)入器位于/System/Library/Spotlight目錄
[圖片上傳失敗...(image-70d3f9-1632396718794)]
用戶(hù)提供的導(dǎo)入器位于/Library/Spotlight目錄。我們可以通過(guò)構(gòu)建MeteData Importer模塊構(gòu)建,自定義Spotlight導(dǎo)入器,其官方文檔Spotlight Importer編程指南
通過(guò)命令行訪(fǎng)問(wèn)Spotlight
mdutil:管理元數(shù)據(jù)數(shù)據(jù)庫(kù)
mdfind:發(fā)出spotlight查詢(xún)
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/文件讀取測(cè)試/test221/1398.txt
/Users/xxx/Desktop/文件讀取測(cè)試/test221.bundle/1398.txt
/System/Library/PrivateFrameworks/Memories.framework/Versions/A/Resources/FlexAudio/18f501e0-4e5d-4af0-95ba-7ad429ee44d8.smsbundle/Summaries/139.83.summary
mdls:列出文件的元數(shù)據(jù)屬性
mdls /Users/xxxx/Desktop/文件讀取測(cè)試/test221.bundle
_kMDItemDisplayNameWithExtensions = "test221.bundle" //文件擴(kuò)展名
kMDItemContentCreationDate = 2021-08-30 07:15:22 +0000 //文件內(nèi)容創(chuàng)建時(shí)間
kMDItemContentCreationDate_Ranking = 2021-08-30 00:00:00 +0000
kMDItemContentModificationDate = 2021-09-10 07:52:30 +0000 //文件內(nèi)容修改時(shí)間
kMDItemContentModificationDate_Ranking = 2021-09-10 00:00:00 +0000
kMDItemContentType = "com.apple.generic-bundle" //文件內(nèi)容類(lèi)型
kMDItemContentTypeTree = (
"com.apple.generic-bundle",
"com.apple.bundle",
"public.directory",
"public.item",
"com.apple.package"
)
kMDItemDateAdded = 2021-09-10 11:26:13 +0000 //文件添加時(shí)間
kMDItemDateAdded_Ranking = 2021-09-10 00:00:00 +0000
kMDItemDisplayName = "test221.bundle"
kMDItemDocumentIdentifier = 0
kMDItemFSContentChangeDate = 2021-09-10 07:52:30 +0000 //內(nèi)容修改時(shí)間
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 //文件種類(lèi)
kMDItemKind = "捆綁包"
kMDItemLogicalSize = 278040 //文件大小 字節(jié)
kMDItemPhysicalSize = 303104
mdimport:配置和測(cè)試spotlight插件
代碼實(shí)現(xiàn)Spotlight檢索
兩種實(shí)現(xiàn)方式
- 使用
MDQuery(CoreSerive.framework)類(lèi),里面提供的都是C語(yǔ)言編寫(xiě)的,并且僅提供在OSX上使用 - 使用
NSMetaDataQuery(CoreFoundation中)類(lèi)。是蘋(píng)果提供的一層對(duì)MDQuery封裝的高級(jí)API。相比MDQuery不支持同步查詢(xún)。并在收集數(shù)據(jù)時(shí)提供最少的更新通知。
使用NSMetaDataQuery實(shí)現(xiàn)文件檢索
執(zhí)行異步元數(shù)據(jù)查詢(xún)主要有四個(gè)步驟:
1.定義和初始化搜索
- 創(chuàng)建一個(gè)
NSMetadataQuery實(shí)例。 - 注冊(cè)接收
NSMetadataQueryDidUpdateNotification批量搜索內(nèi)容返回時(shí)發(fā)送的通知根據(jù)批次值,可能不會(huì)生成此通知。 - 注冊(cè)以接收
NSMetadataQueryDidFinishGatheringNotification初始搜索完成時(shí)發(fā)送的通知。
2.設(shè)置搜索
2.1 設(shè)置查詢(xún)語(yǔ)句
NSPredicate使用適當(dāng)?shù)?Spotlight 查詢(xún)表達(dá)式創(chuàng)建一個(gè)實(shí)例。
2.2 設(shè)置排序順序
可以通過(guò)提供排序描述符數(shù)組來(lái)指定結(jié)果的排序順序。排序基于每個(gè)返回NSMetadataItem對(duì)象的元數(shù)據(jù)屬性鍵。使用所需的元數(shù)據(jù)鍵創(chuàng)建一個(gè) NSSortDescriptor 進(jìn)行排序,在本例中為kMDItemDisplayName。
2.3 設(shè)置搜索范圍
應(yīng)用程序通過(guò)指定搜索范圍來(lái)限制從何處收集搜索結(jié)果。
搜索范圍指定元數(shù)據(jù)查詢(xún)搜索文件的位置。
| 范圍常數(shù) | 支持的操作系統(tǒng) | 描述 |
|---|---|---|
NSMetadataQueryUbiquitousDocumentsScope |
iOS 和 OS X | 搜索應(yīng)用程序 iCloud 容器目錄的 Documents 目錄中的所有文件。 |
NSMetadataQueryUbiquitousDataScope |
iOS 和 OS X | 搜索不在應(yīng)用程序 iCloud 容器目錄的 Documents 目錄中的所有文件。 |
NSMetadataQueryNetworkScope |
操作系統(tǒng) | 搜索所有用戶(hù)安裝的遠(yuǎn)程卷。 |
NSMetadataQueryLocalComputerScope |
操作系統(tǒng) | 搜索所有本地安裝的卷,包括用戶(hù)主目錄。即使是遠(yuǎn)程卷,也會(huì)搜索用戶(hù)的主目錄。 |
NSMetadataQueryUserHomeScope |
操作系統(tǒng) | 搜索用戶(hù)的主目錄。 |
注意:在 OS X 上,雖然文件系統(tǒng)元數(shù)據(jù)在所有卷上可用,但其他元數(shù)據(jù)屬性不可用。Spotlight 不會(huì)為 CD、DVD、磁盤(pán)映像和系統(tǒng)目錄編制索引。
3.啟動(dòng)搜索
創(chuàng)建并配置查詢(xún)對(duì)象后,您可以通過(guò)調(diào)用startQuery函數(shù)執(zhí)行查詢(xún)。
運(yùn)行時(shí),查詢(xún)通常有兩個(gè)階段:初始結(jié)果收集階段和實(shí)時(shí)更新階段。
在初始結(jié)果收集階段,會(huì)在現(xiàn)有 Spotlight系統(tǒng)存儲(chǔ)中搜索與搜索表達(dá)式匹配的文件。當(dāng)結(jié)果使用NSMetadataQueryDidUpdateNotification. 在單個(gè)查詢(xún)中,這對(duì)于指示搜索進(jìn)度的狀態(tài)很有用,而在實(shí)時(shí)搜索中它變得更加重要。
NSMetadataQueryDidFinishGatheringNotification當(dāng)初始結(jié)果收集階段完成時(shí),查詢(xún)向應(yīng)用程序發(fā)送通知。
4.訪(fǎng)問(wèn)返回結(jié)果
在您的應(yīng)用程序與返回的結(jié)果交互之前,它必須首先停止查詢(xún)。
您可以在搜索的初始收集階段或?qū)崟r(shí)更新階段禁用更新。應(yīng)用程序通過(guò)調(diào)用NSMetadataQuery實(shí)例方法確定已返回的結(jié)果數(shù)resultCount。然后,應(yīng)用程序使用resultAtIndex:方法請(qǐng)求所需索引處的結(jié)果項(xiàng)。
結(jié)果項(xiàng)作為類(lèi)型的對(duì)象實(shí)例返回NSMetadataItem。每個(gè)對(duì)象都封裝了文件的元數(shù)據(jù)屬性。
然后,您的應(yīng)用程序通過(guò)向每個(gè)實(shí)例valueForAttribute:傳遞帶有所需元數(shù)據(jù)屬性名稱(chēng)的消息,從這些項(xiàng)目中檢索元數(shù)據(jù)屬性。
5.NSMetadataQuery代碼示例
// 初始化搜索方法
- (void)initiateSearch {
//1.初始化創(chuàng)建搜索
self.metadataSearch = [[NSMetadataQuery alloc] init];
//注冊(cè)通知監(jiān)聽(tīng)
[self addQueryObserver];
//2.設(shè)置查詢(xún)
//2.1 設(shè)置查詢(xún)語(yǔ)句
NSPredicate *searchPredicate =[NSPredicate predicateWithFormat:@"kMDItemDisplayName == 'fileName'"];
[metadataSearch setPredicate:searchPredicate];
//2.2 設(shè)置排序順序,以便對(duì)查詢(xún)結(jié)果進(jìn)行排序
NSSortDescriptor *sortKeys = [[[NSSortDescriptor alloc] initWithKey:(id)kMDItemDisplayName
升序:是] 自動(dòng)釋放];
[metadataSearch setSortDescriptors:[NSArray arrayWithObject:sortKeys]];
//2.3 設(shè)置搜索范圍
NSArray *searchScopes = [NSArray arrayWithObjects:NSMetadataQueryUserHomeScope,
NSMetadataQueryUbiquitousDocumentsScope,nil];
[metadataSearch setSearchScopes:searchScopes];
//3.開(kāi)啟異步查詢(xún)
[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];
}
// 初始查詢(xún)收集完成時(shí)調(diào)用的方法
- (void)initalGatherComplete:sender;
{
// 停止查詢(xún),單遍完成。
[_metaDataSearch stopQuery];
// 處理內(nèi)容。在這種情況下,應(yīng)用程序只需迭代內(nèi)容,打印顯示名稱(chēng)鍵
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);
}
// 刪除通知以在我們自己之后清理。
// 同時(shí)釋放metadataQuery。
// 當(dāng) Query 被移除時(shí),查詢(xún)結(jié)果也會(huì)丟失。
[self removeQueryObserver];
self.metaDataSearch = nil;
}