Как правильно настроить NSPredicate для отношения "многие" при использовании Core Data?
У меня есть модель Core Data, в которой объект Task включает в себя необязательное отношение to-many ExcludedDays к объекту ExcludedDay. Одним из свойств ExcludedDay является день, который является объектом NSDate. Объект ExcludedDay имеет обратное обязательное отношение к одному объекту Task.
Чтобы получить задания в течение указанного дня, мне нужно убедиться, что указанный день не отображается как свойство day любого объекта ExludedDay.
Я начал пробовать
NSPredicate *dayIsNotExcludedPredicate = [NSPredicate predicateWithFormat: @"ALL excludedDays.day != %@", today];
Однако, несмотря на то, что говорится в документации, ALL не работает, и приложение выдает исключение: Завершение приложения из-за неотображенного исключения 'NSInvalidArgumentException, reason:' Неподдерживаемый предикат.
После публикации того же вопроса на этом форуме, я смог разработать следующий предикат с помощью разных людей:
NSPredicate * dayIsNotExcludedPredicate = [NSPredicate predicateWithFormat: @"[email protected] == 0 || ([email protected] > 0 && NONE excludedDays.day == %@))", today];
Пока это работало, я только что обнаружил, что это работает только тогда, когда объект ExcludedDay содержит ТОЛЬКО один день. Как только объект ExcludedDay содержит более одного дня для одной задачи, этот предикат перестает работать. В результате задача выбирается в течение дня, даже если день появляется как день в объекте ExcludedDay, что, конечно, неверно. Проблема не связана с тем, что день свойства является объектом NSDate: заменяя день соответствующей NSString или эквивалентно целому числу, я по-прежнему сталкиваюсь с той же проблемой и неправильным поведением.
Каков правильный способ реализации предиката в этом случае? Может быть, это ошибка, связанная с любым агрегирующим оператором при использовании основных данных?
Заранее спасибо, теперь это сводит меня с ума.
Ответы
Ответ 1
Оказывается, это еще одна проблема с отсутствующей и/или несогласованной документацией.
Правильный предикат в этом случае следующий:
[NSPredicate predicateWithFormat:@"([email protected] == 0) OR (0 == SUBQUERY(excludedOccurrences, $sub, $sub.day == %@)[email protected])", today]
В предикате подзапрос используется для проверки того, равна ли количество связанных исключенных экземпляров с датой, соответствующей вашей дате тестирования, равной нулю. Однако документация вводит в заблуждение. Вот что говорится в руководстве по программированию Predicate относительно использования предикатов в сочетании со многими отношениями.
Использование предикатов с отношениями
Если вы используете отношение "ко многим", построение предиката несколько отличается. Если вы хотите получить департаменты, в которых, по крайней мере, один из сотрудников имеет имя "Matthew", например, вы используете оператор ANY, как показано в следующем примере:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"ANY employees.firstName like 'Matthew'"];
Если вы хотите найти отделы, в которых всем сотрудникам выплачивается более определенной суммы, вы используете оператор ALL, как показано в следующем примере:
float salary = ... ;
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"ALL employees.salary > %f", salary];
Ответ 2
Очень любопытно, как решить эту проблему. Я настраиваю небольшой проект и создаю контекст, который вы используете.
NSDate *today = [NSDate date];
NSMutableArray *objects = [NSMutableArray array];
{
NSArray *day = [NSArray arrayWithObjects:today, [today dateByAddingTimeInterval:20.0f], nil];
NSDictionary *excludedDay = [NSDictionary dictionaryWithObject:day forKey:@"day"];
NSDictionary *object = [NSDictionary dictionaryWithObject:excludedDay forKey:@"excludedDay"];
[objects addObject:object];
}
{
NSArray *day = [NSArray arrayWithObjects:[today dateByAddingTimeInterval:20.0f], nil];
NSDictionary *excludedDay = [NSDictionary dictionaryWithObject:day forKey:@"day"];
NSDictionary *object = [NSDictionary dictionaryWithObject:excludedDay forKey:@"excludedDay"];
[objects addObject:object];
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NONE excludedDay.day == %@", today];
NSArray *filtered = [objects filteredArrayUsingPredicate:predicate];
NSLog(@"%@", filtered);
Это дает объект, когда:
- Дневной массив пуст
- В массиве "День" нет "сегодняшней" даты
Это не дает объект, если:
- В дневном массиве содержится "сегодня",
- Неважно, сколько объектов в массиве day