首页 > 解决方案 > NSPredicate 获取给定值下属性的最大值

问题描述

我有一个Update具有两个属性的实体:dateamount. 假设我在 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() 不知道其他约束(请参阅此处的讨论)

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。但这在我的情况下不起作用,因为可能有多个对象具有相同的日期,如果它们的日期符合要求,我想获得所有这些对象。

标签: ioscore-datasubquerynspredicatensexpression

解决方案


可以将两个提取合并为一个。与其“运行”第一次提取,不如将它(作为 a 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.


推荐阅读