29

There appears to be zero documentation about the SUBQUERY keyword from Apple and I can't find a simple explanation about it on SO or on Google. It's a conspiracy! ;)

Please, could someone from the inner-circle please just provide a quick explanation of its syntax so I can use it?

SUBQUERY(Bs, $x, $x IN %@)

Thanks

János
  • 32,867
  • 38
  • 193
  • 353
ApeOnFire
  • 649
  • 2
  • 6
  • 11

3 Answers3

44

And for people who don't quite get what the documentation is saying, a SUBQUERY is essentially this:

SUBQUERY(collection, variableName, predicateFormat)

And could (simplistically) be implemented like this:

id resultingCollection = ...; //a new collection, either a mutable set or array
NSMutableDictionary * substitutions = [NSMutableDictionary dictionary];
NSPredicate * p = [NSPredicate predicateWithFormat:predicateFormat];
for (id variable in collection) {
  [substitutions setObject:variable forKey:variableName];
  NSPredicate * filter = [p predicateWithSubstitutionVariables:substitutions];
  if ([filter evaluateWithObject:collection] == YES) {
    [resultingCollection addObject:variable];
  }
}
return resultingCollection;

So in a nutshell, a SUBQUERY is basically taking a collection of objects and filtering out various objects based on the predicate expression of the SUBQUERY, and returning the resulting collection. (And the predicate itself can contain other SUBQUERYs)

Example:

NSArray * arrayOfArrays = [NSArray arrayWithObjects:
                           [NSArray arrayWithObjects:....],
                           [NSArray arrayWithObjects:....],
                           [NSArray arrayWithObjects:....],
                           [NSArray arrayWithObjects:....],
                           [NSArray arrayWithObjects:....],
                           [NSArray arrayWithObjects:....],
                           nil];
NSPredicate * filter = [NSPredicate predicateWithFormat:@"SUBQUERY(SELF, $a, $a.@count > 42)"];
NSArray * filtered = [arrayOfArrays filteredArrayUsingPredicate:filter];
//"filtered" is an array of arrays
//the only arrays in "filtered" will have at least 42 elements each
Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
  • 1
    Your NSPredicate won't compile: caught "NSInvalidArgumentException", "Unable to parse the format string "SUBQUERY(SELF, $a, $a.@count > 2)"" – cfischer May 14 '15 at 14:49
  • 2
    @cfisher Minor correction: That's a runtime error, not compilation error. (Not trying to be a jerk.) – sudo Aug 30 '15 at 03:05
21

This is what a subquery evaluates to. (Found from this mailing list thread, the #1 hit for “NSPredicate subquery” in Google.) That bit of documentation also explains how the predicate format string syntax relates to it.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • Damn! I can't believe I missed that. Thanks a lot. – ApeOnFire Sep 28 '10 at 09:09
  • How do you know what letter to use? Some places I see $x some places I see $s, some I see $a, and I can't see by context any diff between $x and $s. – jpswain Jan 14 '13 at 07:06
  • 1
    @orange80: The variable name is your choice. See the documentation I linked to in my answer. If you know Python, it's like saying `[x for x in collection if (some condition involving x)]`. As far as I know, the variable name doesn't even have to be a single letter. – Peter Hosey Jan 14 '13 at 07:11
  • 2
    Just a note: at this moment THIS post is google's first hit for "nspresicate subquery" :) +1 for the link – Pedro Borges Oct 04 '14 at 13:43
1

Subquery represents a predicate (third argument - $x IN %@) that is evaluated on all objects (second argument - $x - it's like a variable name in foreach) of a relationship (first argument - Bs). Similarly to regular query returns a list of objects.

I see in many places that people use $x almost dogmatically, but $object in objects relationship makes perfect sense as well (or $city in cities...) :)

I've written a blog post about SUBQUERY some time ago. You can check it here.

Maciek Czarnik
  • 5,950
  • 2
  • 37
  • 50