Search code examples
swiftcore-datansexpression

CoreData Fetch: Getting the objects with the maximum of one atttribute and having another attribute in common


I have a CoreData ManagedObject type Event with the properties name:Sting and date:Date.

I want to fetch all EventObject, with the name containing a filter. If more than one object with the same name matches the filter, only the object with the latest date should be returned.

Just to clarify what I want. In a table based approach I would query in SQL like:

SELECT name, max(date) FROM Event
WHERE name contains 'filter'
GROUP BY name  

In CoreData I tried like this, but got:
Could not cast value of type 'NSKnownKeysDictionary1' to 'xx12.Event'.

let fetchRequest : NSFetchRequest<Event> = Event.fetchRequest(    fetchRequest.predicate = NSPredicate (format: "name contains %@", filter)
fetchRequest.sortDescriptors = [NSSortDescriptor (key: "name", ascending: true)]
   
// We want Event not Dictionary back 
// fetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType
      
let keypathExpression = NSExpression(forKeyPath: "date")
let maxExpression = NSExpression(forFunction: "max:", arguments: [keypathExpression])

let key = "maxDate"   
let expressionDescription = NSExpressionDescription()
expressionDescription.name = key
expressionDescription.expression = maxExpression
expressionDescription.expressionResultType = .dateAttributeType

fetchRequest.propertiesToFetch = [expressionDescription]
      
let resultEvents = try moc.fetch(fetchRequest)

Example:
if I have these objects

meeting1, 1.1.2020
meeting1, 1.4.2020
meeting2, 1.1.2020
meeting2, 1.4.2020
party, 2.3.2020

and the filter: meeting
I want to get the objects:

meeting1, 1.4.2020
meeting2, 1.4.2020

Solution

  • I would use a basic fetch request and then do the grouping part in swift code instead of working with NSExpression

    let fetchRequest : NSFetchRequest<Event> = Event.fetchRequest()
    fetchRequest.predicate = NSPredicate(format: "name contains %@", filter)
    
    let result = try moc.fetch(fetchRequest)
    
    return Dictionary(grouping: result, by: \.name).values
        .compactMap { $0.max {$0.date < $1.date } }