8

I have a project where I need to do a fetch request that gets the most recent 'updated' date of a Core Data entity. When I actually examine the results returned by my query however I am seeing some rather strange behaviour. In addition to the 'correct' result containing the date the results also include an empty dictionary item. This happens every time, regardless of what the underlying data looks like. Even stranger, if I turn on sql logging in xcode and execute the logged query against the sqllite db it produces the correct result with no extra entries. I'm not quite sure what I'm doing wrong here, any help would be appreciated.

The function that builds and executes the query:

func queryForContactDate(context:NSManagedObjectContext) -> AnyObject?
{

    var expressionDescriptions = [AnyObject]();

    let expressionDescription = NSExpressionDescription()

    // Name the column
    expressionDescription.name = "maxUpdated"
    // Use an expression to specify what aggregate action we want to take and
    // on which column. In this case max on the update_at column
    expressionDescription.expression = NSExpression(format: "@max.updated_at")
    // Specify the return type we expect
    expressionDescription.expressionResultType = .DateAttributeType
    // Append the description to our array
    expressionDescriptions.append(expressionDescription)

    // Build out our fetch request the usual way
    let request = NSFetchRequest(entityName: Contact.entityName())

    // Specify we want dictionaries to be returned
    request.resultType = .DictionaryResultType

    // Hand off our expression descriptions to the propertiesToFetch field.
    request.propertiesToFetch = expressionDescriptions

    // Our result is going to be an array of dictionaries.
    var results:[[String:AnyObject]]?

    // Perform the fetch. This is using Swfit 2, so we need a do/try/catch
    do {
        results = try context.executeFetchRequest(request) as? [[String:AnyObject]]
    } catch _ {
        // If it fails, ensure the array is nil
        results = nil
    }

    return results![0];
}

If I put a breakpoint at the end and print out the results it produces:

Printing description of results:
▿ Optional([[:], ["maxUpdated": 2015-12-30 20:05:31 +0000]])
  ▿ Some : 2 elements
    - [0] : 0 elements
    ▿ [1] : 1 elements
      ▿ [0] : 2 elements
        - .0 : "maxUpdated"
        - .1 : 2015-12-30 20:05:31 +0000
pbuchheit
  • 1,371
  • 1
  • 20
  • 47
  • Can you print out with context.executeFetchRequest(request) is without a cast? It's possible that you're casting something that's not actually [[String: AnyObject]]. Especially in the corners of the API that are objc being ported to Swift there are some strange things that go on with dictionaries/arrays – tbondwilkinson Jan 18 '16 at 22:40
  • I suspect you are on the right track but the print was not especially enlightening. Here is what I got: [{ }, { maxUpdated = "2015-12-30 20:05:31 +0000"; }] – pbuchheit Jan 19 '16 at 13:27
  • Then I would put a breakpoint in the fetch method and trace it back and see where the request originates/when that empty dictionary gets there – tbondwilkinson Jan 19 '16 at 15:31
  • I would love to, but unfortunately I can't debug any deeper into that call to context.executeFetchRequest without access to the source code for NSManagedObjectContext. – pbuchheit Jan 20 '16 at 13:45

1 Answers1

1

The traditional Core Data way to get a max or min item is to query with a fetch limit of 1 and sort on that key. As in this Objective-C code:

+ (NSFetchRequest *) requestForStatusWithMaxID {

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName: kAMStatusEntity];

    NSSortDescriptor *sd = [NSSortDescriptor sortDescriptorWithKey: kAMTwID ascending:NO];

    request.sortDescriptors = @[sd];
    request.fetchLimit = 1;

    return request;

} // +requestForStatusWithMaxID

It would be quite simple to modify the above for your updated_at property.

adonoho
  • 4,339
  • 1
  • 18
  • 22
  • Yes, it is possible to do the fetch that way, but the performance is going to suffer as the size of the underlying table gets larger. In addition, I'm seeing this same behaviour if I try other aggregate functions like SUM. Simply sorting and taking the first result will not help with those. – pbuchheit Jan 06 '16 at 16:20
  • Any query without an index, including yours listed above, has scaling issues as the table grows. How big does your table get? 100, 1,000, 10,000, 1M rows? If it is large, then you need an index -- for both query styles. – adonoho Jan 06 '16 at 21:20