objective-ccocoanspredicatespotlightnsmetadataquery

NSPredicate(s) for NSMetadataQuery to match kMDItemAccessedDates (array) containing a target date?


UPDATE: I found an answer though there may still be a better way - see the answer added below.


a spotlight importer imports metadata that is an array of dates in which an item was accessed. Looks like this:

kMDItemAccessedDates               = (
    "2012-06-18 07:00:00 +0000",
    "2013-01-10 08:00:00 +0000",
    "2013-01-15 08:00:00 +0000"
)

I'd like to query for any of these files that were accessed on a particular date the user enters but I'm not finding the right syntax to do this. Wondering if it's possible to do...

The first issue is that the user enters a date which represents a day, not a day and time.

Seemed like I need to search for any date that falls on that day, so logically: ADATE >= user_day_start && ADATE <= user_day_end

Now there is an array of dates in the metadata so I really want it to match if ANY of those dates fall within the user_date day.

Be nice if this worked:

NSDate * date = [NSDate dateWithNaturalLanguageString: inWhenString];
NSDate * endDate = [NSDate dateWithTimeInterval: 86400 sinceDate: date];

predicate = [NSPredicate predicateWithFormat: 
              @"kMDItemAccessedDates between {%@, %@}", date, endDate];

but that throws an exception:

Exception detected while handling key input.
2013-01-16 03:12:57.517 Chatty[96974:403] Unknown type of NSComparisonPredicate 
  given to NSMetadataQuery 
  (kMDItemAccessedDates BETWEEN {CAST(379540800.000000, "NSDate"),
  CAST(379627200.000000, "NSDate")}) 

and seems like it might only work for a single date rather than an array. Adding the "ANY" to the format string like so: "ANY kMDItemAccessedDates between {%@, %@}" ... didn't help, still throwing an exception.

Tried this and it's not throwing an exception, but isn't really working either:

NSPredicate *p1 = [NSPredicate predicateWithFormat: @"kMDItemAccessedDates >= %@", date ];
NSPredicate *p2 = [NSPredicate predicateWithFormat: @"kMDItemAccessedDates <= %@", endDate ];
predicate = [NSCompoundPredicate andPredicateWithSubpredicates: [NSArray arrayWithObjects: p1, p2, nil]];

Wondering if anyone has any ideas on how to make this work... or knows that it's impossible (like have to get them all and then do the filter in memory or something; really hope that's not the case - it could be a long list!).


Solution

  • So another few hours digging into internet search results and trying things and I sort of have this working.

    NSDate * userDate = [NSDate dateWithNaturalLanguageString: inWhenString];
    NSDateComponents * userDateComponents = [[NSCalendar currentCalendar] components: NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate: userDate];
    NSDate * dayDate = [[NSCalendar currentCalendar] dateFromComponents: userDateComponents];
    dNSLog(@"date %@", dayDate );
    NSString * formatStr = [NSDateFormatter dateFormatFromTemplate: @"yyyy-MM-dd" options: 0 locale: nil];
    NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat: formatStr];
    
    NSString * dayStr = [formatter stringFromDate: dayDate];
    dayStr = [dayStr stringByAppendingString: @"*"];
    dNSLog(@"dayString: %@", dayStr );
    NSPredicate *p1 = [NSPredicate predicateWithFormat: @"%K >= CAST(%f, \"NSDate\")", @"kMDItemAccessedDates", [dayDate timeIntervalSinceReferenceDate] ];
    NSPredicate *p2 = [NSPredicate predicateWithFormat: @"%K <= CAST(%f, \"NSDate\")", @"kMDItemAccessedDates", [dayDate timeIntervalSinceReferenceDate] + 86400.0f ];
    predicate = [NSCompoundPredicate andPredicateWithSubpredicates: @[p1, p2]];
    

    There may be other issues with this whole approach (based on how the app updates this array of dates) that may require me to do something else entirely, but this seems to answer the original question of how to fetch only items that were accessed on a particular day.

    The key was the CAST(%f, \"NSDate\") I believe.

    There might still be a better way, but I thought I'd put this up here in case someone else is digging around for any clues.