NSPredicate with to-many to-many relationships - swift

I am having some trouble writing a predicate for my NSFetchedResultsController to get all Modifier from a table where where Exercise and Modifier contain the same Exercise_Modifier
The model look like this: Exercise <->>>Exercise_Modifier<<<->Modifier
I've tried using ANY and ALL keywords but cannot seem to get to the answer. I've tried this along the line of this:
fetchRequest.predicate = NSPredicate(format: "ALL exercise_modifiers == %#", exercise!.modifiers!)
where exercise_modifiers is the relationship from Modifier <->>>Exercise_Modifier and exercise!.modifiers! are the Exercise_Modifier for the current exercise I have. Does anyone know where I might be missing something?

Related

How to use Dynamic Multiple NSPredicate from NSArray in Core data

I am fairly new to core data. I have database which contains articles with different categories. I want to fetch data from multiple categories, which can be selected by user on the fly. Now I have NSArray which contains selected multiple categories (count from 1 to n) I want to add predicate for selected categories but I'm not able to do so. as per my knowledge i have added for loop for adding categories in predicate but it is not working.
for i in 0...(catArray?.count)!-1 {
let str = String(format:"catId = %#",(catArray?.object(at: i) as? NSNumber)!)
predicateString = predicateString+str
}
let dP = NSPredicate(format:"%#",predicateString)
fetchedRequest.predicate=dP;
but app is crashing while fetching request.
Is there any other way to do it?
Please help me with this.
Any help is much appreciated.
To select objects which have any category from the given array
a simple predicate is sufficient:
NSPredicate(format: "catId IN %#", catArray)
Remark: Never use String(format:) to build predicates dynamically,
because almost surely the quoting and escaping will be wrong.
Use only NSPredicate(format:) and, if necessary, NSCompoundPredicate.

How to verify that NSMutableArray of entity contains object with certain attribute

Based on the data model here: Photographer<------->>Photo
When the user goes to add a Photo, the user also specifies what Photographer took said picture. When the user decides to save the Photo, there is the possibility that the Photographer doesn't exist yet. After executing a NSFetchRequest to get a list of all instances of Photographer, how do I check if the NSMutableArray(photographerArray) contains an object that has the same fullName attribute as what the user is currently adding?
The naive way would be to simply get all the fullName of the array and check if it's in it.
BOOL photographerExist = [[listOfPhotographer valueForKey:#"fullName"] containsObject:enteredFullName];
However, it would be way more easier to put that directly in your fetch request. Just add a predicate to it.
NSPredicate *fullNamePredicate = [NSPredicate predicateWithFormat:#"fullName = %#", enteredFullName];
fetchRequest.predicate = fullNamePredicate;
If the result of the fetch is empty, then the photographer doesn't exist yet.

NSPredicate multiple field search

I'm trying to search multiple fields. Something like this:
[NSPredicate predicateWithFormat:#"(name title contains[cd] %#) AND (title contains[cd] %#", self.searchBar.text];
A search is made on both the name field or the title field.
Also, if anyone knows what a wildcard search would look like I'd appreciate that too.
I tried:
[NSPredicate predicateWithFormat:#"* contains[cd] %#", self.searchBar.text];
my error code:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "(name contains[cd] %#) OR (title contains[cd] %#"'
First off, you must specify all the properties to examine, since there is no fixed or universal list of properties, and in Objective-C there is no real semantic distinction between a property and any other method.
Second off, to examine multiple properties to see if they contain a search string, you should use OR, not AND, since your search is satisfied if any of the properties match, not all.
Otherwise, the syntax you have appears correct (I'm assuming name title in the first subpredicate is meant to be just name.)
Not sure if anyone still reads this post, but I found your problem. You are missing a closing ")" in the second predicate. Here is the correct code:
[NSPredicate predicateWithFormat:#"(name title contains[cd] %#) AND (title contains[cd] %#)", self.searchBar.text];
Edit: After testing this I noticed that two format specifiers worked fine with ONE format argument (see code above). When I added a third predicate and format specifier (with only one format argument) it crashed. Either way, you should always have the same amount of format specifiers and format arguments.

accessing values using multiple relationships with NSPredicate

here is my core data model:
Locations < --- >> ThemesList << --- > Themes
The entites have the following attributes:
Locations
- Property: Name
- Relationship: ThemesList
ThemesList
- Relationship: Locations
- Relationship: Themes
Themes
- Property: Name
- Relationship: Locations
I am running a fetch on entity Locations and I want to only pull values where the name property in Themes is equal to a particular value. Based on what I've read, I need to do a subquery. I've tried something along the lines of the code below, but I always receive the error of Unable to parse the format string
[NSString stringWithFormat:#"SUBQUERY(ThemesList, $theThemes, $theThemes.Themes.Name LIKE %#)", #"a theme name"];
Any ideas on how I can accomplish this - what am I doing wrong?
Thanks
As a rule of thumb, you should fetch the objects of the entity you are testing with the predicate instead of starting with another entity altogether and walking a relationship inside the predicate. Walking relationships, especially to-many relationships inside of predicates is computationally intensive and slow.
Instead, I would recommend running the fetch against the Theme entity and then just ask the returned Theme objects for their associated Locations objects.
Your predicate would simplify to just:
NSPredicate *p = [NSPredicate predicateWithFormat:#"Themes.Name LIKE %#", #"a theme name"];
Then use a collection operator to find the location objects:
NSSet *locations=[fetchedThemes valueForKeyPath:#"#distinctUnionOfSets.locations"];
Your problem is this: $theThemes.Themes.Name. The parser will want to parse it as a variable expression (since it starts with a $), but when it comes across the ., it's going to produce an error.
As it turns out, the SUBQUERY is unnecessary. That same query can be effectively done as:
NSPredicate *p = [NSPredicate predicateWithFormat:#"ANY ThemesList.Themes.Name LIKE %#", #"a theme name"];
Hopefully Core Data will have an easier time handling that than the SUBQUERY.

Core Data: Subquery Performance Problem

I've been trying to see if there is any way I can improve on the performance of the following Predicate:
[NSPredicate predicateWithFormat:#"A=%# and SUBQUERY($self.words,$k,$k.keyword IN %#).#count==%d",
<value for A>,
keywordList,
[keywordList count]];
What I'm trying to do is return all the records of A that have keywords that are ALL contained in the supplied array (keywordList). I have a small database of about 2000 records. However, the keywords for each Entity ranges from 40-300 keywords. The keywords are represented as a to-Many relationship from A to an entity called Keywords. The above query works but takes about 7 seconds to run on my iPhone4. I want to see what I can do to shrink that down to a sub-second response.
Thanks,
Michael
This doesn't quite solve the problem as the results that come back from intersection of the fetchedObjs is not correct. It doesn't guarantee that all the 'A' objects contain all the supplied keywords. In fact, what comes back is an empty list and the entire process actually takes longer.
Something must be wrong with my model or the previous answer. I'm not getting the results I'm expecting. One alternative to returning a list of 'A' would be to return a list of ManagedObjectIDs of 'A'. [mo valueForKey: (NSString *)] returns the object of the relationship.
Note: I originally posted this question anonymously thinking I was logged in, so i can't seem to comment on anybody's answer.
I think you are approaching the problem backwards. If you have the keywords, you should search for the Keyword objects and then walk their A relationships to find the related A objects. Then check for overlap in the relationship sets.
So something like:
NSFetchRequest *fetch=//...set up fetch
[fetch setEntity:Keyword_entity];
NSPredicate *p=[NSPredicate predicateWithFormat:#"keyword IN %#",keywords];
[fetch setPredicate:p];
NSArray *fetchedObj=//... execute fetch
NSMutableSet *inCommon=[NSMutableSet setWithCapacity:[fetchedObjs count]];
for (NSManagedObject *mo in fetchedObjs){
if ([inCommon count]==0){
[inCommon addObjects:[mo valueForKey:#"aRelationshipName"]];
}else{
[inCommon intersectSet:[mo valueForKey:#"aRelationshipName"]];
}
}
... inCommon would then contain a set of all unique A objects that shared all the keywords.
Update:
From OP author:
This doesn't quite solve the problem
as the results that come back from
intersection of the fetchedObjs is not
correct. It doesn't guarantee that all
the 'A' objects contain all the
supplied keywords.
Okay, lets take another tack. Assuming you have a data model that looks something like this:
A {
keywords<<-->>Keyword.as
}
Keyword{
keyword:string
as<<-->>A.keywords
}
Then something like this should work:
NSFetchRequest *keyWordFetch=//...set up fetch
[keyWordFetch setEntity:Keyword_entity];
NSPredicate *p=[NSPredicate predicateWithFormat:#"keyword IN %#",keywords];
[keyWordFetch setPredicate:p];
NSArray *fetchedKeywords=//... execute keyWordFetch
NSFetchRequest *entityAFetch=//...set up fetch
[entityAFetch setEntity:A_entity];
NSPredicate *pred=[NSPredicate predicateWithFormat:#"ALL keywords IN %#", fetchedKeywords);
[entityAFetch setPredicate:pred];
NSArray *fetchedAs=//... execute fetch