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

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)

Related

NSPredicate with Core Data

I've look through apple documentation and SO and I cannot find this simple code.
I will accept any down votes I get for such a question, but what is the predicate string that will return a given entity where that entity's property is equivalent to a given string.
I have company and product entities. At the moment, they are simple. Company has name and some other attributes. Product has manufacturer and some other attributes.
I want to do a fetch for all products where the products manufacturer attribute is equivalent to the name of the company that was selected in the company's view controller.
Here is the method in my data model that handles updating the products when a company is selected. It uses the title of the product view controller because that property is assigned the company name in didSelectItemAtIndexPath.
-(void)updateProducts {
NSFetchRequest *reqProd = [[NSFetchRequest alloc] init];
NSEntityDescription *entProd = [NSEntityDescription entityForName:#"Product" inManagedObjectContext:self.context];
[reqProd setEntity:entProd];
NSString *companyToFilter = self.productsViewController.title;
reqProd.predicate = [NSPredicate predicateWithFormat:#"manufacturer like %#", companyToFilter];
NSError *errorProd = nil;
self.productList = [self.context executeFetchRequest:reqProd error:&errorProd];
}
The problem is that this array always returns 0 elements. I know I have a product named iPhone who's manufacturer is apple. Therefore, it must be the predicate that is generating inaccurate SQL.
Again, I spent way too much time search for the answer to no avail. would somebody help me and explain the proper string for the predicate or direct me to the proper documentation?
Predicates are not exactly like SQL, but you can get it to output the SQL being used by adding -com.apple.CoreData.SQLDebug 1 in the startup options.
Also, the predicate format is documented. The short answers to your question is as John says: use CONTAINS instead of LIKE.
The semi-equivalent of the SQL LIKE operator in CoreData is BEGINSWITH for prefixes, ENDSWITH for suffixes, or CONTAINS for matches anywhere in the string.
[NSPredicate predicateWithFormat:#"manufacturer CONTAINS %#", companyToFilter]
You can also write CONTAINS[cd] to indicate "case and diacritic insensitive", or just CONTAINS[c] or CONTAINS[d].

NSPredicate predicateWithFormat dropping $ symbol from key

I am using NSPredicate to filter the contents of an NSArray of NSDictionary objects. Nothing crazy. I want to check three different values in the dictionary for the same search string. Here is the string I am passing to [NSPredicate predicateWithFormat:]:
($ECInstanceLabel CONTAINS[c] filterMe) OR (SubLabel1 CONTAINS[c] filterMe) OR (SubLabel2 CONTAINS[c] filterMe)
When I execute this, a valid instance of a new NSPredicate is created, but when I do [NSArray filteredArrayUsingPredicate:], the application throws an exception.
The exception is:
'NSInvalidArgumentException', reason: 'Can't get value for 'ECInstanceLabel' in bindings { }.'
I think part of the problem is that my format string specifies that the first dictionary key value is $ECInstanceLabel but somehow the $ is getting escaped. I have tried manually escaping this character but then the program crashes when I try to create the predicate, so I don't think the $ symbol is recognized as an escapable character.
Any ideas or something obvious I may be missing?
Thanks in advance.
$var is used in predicates for variables that can be substituted later.
If you have a key containing $ or other special characters then use the %K expansion:
[NSPredicate predicateWithFormat:#"(%K CONTAINS[c] %#) OR (SubLabel1 CONTAINS[c] %#) OR (SubLabel2 CONTAINS[c] %#)",
#"$ECInstanceLabel", searchTerm, searchTerm, searchTerm]
And use the %# format for the search term instead of writing it inline, to avoid
errors if the search term contains special characters.
Of course you can use %# for all keys, but in this case you need it only for
the first key that contains special characters.

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.

Core Data fetch request with array

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.

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"]];