NSFetchRequest с NSPredicate иногда выдает NSInvalidArgumentException
Этот сбивает меня с толку:
У меня есть основной набор данных, который я ищу / фильтрую, используя следующий код:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:@"Lead" inManagedObjectContext:moc]];
NSString *predicateString = [NSString stringWithFormat:@"("
"(isLocalVersion == %@) AND (LeadStatusID =='Active')"
") AND ("
"(LeadID contains[cd] '%@')"
"OR (AccountNumber contains[cd] '%@')"
"OR (ANY contactItems.FirstName contains[cd] '%@')"
"OR (ANY contactItems.LastName contains[cd] '%@')"
"OR (ANY addressItems.Address1 contains[cd] '%@')"
"OR (ANY addressItems.City contains[cd] '%@')"
")", [NSNumber numberWithBool:YES], sTerm, sTerm, sTerm, sTerm, sTerm, sTerm];
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *leads = nil;
@try {
leads = [moc executeFetchRequest:fetchRequest error:&error];
}
@catch (NSException *exception) {
LogSevere(@"Error Executing Fetch Request: %@", exception);
leads = [NSArray array];
}
@finally {
[fetchRequest release];
}
sTerm это просто NSString
,
Это работает в 95% случаев, однако время от времени ловит NSInvalidArgumentException: Can't use in/contains operator with collection 10076173 (not a collection).
Форматы Predicate String должны быть такими:
((isLocalVersion == 1) AND (LeadStatusID == 'Active')) AND ((LeadID содержит [cd] 'i') ИЛИ (AccountNumber содержит [cd] 'i') ИЛИ (ЛЮБОЙ contactItems.FirstName содержит [cd] 'i') ИЛИ (ЛЮБОЙ contactItems.LastName содержит [cd] 'i') ИЛИ (ЛЮБОЙ addressItems.Address1 содержит [cd] 'i') ИЛИ (ЛЮБОЙ addressItems.City содержит [cd] 'i'))
Мой улов позволяет мне не падать и просто возвращать 0 результатов, но это не оптимально. Кто-нибудь когда-нибудь видел это раньше?
Единственная подсказка - это то, что это происходит после того, как я изменяю (и сохраняю) запись в основных данных (опять же только иногда).
5 ответов
Да, вы не можете иметь выражения, которые оценивают коллекции как часть предиката fetchRequest. Это в документации:
Агрегированные выражения не поддерживаются Core Data.
Базовое хранилище SQL данных поддерживает только одну операцию "многие" на запрос; поэтому в любом предикате, отправленном в хранилище SQL, может быть только один оператор (и один экземпляр этого оператора) из
ALL
,ANY
, а такжеIN
,
Что может случиться так:
- если одно из ранних состояний оценивается как
TRUE
остальные условия не оцениваются, поэтому сбоя не происходит - если вы доберетесь до
OR (ANY contactItems.FirstName contains[cd] '%@')
И егоTRUE
вы получите ваше первое использованиеANY
сбоев не происходит - если вы доберетесь до
OR (ANY contactItems.LastName contains[cd] '%@')
, вы получите ваше второе использованиеANY
, происходит сбой
Вам нужно выполнить ЛЮБЫЕ отдельно и объединить результаты, возможно, используя NSCompoundPredicate;
Является ли LeadID int свойство случайно? Я просто столкнулся с этой же проблемой, создавая предикат, в котором я проверяю, содержит ли одно или несколько свойств поисковый термин. Это работало большую часть времени, но время от времени это приводило к ошибке, как и у вас.
Проблема заключалась в том, что все свойства, которые я проверял, были строками, кроме одного. Когда я удалил это свойство из предиката, все работало нормально.
Я подозреваю, что 10076173 является значением int LeadID для некоторого объекта в вашей базе данных. Похоже, что Core Data обычно преобразует int в строку, как вы ожидаете, но иногда это не так, и именно тогда происходит ошибка. Оператор CONTAINS не имеет смысла для целого числа, и он вылетает.
В качестве еще одного доказательства я попытался изменить предикат на использование LIKE вместо CONTAINS, и у меня возникла связанная с этим ошибка, которая, в конце концов, подтолкнула меня к тому, что происходит. Предикат был
itemNumber LIKE "*test*" ИЛИ модель LIKE "* test *" ИЛИ productDescription LIKE "*test*" [..snip..]
и исключение было
'Невозможно сопоставить регулярные выражения для объекта 1008.'
В тот момент я понял, что 1008 выглядело знакомо... как itemNumber, который является Int32.
Что касается причины, то Core Data обычно, но не всегда, преобразует свойство int в строку, когда вы используете оператор CONTAINS, я не знаю. Но я знаю, что предикат работал хорошо, пока контекст не имел несохраненных изменений: в частности, когда было изменено свойство отношения объекта ко многим.
Нашел похожую проблему с предикатом, который проверял долготу NSDecimalNumber, и часть его пришла к longitude<-23
и вызвало obj_c_exception_throw
без подробностей. Исправление должно было гарантировать, что после <
т.е. longitude < -23
, Это в основном рухнуло за что-нибудь с <-
вместе, но не >-
,
NSPredicate выбрасывает некоторые странные недокументированные ошибки.
Кажется, я помню похожую проблему с коллекциями в Core Data, где мой предикат fetch как-то приводил к неверному SQL (другие типы хранилищ данных работали нормально). Это не идеальное решение, но если у вас мало объектов, вы можете выполнить полную выборку, а затем отфильтровать набор результатов по факту.