swiftmacoscocoaspotlightnsmetadataquery

How to limit the number of results returned from NSMetadataQuery?


Is there a way to limit the maximum number of file search results that are returned from an NSMetadataQuery query on macOS? Currently, if I enter 1-2 characters as a query string, it takes over 5 seconds to receive the initial set of results, and the result count is about 100k-300k, while I only need the top 50 results. The query time reduces down to a few milliseconds if my query string is 5+ characters long.

I tried the same with MDQuery and was able to limit the max. results to 50 using MDQuerySetMaxCount, so I was wondering if there was something similar in NSMetadataQuery? I've seen Alfred and similar apps return the top 20-40 results almost instantly with every keystroke.

Here's what I have so far:

class MDQSearch {
    var metadataQuery = NSMetadataQuery()
    
    init() {
        registerNotifications()
        metadataQuery.searchScopes = [NSString("~/Documents").expandingTildeInPath]
    }
    
    func updateQuery(to queryString: String) {
        guard queryString.count > 0 else { return }
        
        metadataQuery.predicate = NSPredicate(format: "%K CONTAINS[cd] %@", argumentArray: [NSMetadataItemFSNameKey, queryString])
        metadataQuery.start()
    }
    
    func registerNotifications() {
        NotificationCenter.default.addObserver(self, selector: #selector(onQueryDidFinishGathering), name: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: metadataQuery)
        NotificationCenter.default.addObserver(self, selector: #selector(onQueryGatheringProgress), name: NSNotification.Name.NSMetadataQueryGatheringProgress, object: metadataQuery)
        NotificationCenter.default.addObserver(self, selector: #selector(onQueryDidUpdate), name: NSNotification.Name.NSMetadataQueryDidUpdate, object: metadataQuery)
    }
    
    @objc func onQueryDidUpdate() {
        print("QueryDidUpdate")
    }
    
    @objc func onQueryDidFinishGathering() {
        print("QueryDidFinishGathering")
        metadataQuery.stop()
        
        print("result count: \(metadataQuery.resultCount)")
    }
    
    @objc func onQueryGatheringProgress() {
        print("QueryGatheringProgress")
        
        if(metadataQuery.resultCount >= 50) {
            metadataQuery.stop()
            
            print("result count: \(metadataQuery.resultCount)")
        }
    }
}

Solution

  • I would suggest that the problem is your use of NSMetadataItemFSNameKey. That's slow because the file system must be consulted. Use the display name key instead; that's information indexed by Spotlight, so it's fast.