I have a data model setup with three entities, Course, Student, TestScores.
They are linked in too-many relationships like this:
Course <---->> Student <---->> TestScores
So a Course would have several Students, who in turn could have several TestScores (or no test scores)
The Course entity has a Name attribute. TestScores is a simple entity which just contains a testScore int attribute.
I want to be able to get an array of Students who have at least one textScore of 100, ordered by Course name. Is this possible with NSPredicate?
I think you could have your predicate as
ANY testScores.score == 100
Then put it all together in a fetch request:
NSFetchRequest *req = [NSFetchRequest fetchRequestForEntityNamed:#"Student"];
req.predicate = [NSPredicate predicateWithFormat:#"ANY testScores.score == 100"];
req.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"course.name" ascending:YES]];
Related
Assuming a Department entity and an Employee entity with a one-many relationship
Assuming relationship called employees on Department and department on Employee
Assuming Employee has an attribute name
I need to fetch all Departments that don't have an Employee called "Bob"
Based on other answers on SO that I've read, I've tried this:
name = #"Bob";
predicate = [NSPredicate predicateWithFormat:#"ALL employees.name != %#", name];
but it doesn't seem to work. Any ideas?
Thanks.
Do do not need a subquery here:
name = #"Bob";
predicate = [NSPredicate predicateWithFormat:#"NOT ANY employees.name == %#", name];
For some reason, the "ALL" aggregate does not work with to-many relationships, but the "ANY" aggregate works.
I have two entities in core data (call them entityOne and entityTwo). I have a entityOne<--->>entityTwo relationship between them.
I am now trying to code an NSPredicate to fetch entityTwo objects on the following basis:
Fetch entityTwo objects that have a relationship with a specified entityOne object.
Filter out those entityTwo objects which have no value for one of their attributes.
I am best doing both of these in an NSPredicate or is there a better way?
I am trying the following:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(attribute <> "") AND (relationship == entityOne"];
[request setPredicate:predicate];
Any pointers on coding great fully received.
You can use a Predicate like this:
[NSPredicate predicateWithFormat:#"entityOneRelationship = %# AND attribute.length > 0",specifiedEntityOne];
Pretty common approach to do that.
I'm new to core data and having some difficulty finding any information about sorting and only fetching non duplicates of a specified attribute.
I have a list of locations and they all have different addresses but some have the same city. i would like to fetch all the cities excluding duplicate cities and in alphabetical order.
Would the best way be to have 2 attributes, one for city and another for locationDetails. in the city attribute it will just have a list of cities with no duplicates and when selecting a city it will fetch all the locationDetails for that city attribute?
Thanks,
Yes the best way will be to have a separate attribute for city. And you don't need to structure your entity as unique. You can take care of sorting and fetching unique in your fetch request.
NSFetchRequest *request=[[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"<your entity name>" inManagedObjectContext:context]];
[request setResultType:NSDictionaryResultType];
[request setPropertiesToFetch:[NSArray arrayWithObject:#"City"]];
request.sortDescriptors=[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"City" ascending:YES]];
[request setReturnsDistinctResults:YES];
Note: This request returns an array of dictionaries with City as its key. Don't forget to release the NSFetchRequest after executing the fetch.
EDIT
When the user clicks on a city, store it in some variable (cityName). Now execute another fetch request like:
request.predicate=[NSPredicate predicateWithFormat:#"City = %#",cityName];
You should do this with the same entity. This will fetch only the objects whose city name has been selected. Now, you can either do the fetch in 2 ways:
Fetch the entire entities and then dispay using the format
entityName.Location. In this case you will get an NSArray of
objects
Set the properties to fetch like:
[request setPropertiesToFetch:[NSArray arrayWithObject:#"Location"]];
In the second case, it returns a NSDictionary ( Don't forget to set Result Type as NSDictionaryResultType as before ).
You should structure your Core Data model so that you have a City entity for each unique city and a Location entity with a one-to-one relationship to the relevant City entity (and a one-to-many reverse relationship). The city name itself should not be part of the Location entity. When structured this way, you simply fetch all City entities.
As for sorting, you should add a NSSortDescriptor to your NSFetchRequest. For example:
NSSortDescriptor *sorter;
sorter = [[NSSortDescriptor alloc] initWithKey: #"name" ascending: YES];
[fetchRequest setSortDescriptors: [NSArray arrayWithObject: sortDescriptor]];
For a relationship like this:
TagGroups<---->>Tags<<---->>Object
An Object has tags, tags can be grouped by tagGroups.
I have an object, and I want to know all of the TagGroups its Tags belong to.
To construct he predicate, I first tried the following format string:
(SELF is a TagGroup)
NSPredicate* p = [NSPredicate predicateWithFormat:#"%# IN SELF.tags.objects" , object];
This fails because sets are not traversed ala Key-ValueCoding.
After some research I have found several questions explaining SUBQUERY
Core Data, try to use NSPredicate to filter a toMany relationship set but get the "to-many key not allowed here" error
Core data to-many NSPredicate with AND
These seem to be part of the solution, but unlike these questions I am not testing for a value like "tag.name", but membership within the collection.
So with that in mind I tried this:
NSPredicate* p = [NSPredicate predicateWithFormat:#"%# IN SUBQUERY(SELF.tags, $eachTag,$eachTag.object)" , object];
Which fails to parse (I tried a few other variants unsuccessfully as well)
Any ideas on how to construct this format string properly?
Update:
Also tried it from the other direction:
NSPredicate* p = [NSPredicate predicateWithFormat:#"ALL SUBQUERY(%#.tags,$eachTag,$eachTag.tagGroup)" , anObject];
If you "have an object" i.e. you have a particular managedObject whose entity is Object then you don't need a predicate. You just need to walk the relationship keypath.
Here is an example implemented with dictionaries by it works the same way with a managed object.
NSDictionary *tagGroup1=[NSDictionary dictionaryWithObjectsAndKeys:#"tagGroupOne",#"name",#"tagGroup1",#"theValue",nil];
NSDictionary *tagGroup2=[NSDictionary dictionaryWithObjectsAndKeys:#"tagGroupTwo",#"name",#"tagGroup2",#"theValue",nil];
NSDictionary *tag1=[NSDictionary dictionaryWithObject:tagGroup1 forKey:#"tagGroup"];
NSDictionary *tag2=[NSDictionary dictionaryWithObject:tagGroup2 forKey:#"tagGroup"];
NSSet *tags=[NSSet setWithObjects:tag1,tag2,nil];
NSDictionary *objD=[NSDictionary dictionaryWithObjectsAndKeys:tags,#"tags",nil];
NSLog(#"tagGroup names=%#",[objD valueForKeyPath:#"tags.tagGroup.name"]);
NSLog(#"tagGroup objects=%#",[objD valueForKeyPath:#"tags.tagGroup"]);
... which outputs:
tagGroup names={(
tagGroupTwo,
tagGroupOne
)}
tagGroup objects={(
{
name = tagGroupTwo;
theValue = tagGroup2;
},
{
name = tagGroupOne;
theValue = tagGroup1;
}
)}
So, really all you need is a line like:
NSSet *tagGroups=[anInstanceOfObject valueForKeyPath:#"tags.tagGroup"];
That's the power of key-value coding.
You would only need a subquery if you were trying to fetch Objects that had a relationship to a TagGroup with a particular attribute value.
Lets say I have a model similar to the one below, and I need to Fetch all 'Person' of a specific Company.companyName sorted by personRole.roleWeight
This is the model I have at the moment:
Entity: Company
Attributes: companyName
Relationships: companyRole
Entity: Role
Attributes: roleName, roleWeight
Relationships: rolePerson, RoleCompany
Entity: Person
Attributes: personName
Relationships: person Role
Here is a simple diagram of the relationship:
Company --< Role >--< Person
Is there a way to do this?
If I need to modify the model I would be happy to do so. All suggestions are welcome.
Thanks,
You can't sort by role weight because it is possible to have more than one role that fits.
You also can't come at it from the Role (as opposed to the Person) because you have a many-to-many between role and person.
You should re-think your design because having that many-to-many there does not make much sense. A little bit of data de-normalization, changing that many-to-many to a one-to-many and duplicating the rolename and roleweight values would solve the issue.
Update
Assuming you changed the design to:
Company --< Role >-- Person
Then the solution gets much easier:
- (NSArray*)peopleSortedByWeightInCompany:(NSString*)company inManagedObjectContext:(NSManagedObjectContext*)moc
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Role" inManagedObjectContext:moc]];
[request setPredicate:[NSPredicate predicateWithFormat:#"company.name == %#", companyName]];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"weight" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
NSError *error = nil;
NSArray *roles = [moc executeFetchRequest:request error:&error];
[request release], request = nil;
NSAssert2(roles != nil && error == nil, #"Error fetching roles: %#\n%#", [error localizedDescription], [error userInfo]);
NSArray *people = [roles valueForKeyPath:#"#distinctUnionOfObjects.person"];
return people;
}
You basically fetch the Role entities sorted by weight and filtered by the company name. From that array of Role entities you then use KVC to gather all of the person objects on the other end of the relationship which will retrieve them in order.
Is there a reason that Role is the monkey in the middle and not person? It would make this task easier but maybe you have other things you are doing with the data that precludes this option. If each person had one role and one company you could then create a sort descriptor for role.roleWeight and use NSSet's sortedArrayUsingDescriptors: method on the Company in question's employees relationship set. That would give you a new sorted array with a list of all the Person entities attached to a given Company sorted by roleWeight (you might want to include a secondary sort descriptor to take care of matching roleWeights).