NSFetchedResultsController 中的自定义排序

Posted

技术标签:

【中文标题】NSFetchedResultsController 中的自定义排序【英文标题】:Customized sort in NSFetchedResultsController 【发布时间】:2011-10-16 02:45:11 【问题描述】:

花了几个小时后,我从下面的文章中发现无法在 SQLite 支持的 NSFetchedResultsController 中进行自定义排序。

NSFetchedResultsController custom sort not getting called

但是,我找不到我的问题的实际解决方案。

这就是我想要做的。

背景: 我在 CoreData 中有一个英语词典数据库(只是一个简单的单词列表 - 非常大)。单词在 UITableView 中使用 NSFetchedResultsController 显示。

UITableView 有一个关联的搜索栏。当用户在搜索栏中输入字符串时,UITableView 会显示过滤后的单词列表。

为什么需要自定义排序: 当用户输入一个字符串时,假设它是bre,我将其更改为正则表达式b.*r.*e.*,并将其用作NSPredicate,然后执行performFetch。这样所有像“bare”和“break”这样的词都会显示在表格视图中。

默认情况下,单词按字母顺序显示。因此,bare 将出现在 break 之前。

我希望break 在搜索列表中排在bare 之前,因为break 的前三个字符与用户输入的内容完全匹配。

可能的想法:

    将 NSFetchedResultsController 的结果复制到 NSArray 并进行自定义排序。 我不确定 NSArray 对像英语词典这样的大型数组的工作速度有多快。 多次尝试 performFetch。例如上面,按顺序对bre.*br.+e.*b.+r.+e.* 尝试 performFetch 并将它们组合起来。

这两个想法看起来都不是很整洁。

如果您能针对此类问题提出任何已知的简洁且典型的解决方案,我将不胜感激。

【问题讨论】:

【参考方案1】:

您可以考虑的另一种方法是使用瞬态属性来修饰结果以指示匹配类型,然后使用该瞬态属性作为第一个排序描述符。

这需要遍历所有结果,再次比较字符串,并设置属性。如果您期望得到很长的结果集,那么使用数组可能同样简单。

如果您需要有效地处理大型结果集,我建议使用两个排序描述符,一个只返回完全匹配,另一个只返回非精确匹配。然后显示第一个结果,然后显示第二个结果。使用应该可以完成的复合谓词。

【讨论】:

【参考方案2】:

哇,这个问题很烦人。

我的设置如下。我有一个搜索,它接受输入并通过匹配用户名或全名来查找用户。服务器已经返回了适当的顺序,但是由于我使用的是 NSFetchedResultsController 我需要一些排序描述符这是我所做的,似乎运行良好。我向我的用户实体添加了一个名为matchScore 的新属性,并且在从服务器进行CRUD 期间,我得到了MIN() Levenshtein 查询 用户名和查询 全名

之间的距离分数

我现在有一个排序描述符,它将根据来自服务器的最接近的匹配结果与用户的查询进行排序。代码是 ruby​​motion,但应该仍然可读。

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

使用新的排序描述符,我现在可以获取“不太理想”的结果,并且仍然首先保留最接近的匹配项。我现在可以避免 @Jaemin 的一些潜在解决方案,这些解决方案涉及复杂的结果聚合来解决自定义排序不起作用的问题。

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

匹配分数现在从服务器生成在 CRUD 上。

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
)

这是我用来获取 Levenshtein 距离的 NSString 类别。 https://gist.github.com/iloveitaly/1515464

【讨论】:

以上是关于NSFetchedResultsController 中的自定义排序的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 应用程序中调用 performFetch 后,是不是需要手动更新表视图?