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