NSPredicate 获取给定值下属性的最大值
Posted
技术标签:
【中文标题】NSPredicate 获取给定值下属性的最大值【英文标题】:NSPredicate to get the max value of a property under a given value 【发布时间】:2020-01-23 09:22:03 【问题描述】:我有一个实体Update
,它有两个属性:date
和amount
。假设我在 Core Data 中有这些对象:
| Date | Amount |
|------------+--------|
| 2020-01-01 | 100 |
| 2020-01-05 | 200 |
| 2020-01-10 | 300 |
| 2020-01-15 | 400 |
我的目的是获取给定日期之前具有最新日期的对象。例如,给定日期 2020-01-12,结果应该是日期为 2020-01-10 的对象。我想知道是否可以使用单个 NSPredicate 来做到这一点?
我尝试了以下方法,但它不起作用,因为 max() 不知道其他约束(请参阅讨论 here)
request.predicate = NSPredicate(format: "date < %@ AND date == max(date)", given_date as CVarArg)
我还考虑了 SUBQUERY,因为这是在 SQL 中执行此操作的方法。但不幸的是,Core Data 中的 SUBQUERY 似乎应该与两个表一起使用(它需要一个显式的集合参数)。
我已经阅读了 NSExpression 文档,但据我所知,也无法定义 NSExpression 来执行此操作(我尝试的上述 NSPredicate 格式字符串实际上是字符串格式的 NSExpression,因此它们使用相同的 max() )。
这是否意味着我必须首先使用NSPredicate(format: "date < %@", given_date as CVarArg)
获取多个条目,然后运行第二个谓词来获取最新的?但这不是效率低下吗,因为它获取多个条目,尽管我只需要一个?
我错过了什么吗?感谢您的任何建议。
注意:我考虑将 fetchLimit
设置为 1。但这在我的情况下不起作用,因为可能有多个对象具有相同的日期,如果他们的日期符合要求。
【问题讨论】:
【参考方案1】:可以将两个提取合并为一个。与其“运行”第一次提取,不如将它(作为NSFetchRequestExpression
)传递给主提取请求:
func fetchUpdates(_ date: Date)
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Update")
request.predicate = NSPredicate(format: "date <= %@", date as CVarArg)
let expressionDescription = NSExpressionDescription()
expressionDescription.expression = NSExpression(format: "@max.date")
expressionDescription.name = "maxdate"
expressionDescription.expressionResultType = .dateAttributeType
request.propertiesToFetch = [expressionDescription]
request.resultType = NSFetchRequestResultType.dictionaryResultType
// Identical up to here, then:
let contextExpression = NSExpression(forConstantValue: self.managedObjectContext)
let fetchExpression = NSExpression(forConstantValue: request)
let fre = NSFetchRequestExpression.expression(forFetch: fetchExpression, context: contextExpression, countOnly: false)
let mainFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Update")
mainFetch.predicate = NSPredicate(format: "date == %@", fre)
let results = try! self.managedObjectContext!.fetch(mainFetch)
....
请注意,Date 属性类型包括时间,因此在同一 DAY 发生的更新可能具有不同的 date
。
【讨论】:
谢谢!你拯救了我的一天。所以我在谷歌上搜索它但发现很少讨论(我了解它的用途,我只是好奇人们使用它的频率)。好像不经常使用?但我认为我的问题是一个典型的场景……也许这表明大多数人只是拉取所有条目并在 App 中处理它(也许是因为在大多数情况下性能差异并不明显)。 如果您不介意,我可以再问一个问题吗? (这有点笼统,所以我不确定提交单独的问题是否是个好主意)。在上面的示例中,我可以在我的应用程序代码中使用获取请求或关系。两者都易于实现。您认为哪种方法在性能方面更好?或者这是一个个案决定?我有点困惑,因为我在一些地方(例如,objc.io 的一本书)读到应该尽可能避免获取请求,但另一方面,我的印象是在大多数 SO 讨论中人们似乎使用 fetch 请求。 恐怕预测CoreData性能(获取v代码)过于依赖个人情况,所以真的是尝试一下。 Apple 对 CoreData 进行了很好的优化,因此在许多情况下,运行 fetch 的开销有限。尽管如此,如果可以的话,值得获取一次并将结果保存在内存中。例如,如果您正在填充 tableView,则在加载视图时获取一次(或使用获取的结果控制器) - 当然不要在每次调用cellForRowAt
时获取(我以前见过这样做)!跨度>
【参考方案2】:
我的工作是这样的:
func fetchUpdates(_ date: Date)
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Update")
request.predicate = NSPredicate(format: "date <= %@", date as CVarArg)
let expressionDescription = NSExpressionDescription()
expressionDescription.expression = NSExpression(format: "@max.date")
expressionDescription.name = "maxdate"
expressionDescription.expressionResultType = .dateAttributeType
request.propertiesToFetch = [expressionDescription]
request.resultType = NSFetchRequestResultType.dictionaryResultType
// Run the request, this will return the the date
...
// Then construct a second fetch request to get the object(s) with the date
...
该函数发出两个获取请求。如果我理解正确,第一个请求仍然会在 SQLite 层收集多个条目并在核心数据层执行聚合操作。我不确定这是否比仅发出一个获取请求但将所有条目传递到应用层并让应用搜索它们(例如,使用 NSPredicate)以查找具有最大日期的条目的通常方法更好。
【讨论】:
以上是关于NSPredicate 获取给定值下属性的最大值的主要内容,如果未能解决你的问题,请参考以下文章