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,

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html

Что может случиться так:

  • если одно из ранних состояний оценивается как 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 (другие типы хранилищ данных работали нормально). Это не идеальное решение, но если у вас мало объектов, вы можете выполнить полную выборку, а затем отфильтровать набор результатов по факту.

Другие вопросы по тегам