This one is baffling me:
I have a core data set that I search/filter using the following code:
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 {
l开发者_运维问答eads = [moc executeFetchRequest:fetchRequest error:&error];
}
@catch (NSException *exception) {
LogSevere(@"Error Executing Fetch Request: %@", exception);
leads = [NSArray array];
}
@finally {
[fetchRequest release];
}
sTerm is just a NSString
.
This works 95% of the time, however every once in a while it catches an NSInvalidArgumentException: Can't use in/contains operator with collection 10076173 (not a collection).
The Predicate String formats to be this:
((isLocalVersion == 1) AND (LeadStatusID =='Active')) AND ((LeadID contains[cd] 'i')OR (AccountNumber contains[cd] 'i')OR (ANY contactItems.FirstName contains[cd] 'i')OR (ANY contactItems.LastName contains[cd] 'i')OR (ANY addressItems.Address1 contains[cd] 'i')OR (ANY addressItems.City contains[cd] 'i'))
My catch allows me to not crash and just return 0 results, but this isn't optimal. Has anyone ever seen this before?
My only clue is that it seems to happen after I modify (and save) a record in the core data (again only sometimes).
Yep, you can't have expressions that evaluate to collections as part of a fetchRequest's predicate. This is in the documentation:
Aggregate expressions are not supported by Core Data.
The Core Data SQL store supports only one to-many operation per query; therefore in any predicate sent to the SQL store, there may be only one operator (and one instance of that operator) from
ALL
,ANY
, andIN
.
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html
What might be happening is:
- if one of the early conditions evaluates to
TRUE
, the remaining conditions are not evaluated, so no crash occurs - if you get to
OR (ANY contactItems.FirstName contains[cd] '%@')
and it'sTRUE
, you get your first use ofANY
, no crash occurs - if you get to
OR (ANY contactItems.LastName contains[cd] '%@')
, you get your second use ofANY
, crash occurs
You need to execute the ANYs separately and combine the results, possibly using NSCompoundPredicate;
Is LeadID a int property by any chance? I just ran into this same problem, constructing a predicate where I check to see if one or more properties contain a search term. It worked most of the time, but every now and then it would fail with an error just like yours.
The problem was that all of the properties I was checking were strings except one. When I removed that property from the predicate, everything worked fine.
I suspect 10076173 is the int value of LeadID for some object in your database. It appears that Core Data will usually convert the int to string like you expect, but sometimes it doesn't and that's when the error happens. The CONTAINS operator doesn't make sense on an integer and it crashes.
As further evidence, I tried changing the predicate to use LIKE instead of CONTAINS and I got a related error which finally tipped me off to what was happening. The predicate was
itemNumber LIKE "*test*" OR model LIKE "*test*" OR productDescription LIKE "*test*" [..snip..]
and the exception was
'Can't do regex matching on object 1008.'
At the point I realized 1008 looked familiar... like an itemNumber, which is an Int32.
As for the reason Core Data usually, but not always, converts an int property to a string when you use the CONTAINS operator, I don't know. But I do know that the predicate worked fine until the context had unsaved changes: specifically when an object's to-many relationship property was changed.
Found a similar problem with a predicate that was checking for NSDecimalNumber longitude and part of it came to longitude<-23
and caused an obj_c_exception_throw
with no more details. The fix was to ensure that there was a space after the <
, i.e. longitude < -23
. It basically crashed for anything with <-
together, but not >-
.
NSPredicate throws some odd undocumented errors.
I seem to remember having a similar problem with collections in Core Data, where my fetch predicate somehow resulted in invalid SQL (other data store types worked fine). It's not an ideal solution, but if you have few enough objects you could do a complete fetch and then filter the result set after the fact.
精彩评论