Search code examples
objective-ccore-datanspredicateone-to-manynsexpression

Core Data - Get objects filtering data using the exact elements of an array


I have 3 entities: object, object_tag and tag, and i have to get just the objects matching an array of tags by its id, but not just a tag, but that exactly matches all the tags in the array. If the array has 3 tags, return all the objects that have these 3 tags and not more neither less.

The entities are like:

object
{
    id
    name
    -----
    tags <--->> object_tags
}

object_tag
{
    id
    id_object
    id_tag
    -----
    object <---> tags
    tag <---> objects
}

tag
{
    id
    name
    -----
    objects <--->> object_tags
}

Using predicates and/or expressions how can i get what i want?. I've tried many ways, but all I've gotten have been objects that include any of the tags in the array, but not all the tags at once.

EDIT 1:

Sorry, i forgot to clarify something about relationships. The entities object and tag points to the intermediate entity object_tag, that contains object-tag pairs.


Solution

  • The following predicate for the object entity should work:

    NSArray *tagIds = @[ @1, @4, @7 ]; // Your set of tag ids
    [NSPredicate predicateWithFormat:@"(object_tags.@count == %d) AND (SUBQUERY(object_tags, $x, $x.tag.id IN %@).@count == %d)",
                          tagIds.count, tagIds, tagIds.count];
    

    where object_tags is the to-many relationship from object to *object_tag*, and tag is the to-one relationship from *object_tag* to tag.

    If you simplify your object model to a many-to-many relationship between object and tag (as suggested by David Ravetti in a comment), then the predicate would look like this:

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

    If you only want to check that the object has all tags from the given array (but might have more tags), then you can simplify the query to

    [NSPredicate predicateWithFormat:@"SUBQUERY(tags, $x, $x.id IN %@).@count == %d",
                          tagIds, tagIds.count];