Core Data fetch request predicate filtering across one
I have the following model graph:
+-----------+ +-----------+
| Container | | Group |
+-----------+ +-----------+
| groups |<-->>| container |
+-----------+ +-----------+
^ ^
| |
+-----------+ +-----------+ +-----------+ +---------+
| Owner | | ToyBox | | ToyType | | Item |
+-----------+ +-----------+ +-----------+ +---------+
| toyBox |<--->| owner | | items |<-->>| toyType |
+-----------+ +-----------+ +-----------+ +---------+
In a UITableView I am to display a list a list of Items. In this case I only want to show the items that belong to a particular owner. To do this I will use NSFetchedResultsController to display the items. This means that I need create an NSFetchRequest with an appropriate NSPredicate to give to the NSFetchedResultsController.
Attempting to use a keypath predicate causes an exception due to the parent entities. This appears to be an Apple bug or decision not to support. A radar has been filed. Additionally, I do not wish to flatten the entities.
So that left me with an attempt to do this with SUBQUERY() as follows:
NSFetchRequest *itemsFetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Item"];
NSPredicate *itemsPredicate = [NSPredicate predicateWithFormat:@"SUBQUERY(toyItem, $g, SUBQUERY($g.container, $c, SUBQUERY($c.owner, $o, $o = %@).@count > 0).@count > 0).@count > 0", ownerObject];
This results in the following exception:
Can't have a non-relationship collection element in a subquerySUBQUERY($c.owner, $o, $o == <MLMOOwner: ...
I realize that because the relationship is one to one between Owner and ToyBox that there isn't a collection returned and this is the problem. So my questions are:
1) Is there a way to force the return of a collection for to-one relationships in the subquery?
2) If not is there another way to go about creating this predicate for the fetch request?
Seems like it should be a lot simpler to do what you want. If you want all the items with a given owner, start with the owner and use a key path to get the associated items. You don't need a predicate. If you've got 10 jars of peanuts and you want to retrieve the peanuts in jar 2, you don't start with the set of all peanuts and then filter them according to their jar, right? You first get jar 2, and then you look at the peanuts it contains. So do this:
NSSet *groups = [ownerObject valueForKeyPath:@"toyBox.groups"];
That gives you all the groups owned by ownerObject
. You can't just get all the items using a single key path because the Group
entity doesn't have an items
attribute. You could make life easier for yourself by giving Group
an items
accessor, even if it only returns an empty set. That would let you do this:
NSSet *items = [ownerObject valueForKeyPath:@"toyBox.groups.items"];
If you don't want to add an items
attribute to Group
, you'll have to filter the set groups
from the first example to pick out just those objects that are ToyType objects. You can then use the key path @"items"
on that set to get the list of items that you want.
You have to be a bit careful with entity inheritance. You've just seen how it can make fetching a little more complicated. It also has implications for the way your data is stored, namely that all instances of sub-entities are stored in the same table. So, if you have a dozen entities derived from Group
(such as ToyType
), all the instances of all those entities are stored together.
上一篇: 使用SUBQUERY进行NSPredicate不起作用
下一篇: 核心数据获取请求谓词过滤