NSPredicate to get children's children's - ios

I want to fetch all the treatments, that are children of this List object.
I am not sure if it can be done.
I have a TableView showing all the patients of a list. Each patient can have many treatments.
I have a reference to the current list, and want to use that to somehow find the childrens children.
Thank you

Use a fetch request for the "Treatment" entity with the predicate
[NSPredicate predicateWithFormat:#"ofPatient.ofList = %#", currentList]
using the inverse relationships from "Treatment" -> "Patient" -> "List".

Related

Predicate to fetch objects which parameter is contained in an specific NSSet

I have following entities:
PBOUser, PBOBusiness and PBOLocation.
PBOUser may have a lot of businesses.
PBOBusiness may have a lot of locations.
PBOLocation may belong to only one business.
PBOBusiness may belong to many users.
I need to find these locations which belong to those businesses that my user owns.
let predicate = NSPredicate(format: "business IN %#", myUser.businesses)
let locations = PBOLocation.MR_findAllWithPredicate(predicate) as? [PBOLocation]
But it doesn't work. How to do this in a quick way?
If you ever wanted to do it as a query you would need to use a SUBQUERY predicate
let predicate = NSPredicate(format: "SUBQUERY(business.users, $user, $user == %#).#count > 0", myUser)
business.users is the keyPath to the collection whose elements you want to test
Each item in the collection is evaluated agains the predicate $user == %#
When each item is evaluated against the predicate we use $user to make that the variable name for the single element to use in the predicate
The SUBQUERY will return a collection of results that match so to summarise we use #count > 0 to say if there was at least one match then this fits our criteria
You could probably come up with a predicate that works. But since you already have a reference to PBOUser, you're making things harder than they need to be. Consider that
PBOUser has a relationship to PBOBusiness
PBOBusiness has a relationship toPBOLocation`
With these relationships, a user's businesses are myUser.businesses, as in your code snippet. So take that one step farther and use the fact that locations are a property of the businesses:
let locations = myUser.businesses.valueForKey("location") as NSSet
This makes use of the fact that valueForKey on an array or set will call valueForKey on each member of the collection, and return the results as the same kind of collection. That's why it's an NSSet-- if you want a sorted array, add a call to sortedArrayUsingDescriptors:.

CoreData fetch with NSSortDescriptor by count of to-many relationship

I have 2 CoreData entities (Person, Pet) with the following relationship:
A Person can have multiple Pets. So, there is a To-Many relationship from Person->Pet.
I want to fetch all Persons, sorted by the count of Pets that they have where the person with the most Pets is first, and the person with the fewest Pets is last.
When fetching a list of Persons, I tried using an NSSortDescriptor like the following:
[NSSortDescriptor sortDescriptorWithKey:#"pets.count" ascending:NO]
I thought that the "pets" property on a "person" would allow me to use the count property of NSSet to sort. Alas, CoreData complains with this error:
Fetch exception to-many key not allowed here
Is this a completely wrong approach? How am I supposed to do something like this with a CoreData fetch?
Thanks in advance for your help.
Try using collection operators:
[NSSortDescriptor sortDescriptorWithKey:#"pets.#count" ascending:NO]
This can't be applied directly though (to a fetch request), you need to execute the fetch and then run the sort on the result array (sortedArrayUsingDescriptors:).
If you want to sort while fetching, you would need to add a (non-transient) attribute to the entity which holds the count number.
Change the format of your key to #"pets.#count"
Take a look at this answer https://stackoverflow.com/a/20317087/2567126
Basically you create a persistent property on the NSManagedObject and update the property count any time a child object is added or removed from the relationship.
I also need to do the same thing but in Swift. I just to follow:
Fetch the request from CoreData without 'count' key-value.
I performed sort (high order function) on the fetched result.
allCategory.sort { (category1, category2) -> Bool in
return category1.tasks?.count > category2.tasks?.count
}
This is working for me.

NSPredicate/TableView only displays one item, why?

I'm working on an app where I have two entities, Post <-->> StreamType. When I create posts I assign StreamType:s like this:
// streamType == one of my default streamTypes
[post addStreamTypesObject:streamType];
My predicate for finding posts that have a certain StreamType looks like this:
predicate = [NSPredicate predicateWithFormat:#"ANY streamTypes.type = %#", [NSNumber numberWithInt:self.pageType]];
I'm not sure why this happens. Any ideas?
Edit
What I basically want is to fetch all Posts that have the right StreamType. Seemed after all that my fetchrequest only returns 1 item from the database. So probably nothing wrong with my tableview.
Edit 3
The problem was with my relationship, should be many-to-many, not one to many. Therefore it only returned one Post item.
First: test if the other code is ok. Simply, remove the predicate (comment the setPredicate line). You should see ALL objects in your tableview.
Right?
Second: check if self.pageType is set correctly. I don't see in your code how you set self.pageType
Test your predicate, add an NSLog like this and check if the result is ok:
NSLog(#"ANY streamTypes.type = %d", self.pageType);
Third:
As far as I understand, you have this situation:
One Post has only one stream type
One stream type has multiple posts.
The ANY keyword is used in situations where you want, for example, obtain all stream types where a particular condition is satisfied at least one time. for example (assuming you have a "content" instance variable on your post, containing the text of the post)
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY post.content CONTAINS[cd] %#", #"aWord"];
In this case, you will obtain all stream types in which there are posts containing "aWord" particular word in the text.
Your case is simpler. I think that you should simply use:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"streamTypes.type = %d", self.pageType];
Try and let me know
I was just having the same issue using Core Data with Swift and just wanted to add this answer in incase anybody else is having a similar issue.
This was my NSPredicate code:
let predicate = NSPredicate(format: "routine == %#", self.selectedRoutine)
Routine holds multiple exercise objects which I was trying to return. An exercise can only have one routine but a routine can have many exercises.
Turns out I'd forgotten to select 'To Many' as the relationship type for the exercises relationship in my routine entity using the Data Model inspector. It was set as 'To One'.

CoreData NSPredicate with two one-to-many relations

I have a CoreData model data structure like this for restricting information based on the group a user belongs to:
Category <->> Information <->> Groups.
I have an NSSet of UserGroups objects. I want to be able to filter categories based on the NSSet of Group objects, such that if a category does not contain any pieces of information which have any groups inside my NSSet, they will not be returned by my predicate.
For information I can do
[NSPredicate predicateWithFormat:#"ANY groups in (%#)",groups];
For categories I've tried the following with only a crash:
[NSPredicate predicateWithFormat:#"ANY information.groups in (%#)",groups];
But I need to write a predicate at the Category level. I am programming under the assumption that the information in my data set is sufficiently large that I cannot pull them all of them out and process them to find my Categories. I want to create predicate that will fetch only the categories that are relevant to the user based on his/her groups.
Thanks for your help!
The following predicate on Category should work (assuming that information is the to-many relationship from Category to Information):
[NSPredicate predicateWithFormat:#"SUBQUERY(information, $i, ANY $i.groups in %#).#count > 0",groups];
Alternatively, you could use the inverse relationships:
// your set of Group objects:
NSSet *groups = ...;
// all Information objects that are related to any group object:
NSSet *information = [groups valueForKey:#"information"] ;
// all Category objects that are related to any information object:
NSSet *categories = [information valueForKey:#"category"];
which can be combined to
NSSet *categories = [groups valueForKeyPath:#"information.category"];
A disadvantage of this alternative solution might be that it creates also the intermediate set of groups in memory.

Core Data with to Many relationship - Creating a NSPredicate with ALL in SUBQUERY

My object graph looks like this
SnapShot -->> Pane --> ManagedImage
I'm trying to find a SnapShot that has the exact ManagedImages contained with in a set.
The code I've got now returns an Array of SnapShots that have one or more of the ManagedImages that are in the set. I then search through the Array to find the correct SnapShot but I'm guessing it would be much faster to filter in the Subquery
With an NSPredicate how can I get the unique SnapShot that has ALL of the ManagedImages that are in the set?
Here's my code
mySet = ... // A unique set of (usually 3) managedImages that I'm trying to find a snapShot for
NSFetchRequest *request = ...
request.entity = [NSEntityDescription entityForName:#"SnapShot" inManagedObjectContext:[self managedObjectContext]];
// Want this to work but sends an exception
//request.predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(self.panes, $pane, ALL $pane.managedImage IN %#).#count != 0", mySet];
// Using this
request.predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(self.panes, $pane, $pane.managedImage IN %#).#count != 0", mySet];
A good rule of thumb is that if you already have managed objects in hand, you don't fetch but instead walk the relationships from the managed objects you have to the managed objects you want.
So, your relationship graph probably actually looks like this:
SnapShot <-->> Pane <--> ManagedImage
or maybe:
SnapShot <<-->> Pane <<--> ManagedImage
Since you have a set of ManagedImage objects all you have to do is walk the keypath of pane.snapShot or panes.snapShots to find the SnapShot objects associated with each ManagedImage object. Then you just extract the unique SnapShot objects.
In the first case, the matter is trivial because of the one-to-one relationship path of
ManagedImage-->Pane-->SnapShot
In the second case, you will need to first get all the unique SnapShot objects:
NSSet *shots=[aMangedImageObj valueForKeyPath:#"distinctUnionOfSets.panes.snapShots"];
... for each ManagedImage instances and then merge all the sets with setByAddingObjectsFromSet: or a similar method to produce a single set of unique objects.
Fetches should be used to find the first objects in graph that you need but once you have the objects, you don't fetch but walk the relationships. Otherwise, there is not much point to having relationships in the first place.

Resources