0

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)")
        }
    }
}
glocore
  • 1,727
  • 1
  • 14
  • 24

1 Answers1

2

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.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Hi @matt, thanks for your answer! I tried using `NSMetadataItemDisplayNameKey` instead, and while the query is certainly faster than before, it still takes at least 2 seconds to complete. Is it possible to receive results at speeds comparable with macOS's Spotlight search or with apps like Alfred? I'm trying to implement a similar file search feature in my app. – glocore Aug 02 '21 at 14:39
  • I'd like to emphasise that it only takes 2 seconds when the query string is 1-2 characters long. Once the number of characters increases to 5 or more, the search query is narrowed down and the results are returned almost instantly. – glocore Aug 02 '21 at 14:43
  • 1
    The results should pour in one at a time and you should be able to respond as they do. You don't have to wait for anything to "complete". Don't your notification methods start printing immediately? – matt Aug 02 '21 at 15:11
  • Yup, it most certainly does. That should work for now; thanks so much for your help! – glocore Aug 02 '21 at 16:12