NSPredicate 获取给定值下属性的最大值

NSPredicate to get the max value of a property under a given value

我有一个实体 Update,它有两个属性:dateamount。假设我在核心数据中有这些对象:

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

我是这样计算的:

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层收集多个entry,在Core Data层进行聚合操作。我不确定这是否比通常的方法更好,后者只发出一个获取请求,但将所有条目传递给应用层并让应用搜索它们(例如,使用 NSPredicate)以查找具有最大日期的条目。

可以将两个提取合并为一个。不是 "running" 第一次提取,而是将它(作为 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