NSFetchRequest с NSPredicate иногда вызывает исключение NSInvalidArgumentException

#objective-c #core-data #nspredicate #nsfetchrequest

#objective-c #core-data #nspredicate #nsfetchrequest

Вопрос:

Это сбивает меня с толку:

У меня есть основной набор данных, который я ищу / фильтрую, используя следующий код:

 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:amp;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).

Формат строки предиката должен быть таким:

((isLocalVersion == 1) И (LeadStatusID ==’Активный’)) И ((leadID содержит [cd] ‘i’) ИЛИ (AccountNumber содержит [cd] ‘i’) ИЛИ (ЛЮБЫЕ контактные данные.FirstName содержит [cd] ‘i’) ИЛИ (ЛЮБЫЕ контактные данные.Фамилия содержит [cd] ‘i’) ИЛИ (ЛЮБЫЕ адресные элементы.Адрес1 содержит [cd] ‘i’) ИЛИ (ЛЮБЫЕ адресные элементы.Город содержит [cd] ‘i’))

Мой catch позволяет мне не аварийно завершать работу и просто возвращать 0 результатов, но это не оптимально. Кто-нибудь когда-нибудь видел это раньше?

Моя единственная подсказка заключается в том, что это, кажется, происходит после того, как я изменяю (и сохраняю) запись в core data (опять же, только иногда).

Ответ №1:

Да, у вас не может быть выражений, которые вычисляют коллекции как часть предиката fetchRequest. Это есть в документации:

Агрегированные выражения не поддерживаются Core Data.

Комментарии:

1. Хорошо, значит, в таком случае я должен получить результирующий набор для isLocalVersion и LeadStatusID , а затем оценить каждое из оставшихся выражений по отдельности и объединить результаты?

2. Спасибо. Кажется, что выполнение ORS отдельно и объединение результатов работает.

3. Но я не вижу здесь никаких агрегированных выражений. Можете ли вы указать, какие части являются агрегированными выражениями? Спасибо.

Ответ №2:

Хранилище 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;

Ответ №3:

Является ли leadID случайно свойством int? Я только что столкнулся с этой же проблемой, создавая предикат, в котором я проверяю, содержит ли одно или несколько свойств условие поиска. Это работало большую часть времени, но время от времени это приводило к сбою с ошибкой, подобной вашей.

Проблема заключалась в том, что все свойства, которые я проверял, были строками, кроме одного. Когда я удалил это свойство из предиката, все работало нормально.

Я подозреваю, что 10076173 является значением leadID для некоторого объекта в вашей базе данных. Похоже, что Core Data обычно преобразует int в string, как вы ожидаете, но иногда этого не происходит, и именно тогда возникает ошибка. Оператор CONTAINS не имеет смысла для целого числа, и он завершается сбоем.


В качестве дополнительного доказательства я попытался изменить предикат на использование LIKE вместо CONTAINS и я получил связанную ошибку, которая, наконец, подсказала мне, что происходит. Предикат был

Номер элемента ТИПА «* test *» Или модель ТИПА «* test *» ИЛИ описание продукта ТИПА «* test *» [.. снип ..]

и исключение было

«Не удается выполнить сопоставление регулярных выражений для объекта 1008».

В тот момент я понял, что 1008 выглядит знакомо… например, ItemNumber, который является Int32.


Что касается причины, по которой Core Data обычно, но не всегда, преобразует свойство int в строку при использовании оператора CONTAINS, я не знаю. Но я точно знаю, что предикат работал нормально, пока в контексте не были сохранены изменения: в частности, когда было изменено свойство отношения объекта ко многим.

Комментарии:

1. Вау, очень полезная информация. Мне придется исследовать это дальше. Я использовал cludge для решения проблемы, но могу повторно исследовать ее с помощью этой информации. 1

Ответ №4:

Обнаружена аналогичная проблема с предикатом, который проверял долготу NSDecimalNumber, и часть его дошла до longitude<-23 и вызвала obj_c_exception_throw ошибку без дополнительной информации. Исправление состояло в том, чтобы гарантировать, что после < , т. е. longitude < -23 , был пробел. Он в основном зависал для чего угодно с <- вместе, но не >- .

NSPredicate выдает несколько странных недокументированных ошибок.

Ответ №5:

Кажется, я помню, что у меня была аналогичная проблема с коллекциями в Core Data, где мой предикат выборки каким-то образом привел к недопустимому SQL (другие типы хранилищ данных работали нормально). Это не идеальное решение, но если у вас достаточно мало объектов, вы могли бы выполнить полную выборку, а затем отфильтровать результирующий набор постфактум.