Core Data NSPredicate for toMany Relationsihp - objective-c

I've got a problem in the formulation of my query; this is the scenario:
based on this db schema, I know the naming part sucks a little, but this is
a project which has been already kicked off, so I've to stick with that.
Now, my goal is to select all the characters with a determined pack_id and a certain category_id, i.e. all the characters from pack 1 in category 5, so this's my NSPredicate
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(category_id == %#) AND (charRelationship.pack_id == %#)", [[cat valueForKey:#"category_id"] stringValue], curPack];
[request setEntity:[NSEntityDescription entityForName:#"Category" inManagedObjectContext:self.managedObjectContext]];
[request setPredicate:predicate];
NSError *error;
NSArray *result = [self.managedObjectContext executeFetchRequest:request error:&error];
as soon as the compiler tries to execute the fetchRequest, it crashes and goes SIGABRT.
I really hate the fact that xcode is not even giving me a clue about the exception so that I could figure it out myself. So after blindly trying to fix it with no success, I wonder if there's anybody out there who could help me.
I've already red a ton of other threads on SO and elsewhere, but I couldn't find any solution.
thanks a lot
-k-

try using ANY for the relationship:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(category_id == %#) AND (ANY charRelationship.pack_id like %#)", [[cat valueForKey:#"category_id"] stringValue], curPack];

Actually you must not store any id separately for an entity that is associated. E.g.: in your case you could just refer to category from the characters entity like:
[NSPredicate predicateWithFormat:#"catRel = %#", cat];
However, I'm not sure what are you going to do with the snippet. Am I right that you have a Category entity stored in your 'cat' variable, and just going to select it from the database with the predicate? I would say, that a category_id already identifies your category... whatever, try not user "charRelationship.pasck_id == %#" but load the corresponding Character entity and use "charRelationship = %#" where you just put your entity.

Related

NSPredicate searching for 1:N - 1:N entities

I have an Entity called AcademicYear that has a relationship 1:N with an entity called Subject which has a 1:N relationship with an Entity called SubjectLanguage:
AcademicYear<--->>Subject<--->>SubjectLanguage
I would like to filter the result of a query on AcademicYear in order to be able to gather Subject and SubjectLanguage already filtered by the parameter language.
On runtime i'll use such expression:
academicYear.hasSubjects.hasLanguages.subjectName
and i would like to be sure they are of a specific language filtered by the starting query.
i tried using during the fetch the following predicate (with no result):
NSPredicate *predicateLang = [NSPredicate predicateWithFormat:#"ALL hasSubjects.hasLanguages.language like %#", language];
NEW (Update)
Due to Marcus suggestion i changed the fetch request in this way:
NSError *error;
NSString * language = [[NSLocale preferredLanguages] objectAtIndex:0];
NSString * yearString = [NSString stringWithFormat:#"%d",year];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"belongToAcademicYear.yearId == %# and ANY hasLanguages.language CONTAINS[cd] %#", yearString, language];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Subject"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
return [self arrayToMutableArrayConverter:fetchedObjects];
}
then i tested this solution with the following code:
NSArray * currentSubjects = [self.coordinator fetchSubjectThatBelongsToAcademicYear:1];
Subject * currentSubject = currentSubjects[0];
NSUInteger count = currentSubject.hasLanguages.count;
XCTAssertEqual(count, 1, #"riscontrati troppi languages. non è stato fatto il filtro su language");
I found the filter did not work correctly and i found two languages while i expect one. I'm pretty sure the issue is related with the predicate.
Any support is appreciated
kind regards
Nicolò
You should reverse this a little bit. Instead of fetching AcademicYear and then trying to reach over to SubjectLanguage, search for Subject.
Build your NSFetchRequest against Subject and change the predicate to:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"academicYear == %# and ANY languages.language CONTAINS[cd] %#", academicYear, language];
Some comments:
Your relationships should not start with "has". They should just be academicYear and languages. Singular for to-one and plural for to-many.
Your entity SubjectLanguage should not have a property called language. Something that relates to the entity can be less descriptive. name would work well so that it is the "name of the SubjectLanguage".
Update
It worked exactly right. You found the Subject that has the language and the year you are looking for. It returned that Subject and ALL of its relationships. Since there is more than one language associated with the subject it gave you access to all of the languages.
The predicate is correct, your test is flawed. Your test should be insuring that one of the languages is the one you are looking for.

Problems with Boolean type attribute in Core Data with predicates in Xcode

Okay, so I already tried the examples on apple's SDK developer pages and it didn't work. I tried the examples from previous questions on stackoverflow like :
NSEntityDescription entitydesc = [NSEntityDescription entityForName:#"Model" inManagedObjectContext:context];
NSFetchRequest request = [[NSFetchRequest alloc]init];
[request setEntity:entitydesc];
//tried this style of predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"accessible == %#", [NSNumber numberWithBool:YES]];
//and this one
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"accessible == YES"];
[request setPredicate: predicate];
NSError *error;
NSArray *matchingData = [context executeFetchRequest: request error: &error];
both of which didn't work. In debugger the matchingData shows 0 objects when it should have many.
All my other fetchrequests have worked perfectly fine. This is the only one giving me problems.
The attribute is listed as type Boolean in the .xcdatamodel
The attribute is listed as this under the entity's header file:
#property (nonatomic, retain) NSNumber * accessible;
The data was entered into the core data database as follows:
NSNumber *accessibleFieldValue = [NSNumber numberWithBool:YES];
[newModel setValue: accessibleFieldValue forKey:#"accessible"];
I've checked and there are no nil values entered in the sqlite database.
What should I do?
It works fine with the new literals:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"done = %#",#NO];
NSNumber *accessibleFieldValue = [NSNumber numberWithBool:YES];
[newModel setValue:accessibleValue forKey:#"accessible"];
accessibleFieldValue or accessibleValue? If you're sure this snippet of code is right, that's where you did wrong.
And b.t.w, you can set value simply like:
newModel.accessible = [NSNumber numberWithBool:YES];
I ended up going to the database in sqlite database browser and changing the type for the field value to Numeric. For some reason core data entered the data as an incompatible data type.....don't really know why, but I have a feeling this might be an Xcode bug. As I said, it works now after I changed the field type. I don't think I will be using boolean values in core data any time soon.....at least not until I understand why this was a problem.

NSPredicate and CoreData?

The query works fine if directly added to a predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"author == %#", author];
[request setPredicate:predicate];
[self.managedObjectContext executeFetchRequest:request error:nil];
The query doesn't work if created and then passed to a predicate
Is there a solution? I rather not pass the predicate itself to the method
Author is a subclass of NSManagedObject
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "%#"'
[self fetchObjectsWithQuery:[NSString stringWithFormat:#"author == %#", author];
- (void)fetchObjectsWithQuery:(NSString *)query
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%#", query];
[request setPredicate:predicate];
[self.managedObjectContext executeFetchRequest:request error:nil];
}
Format strings work differently in
NSString *query = [NSString stringWithFormat:#"author == %#", author] // (1)
and
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"author == %#", author]
In particular the placeholders "%#" and "%K" have different meanings.
(1) produces a string of the form
"author == <textual description of author object>"
which you cannot use in
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%#", query];
So you cannot pre-format predicates as strings. Another example to demonstrate the problem:
[NSPredicate predicateWithFormat:#"author == nil"]
works, but
[NSPredicate predicateWithFormat:"%#", #"author == nil"]
does not.
There's no good reason not to create and pass around NSPredicate objects. There are ways to do exactly what you want to do, but they are either less expressive than just using predicates or will require you to duplicate the logic underlying NSPredicate.

A reverse kind of string compare using NSPredicate

I've been searching for this answer all over internet but so far no luck. So I need to consult the smart and nice people here. This is my first time asking a question here, so I hope I am doing this right and not repeating the question.
For all the examples I saw, it's the search string that is a substring of what's stored in the Core Data. On the other hand, I want to achieve the following:
The strings stored in core data are actually sub-strings. I want to do a search by getting all core data rows that have substrings belong to the provided search string.
For ex:
In core data, I have "AB", "BC","ABC","ABCDEF","GH", "ABA"
And in the app I do a search by providing the super-string: "ABCDEF", the result will return "AB","BC","ABC","ABCDEF" but not "GH", "ABA" because these two sub-strings don't belong to the super-string.
How should I setup my predicateWithFormat statement?
This wont' work cuz it's doing the opposite:
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"substring LIKE[c] %#", #"ABCDEF"];
Thanks all!
The reverse of CONTAINS will not work. Also, you will not be able to use LIKE because you would have to take the attribute you are searching and transform it into a wildcard string.
The way to go is to use MATCHES because you can use regular expressions. First, transform your search string into a regex by affixing a * after each letter. Then form the predicate.
This solution has been tested to work with your example.
NSString *string= #"ABCDEF";
NSMutableString *new = [NSMutableString string];
for (int i=0; i<string.length; i++) {
[new appendFormat:#"%c*", [string characterAtIndex:i]];
}
// new is now #"A*B*C*D*E*F*";
fetchRequest.predicate = [NSPredicate predicateWithFormat:
#"stringAttribute matches %#", new];
where stringAttribute in the predicate is the name of your NSString attribute of your managed object.
I think this will work:
NSPredicate *pred = [NSPredicate predicateWithFormat:#"%# contains self",#"ABCDEF"];
You would use it like this in core data:
-(IBAction)doFetch:(id)sender {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:#"Expense" inManagedObjectContext:self.managedObjectContext];
request.predicate = [NSPredicate predicateWithFormat:#"%# contains desc",#"ABCDEF"];
NSArray *answer = [self.managedObjectContext executeFetchRequest:request error:nil];
NSLog(#"%#",answer);
}
In this example, "desc" is an attribute of the entity "Expense". This correctly retrieves only the rows where "desc" is a substring of "ABCDEF".

How to use NSPredicate to filter based on child elements?

I'm using Core Data; let's say that I have a many-to-one relationship between employees and departments, and employees are stored in an NSSet in each department. I want to find all the departments that only have one employee. How do I do this with Core Data?
I tried the below code and I get an exception saying that MYEmployee doesn't respond to allObjects.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY SELF.employees.allObjects.count == 1"];
singleEmployeeDepartments = [[myModelController allDepartments] filteredArrayUsingPredicate:predicate]];
First of all, you're not using Core Data efficiently here, because you are always fetching all departments from the data store and then filtering the resulting array in memory. You can query the managed object context directly for matching departments, instead of fetching all departments, which can reduce memory consumption and might also be faster if you have a lot of departments.
NSManagedObjectContext *moc = ...;
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:#"Department" inManagedObjectContext:moc]];
[request setPredicate:[NSPredicate predicateWithFormat:#"employees.#count == 1"]];
NSArray *singleEmployeeDepartments = [moc executeFetchRequest:request error:NULL];
The other key part is to use the #count operator in the predicate string to query departments that have a specific number of employees.