NSPredicate для получения максимального значения свойства при заданном значении - PullRequest
0 голосов
/ 23 января 2020

У меня есть сущность Update, которая имеет два атрибута: date и amount. Предположим, у меня есть эти объекты в Базовых данных:

|       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. Но, к сожалению, кажется, что SUBQUERY в Core Data предполагается использовать с двумя таблицами (для этого требуется явный аргумент коллекции).

Я прочитал документ NSExpression, но, насколько я могу судить, определить невозможно NSExpression, чтобы сделать это либо (приведенная выше строка формата NSPredicate, которую я пробовал, на самом деле является NSExpression в строковом формате, поэтому они используют один и тот же max ()).

Означает ли это, что мне нужно выбрать несколько записей с NSPredicate(format: "date < %@", given_date as CVarArg) сначала, а затем запустить второй предикат, чтобы получить последний? Но разве это не неэффективно, потому что он выбирает несколько записей, хотя мне нужна только одна? Спасибо за любые предложения.

Примечание : я рассмотрел установку fetchLimit на 1. Но это не работает в моих случаях, потому что может быть несколько объектов с одной датой, и я хочу чтобы получить их все, если их даты соответствуют требованию.

Ответы [ 2 ]

1 голос
/ 23 января 2020

Можно объединить две выборки в одну. Вместо того, чтобы «запускать» первую выборку, передайте ее (как 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 включает время, поэтому обновления происходят в тот же день может иметь другой date.

0 голосов
/ 23 января 2020

Я работаю примерно так:

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) для записи с максимальной датой.

...