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"]];
Related
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)
So I have an nspredicate that I am using to filter values in a json array. Instead of just searching for one value, I would like the predicate to find objects that are within a range of numbers such as 0-1.0 or 1.1-2.0.
This is my current predicate:
NSPredicate *Predicate = [NSPredicate predicateWithFormat:#"Engine CONTAINS %#", #"?"];
It is probably a simple solution but I have yet to find an answer to this. Thank you for your time.
Please read documentation: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSPredicate_Class/Reference/NSPredicate.html
You can use conditions like AND and OR as well as inequality operations < and >
There are plenty of ways to do this and the docs have a few examples - see the Basic Comparisons section.
A basic solution would probably look like this
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Engine BETWEEN %#", #[ #0, #1]];
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.
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.
Let's say i want to let user search for my objects using a name property of the objects.
I have no problem if the user only enters one word:
e.g: facial
My predicate will be:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name CONTAINS[cd] %#", word];
But what if user enter more than one word separated by space?
I want to do sth like:
NSArray *words = [query componentsSeparatedByString:#" "];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name CONTAINS[cd] ANY %#", words];
But it doesnt work. Any guidance?
Thanks!
Another way of doing this (and I just learnt this myself as a result of your question) is to use subqueries. Check this SO question for more details. You can use subqueries in the following manner –
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(%#, $str, SELF CONTAINS[cd] $str).#count != 0", words];
NSLog(#"%#", [array filteredArrayUsingPredicate:predicate]);
This seems to work as I've tested it myself but this could also be the arcane & obscure way that Dave has mentioned as it finds no mention in the Predicate Programming Guide.
The format for a SUBQUERY can be found here. It's the same link that you will find in the question linked earlier.
As you mentioned (correctly) in the comment, you can do this by building a compound predicate predicate, although you'll want to use orPredicateWithSubpredicates, and not the and variant.
That really is the best way to do this. There are others, but they rely on more arcane and obscure uses of NSPredicate, and I really recommend going with the build-a-compound-predicate route.