How to create a Core Data predicate to test that a relation contains all given objects?
Asked Answered
Q

2

11

Setup:

I have a Core Data object A that has a to-many relation to B. Call the relation "items". So, a.items returns all B-s associated with A.

Now, I have a manually composed NSSet "itemSet" of B objects.

I want to do the following:

return all A objects whose "items" relation exactly matches itemSet

How do I construct a predicate for that? I’ve tried this:

NSPredicate *predicate = [NSPredicate predicateWithFormat:
                          @"(ALL items in %@)", itemSet];

But that just gives me Unsupported predicate (null).

This:

NSPredicate *predicate = [NSPredicate predicateWithFormat:
                          @"(items in %@)", itemSet];

tells me unimplemented SQL generation for predicate. Interesting, but not helpful.

So what’s the right way to filter the relation with a set?

Querist answered 26/10, 2012 at 10:11 Comment(0)
V
20

The following predicate could work:

[NSPredicate predicateWithFormat:@"(items.@count == %d) AND (SUBQUERY(items, $x, $x IN %@).@count == %d)",
                      itemSet.count, itemSet, itemSet.count];

The predicate checks first that the number of items is equal to the size of the given itemSet, and then checks that the number of items which are member of itemSet is also equal to the size of itemSet. If both are true, then items must be equal to itemSet.

Voiceful answered 26/10, 2012 at 11:46 Comment(3)
Interesting. This works. Where can I learn more about SUBQUERY? Official predicate docs are kinda sparse on it.Querist
@Jaanus: I'm afraid that I don't have a better reference. I learned it mostly by trying to understand other people's answers at SO.Voiceful
I was having issues related to insufficient use of parentheses. Thanks for the answer, @MartinRHarlequin
A
0

Have you tried:

NSPredicate *predicate = [NSPredicate predicateWithFormate:@"items == %@", itemSet];

Alternatively, pull out a subset with a simpler predicate and filter them outside of the fetch request. i.e.

  1. Set a predicate for the number of items in the relationship to the be the same as the number of items in your comparison set.
  2. Fetch the results
  3. Filter these results to show only the ones where the sets contain the same items.
Aeriela answered 26/10, 2012 at 10:27 Comment(2)
Yes. That gives another exception: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'to-many key not allowed here'Querist
Manually filtering (first fetch by count, then filter) is definitely an option, but creates unnecessary CD traffic. I’m wondering if this is doable with a predicate at all or not.Querist

© 2022 - 2024 — McMap. All rights reserved.