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:.
Related
I have two class, one is named Folder, the other is named Entry.
In my data model, a folder would contain multiple entry, and an entry can be contained by different folders.
So each folder has a folderID to identify itself, and a relationship named entries which is used to contains Entry instances. And each entry has an inverse relationship named superFolders which points back to the folder contains it.
Now is my question. Here I get a folderID. I want to use this and NSFetchRequest to fetch all the entries contained by this special folder in Core Data. The following is the main code:
let fetchRequest = NSFetchRequest<Entry>(entityName: "Entry")
fetchRequest.predicate = NSPredicate(format: "(ANY folder in %K).folderID = %i", #keyPath(Entry.superFolders), folerID)
The format string in above code is incorrect, but it mostly explain what I mean. Since superFolders property is actually a NSSet in Core Data, I can't use superFolders.folderID == %i as the judge condition. What exactly I want is to find all the entry whose superFolder property contains any of element which its folderID property match the given folderID.
So is that possible to use NSPredicate to express what I mean to Core Data ?
You want:
let fetchRequest = NSFetchRequest<Entry>(entityName: "Entry")
fetchRequest.predicate = NSPredicate(format: "ANY superFolders.folderID = %#", folderID)
See the NSPredicate reference
If you had a more complicated match on the superFolders relationship, you would need to use SUBQUERY, but in this case a simple ANY predicate works fine.
I have a simply BWWishlistBook entity which has a property BWBook. One BWBook may belong to many BWWishlistBooks and one BWWishlistBook always have only one BWBook.
Now I have a NSFetchRequest for BWWishlistBook:
let fetchReguest = NSFetchRequest(entityName: "BWWishlistBook")
I need to avoid to fetch wishlist books with the same book inside. is it possible? How to setup predicate then? BWBook has an unique id property.
In MySQL this is DISTINCT. What is in CoreData?
-returnsDistinctResults
/* Returns/sets if the fetch request returns only distinct values for the fields specified by propertiesToFetch.
This value is only used for NSDictionaryResultType.
Defaults to NO. */
#available(iOS 3.0, *)
public var returnsDistinctResults: Bool
Make a fetch on BWBook with a predicate of wishlist.count != 0.
Now you will have a list of books (all distinct) and all of them will have at least one wishlist associated with them.
Now you can just display the first wishlist from each book and you will have a list of wish lists with distinct books.
If you know which BWBook for which you want to fetch BWWishlistBook the easiest (and most performant way) is the fetch it by the relationship on the BWBook.
Maybe something along the lines of
wishlist = bwBook.bwWishlistBooks
OK so looked for answers and I'm probably making some assumption that others aren't and thats why the solution is working for them.
Problem:
Lets say I have an Entity called DogOwner. A dog owner can have multiple Dogs and each dog can have multiple Wearables. Note that all relationships are optional and one-to-many (inverse many-to-one).
Aim:
Search all the dog owners that own a dog that has a Wearable type GpsTracker and contains id of 898764.
My approach:
Fetching the entity DogOwner with the following predicate
NSPredicate(format: "(dog.wearable.type == %#) AND (dog.wearable.external_id CONTAINS[cd] %#)", theType, theIdentifier)
I get an unimplemented SQL generation for predicate error.
Tried -> with ANY (Nested core data fetch)
Tried other stuff present on the Internet. Nothing as far I have seen works, I assume that the problem is deeper down given that nested tree structured graph is being successfully queried by others using the dot notation.
While subquery is a feasible way to do this it is exceedingly complicated, and unnecessarily so. Why not simply search for the specific Wearable and get the owner via the convenient to-one relationships?
// fetch wearable with id x
let owner = wearable.dog.owner
I think you need to use SUBQUERY, if only because you need to test both conditions simultaneously:
NSPredicate(format: "SUBQUERY(dog, $d, SUBQUERY($d.wearable, $w, $w.type == %# AND $w.external_id CONTAINS[cd] %#).#count > 0).#count > 0", theType, theIdentifier)
Or "fetch DogOwners having more than 0 dogs having more than 0 wearables matching the criteria".
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'.
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.