Customized sort in NSFetchedResultsController

穿精又带淫゛_ 提交于 2019-12-04 09:37:03

Another approach you could consider is using a transient property to decorate the results to indicate the type of match, and then using that transient property as the first sort descriptor.

This requires looping through all the results, comparing the strings again, and setting the attributes. If you expect a long set of results, using an array might be just as easy.

If you need to handle large result sets efficiently, I'd suggest using two sort descriptors, one which returns only exact matches, and another which returns only non-exact matches. Then display the results from the first followed by the results from the second. With a compound predicate that should be possible to accomplish.

Wow, this problem was annoying.

My setup is as follows. I have a search that takes input and looks for users by matching username or full name. The server was already returning the appropriate order, but since i'm using NSFetchedResultsController I need some sort descriptor Here is what I did that seems to be working well. I added a new property to my user entity called matchScore and during CRUD from the server I get the MIN() Levenshtein Distance score between the query <-> username and query <-> full name

I now have a sort descriptor that will order by the closest matching results from the server with the user's query. The code is rubymotion, but should still be readable.

sortDescriptors = []
sortDescriptors << NSSortDescriptor.sortDescriptorWithKey("matchScore", ascending:true)

With the new sort descriptor I can now fetch "less than ideal" results and still keep closest matches first. I can now avoid some of @Jaemin's potential solutions which involved complicated result aggregation to get around custom sorts not working.

request.predicate = NSPredicate.predicateWithFormat("(username MATCHES[cd] %@) OR (username BEGINSWITH[cd] %@) OR (name CONTAINS[cd] %@)", argumentArray:[searchString, searchString, searchString])

The match score is now generated on CRUD from the server.

usersContext.performBlock(lambda{
  restUsers.each do |restUser|
    user = User.entityWithRestModel(restUser, usersContext)
    user.matchScore = [query.compareWithWord(user.username, matchGain:10, missingCost:1), query.compareWithWord(user.name, matchGain:10, missingCost:1].min
    puts "u:#{user.username} <-> q:#{query}   score:#{user.matchScore}"
  end
})

Here is the NSString category that I am using to get the Levenshtein distance. https://gist.github.com/iloveitaly/1515464

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!