Core Data fetch request with array - objective-c

I am trying to set a fetch request with a predicate to obtain records in the store whose identifiers attribute match an array of identifiers specified in the predicate e.g.
NSString *predicateString = [NSString stringWithFormat:#"identifier IN %#", employeeIDsArray];
The employeeIDsArray contains a number of NSNumber objects that match IDs in the store. However, I get an error "Unable to parse the format string". This type of predicate works if it is used for filtering an array, but as mentioned, fails for a core data fetch. How should I set the predicate please?

NSPredicate doesn't use formats like NSString does. So that the result of a predicate creation using the pre-generated predicateString won't be a valid predicate.
You have to make it an actual predicate:
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"identifier IN %#", employeeIDsArray];
[fetchRequest setPredicate:predicate];
See the documentation for more informations on predicate formats.

When you create that string using stringWithFormat:, that method inserts the array's description (a string describing the contents of the array) where you had %#.
That's not what you want. You don't want to test membership in a string describing the array; you want to test membership in the array. So, don't go through stringWithFormat:—pass the format string and the array to predicateWithFormat: directly.

Related

NSPredicate : CoreData fetchRequest filtered by Array of dictionaries

Is it possible to create an NSPredicate using an array of dictionaries ?
I have a following structure :
[{ name: "foo", city:"Paris"},{name:"bar", city:"London"}]
An I want to filter my NSFetchRequest by these pairs. (supposing the properties have the same names in CoreData)
When passing an array I can use the keyword IN. But I don't get how make this work with an array of dictionaries.
I don't think you will be able to use IN, so you need to use a number of ORs. In outline:
Iterate through all elements of the array.
For each element (dictionary), construct a predicate of the form:
[NSPredicate predicateWithFormat:#"name == %# AND city == %#",[dictionary objectForKey:#"name"], [dictionary objectForKey:#"city"]];
Add each such predicate to a NSMutableArray (array) of predicates
Build a compound predicate from the array using:
[NSCompoundPredicate orPredicateWithSubpredicates:array]
If performance is an issue, consider building the individual predicates with substitution variables rather than with format.

Objective-C - wrap attribute value with commas in NSPredicate CONTAINS

Here is the situation. I have some Core Data data and I want to get the data where the value of an attribute wrapped with two commas contains another string.
This is my current code:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"type CONTAINS %#", [NSString stringWithFormat:#",%#,",typeBar]];
Simply put, this is what I want to achieve:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#",type, CONTAINS %#", [NSString stringWithFormat:#",%#,",typeBar]];
With the commas around the 'type'. But obviously when I do it like this, it doesn't recognise the attribute anymore. I have tried ,'type', but that doesn't work either.
I have tried this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#",%K, CONTAINS %#", #"type", [NSString stringWithFormat:#",%#,",typeBar]];
But I get a: 'NSInvalidArgumentException', reason: 'Unable to parse the format string ",%K, CONTAINS %#"'
I hope the question was clear enough.
Thanks a lot!
You can't do that. What you're trying is attempting to change the key to be queried to something invalid, hence the parse error.
You can't really do what you want to if you just store a plain type. Presumably your type may be a string list of things?
So, you need to rethink your approach, there are a number of options depending on what your underlying problem is:
Store the text for type with commas at the beginning and end (as well as between items)
Don't use a string, use some other entity (if you have a list of items)
Predicate on the plain type, without the commas, and then filter the results in more detail once you have the items out of Core Data (where predicates are restrictive compared to 'full' code access to the strings)

Using NSPredicate to search NSArray attribute of core data entity

I've searched far and wide to find a solution to this problem. All of my attempts result in 0 results. Here is the general data structures:
Core Data Entity A {
stringAttribute string
....
transformableAttribute(NSArray of NSString objects) keywords
}
where keywords = [NSArray arrayWithObjects:#"string 1",#"string 2",#"string 3",nil]
I'm trying to run a predicate to search the NSArray transformable attribute.
I've tried the following against entity A. The core data store is a sqlite store.
NSString *term = #"string 1";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY keywords like[cd] %#", term];
----> Results in 0 matches
NSArray *termArray = [NSArray arrayWithObject:#"string 1"];
NSPredicate *predicate = [NSPredicate predicateWithFormat#"ANY keywords in %#", termArray];
----> Results in 0 matches
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(keywords, $x, $x like %#).#count > 0", term]
----> Results in an error that a non-relationship cannot be a subquery collection
I've tried some permutations of the above, but still no results. Any advice? Is this possible with Core data?
Thanks!
I'm not use with transformableAttribute that is an element of Core Data that I haven't got into, but if I recall you can't predicate on them. (so If I'm wrong please some one call me on this)
But when I see that your transformableAttribute is an array of string I just want to say why do you want an array in a database?
Why not make a relationship? A one to many relationship if your array can have different number of string.
Core Data is an abstraction of a Data Base, in a DB you don't use array, you use tables. That would probably simplify your life and make the fetching possible.

How do I set up a NSPredicate to look for objects that have a nil attribute

I have a ManagedObject class, and one of the members of the class is a NSDate. I would like to display all objects of the class for which the date is NOT set. I tried using a predicate like this:
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"(date = NIL)"];
But I still get objects where the date is set. What is the right way to set up a predicate for this?
I think it's a case sensitivity issue. You can use "nil" or "NULL", but not "NIL". This works fine for me:
NSPredicate *eventWithNoEndDate = [NSPredicate predicateWithFormat:#"endDate = nil"];
Figured it out. Couldn't do it by using a predicate with a string format, so tried a predicate with a template and it worked. Here's the code that gave me objects that had endDate set to NULL:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"endDate = $DATE"];
predicate = [predicate predicateWithSubstitutionVariables:
[NSDictionary dictionaryWithObject:[NSNull null] forKey: #"DATE"]];
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Predicates/Articles/pUsing.html#//apple_ref/doc/uid/TP40001794-SW4
following code should work
predicate = [NSPredicate predicateWithFormat:#"firstName = nil"];
There's a super annoying behavior of fetch requests, as documented by Apple:
If an object in a context has been modified, a predicate is evaluated against its modified state, not against the current state in the persistent store. Therefore, if an object in a context has been modified such that it meets the fetch request’s criteria, the request retrieves it even if changes have not been saved to the store and the values in the store are such that it does not meet the criteria. Conversely, if an object in a context has been modified such that it does not match the fetch request, the fetch request will not retrieve it even if the version in the store does match.
It's possible you're clearing the date elsewhere and the fetch request is including results where the date is nil in memory but still set on disk (in the persistent store), and so when the object faults it loads the object with the date set.
My only advice would be to coordinate access to the managed object context (say, on an NSOperationQueue) such that any updates are able to be saved to the persistent store before executing the fetch request.

NSPredicate case-insensitive matching on to-many relationship

I am implementing a search field where the user can type in a string to filter the items displayed in a view. Each object being displayed has a keywords to-many relationship, and I would like to be able to filter the objects based on their keywords. Each keyword object has a name property, so I've set up an NSPredicate to do the filtering that looks like this:
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"keywords.name CONTAINS %#", self.searchString];
This works, but the problem is that the search is case-sensitive, so if the keyword has a capital letter but the user types in all lowercase, no matches are found. I've tried the following modification:
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"keywords.name CONTAINS[c] %#", self.searchString];
But that doesn't make any difference in the case sensitivity of the matching. Is there a way to do this case-insensitive matching using just a plain predicate? Or will I need to implement some sort of custom accessor on the keyword class, e.g. write a lowercaseName method and match against a lowercased version of the search string instead?
Addendum:
After further exploration, the workaround of adding a custom accessor works OK for manual use of NSPredicate, but does not work at all when using NSFetchRequest with Core Data, which only works when querying attributes defined in the Core Data model.
If I understand you correctly, you want your predicate to be true whenever any keywords name matches the search string. For this you need to test with the ANY keyword like this:
[NSPredicate predicateWithFormat:#"ANY keywords.name CONTAINS[c] %#", ...];
This will search the keywords and return true if any of those keywords name contains your search string.
I believe the answer is:
[NSPredicate predicateWithFormat:#"keywords.name CONTAINS[cd] %#", self.searchString];
String comparisons are by default case and diacritic sensitive. You can modify an operator using the key characters c and d within square braces to specify case and diacritic insensitivity respectively, for example firstName BEGINSWITH[cd] $FIRST_NAME.
Predicate Format String Syntax
If you is trying to catch only the equals names but with insensitive case, I think it is the best solution
[NSPredicate predicateWithFormat:#"ANY keywords.name LIKE[c] %#", ...];
You helped me a lot. Thanks guys!!!
In my case I did:
[NSPredicate predicateWithFormat:#"ANY name LIKE[c] %#", #"teste"];
If you must match the keyword but the search must be case-insensitive then you should use NSPredicate(format: "keywords.name =[c] %#", self.searchString)
LIKE does not work on string literals.
If you want both case insensitive and wildcard, use this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"(name like[c] '*%#*')",#"search"]];