What is SUBQUERY?

One of the lesser known bits ofNSPredicateis theSUBQUERY()function. Thedocumentation for a subquery expressionexplains a little bit about what’s going on, but it takes a while to understand when it’s really useful.

Let’s break it down.

The Syntax

A subquery expression takes 3 arguments:

A collection

A variable name

A predicate

The Collection

The collection can be one of the two standard Cocoa collection:NSArrayandNSSet(or some subclass of them). This collection can be hard-coded in, or it can be the result of another expression (like a keyPath expression or the like). A hard-coded collection would look like:

SUBQUERY(%@, …

A keyPath expression would look like:

SUBQUERY(contents, …

With the stipulation that[self contents](orself.contentsif you prefer) must return anNSArrayorNSSet.

The Variable

TheSUBQUERYis going to iterate over the collection, gathering certain objects. We need a way to represent what each item in the collection is, and for that we use the variable.

Variables in anNSExpression(orNSPredicateformat string) take the form$identifier, whereidentifieris a valid C-style identifier. Most examples ofSUBQUERYgenerally use$xas the variable. It’s short and to-the-point.

It’s the second argument in the expression:

SUBQUERY(contents, $x, …The Predicate

A predicate, as we know, is a statement that evaluates to true or false. In the case of the subquery, the predicate will be evaluated for each object (represented by the variable) in the collection. If the predicate returns true, then that object will be included as part of the resulting collection.

SUBQUERY(contents, $x, $x.property = ‘foo’ and $x.number = 42)TL;DR

ASUBQUERY()expression is the functional equivalent of doing this:

NSPredicate * p = [NSPredicate predicateWithFormat:@"property = ‘foo’ and number = 4"];NSArray * results = [[self contents] filteredArrayUsingPredicate:p];

If this were expressed as a subquery in a predicate, it would be:

NSPredicate * p = [NSPredicate predicateWithFormat:@"SUBQUERY(contents, $x, $x.property = ‘foo’ and $x.number = 42) …"]; //with some operation to use the resulting collection in a comparison or somethingSo what?

Now that we get what the various bits of a subquery expression are, let’s ask the real question: when is this ever useful?

To be honest, the answer to this is “not often”. However, when you need it, it’s incredibly useful.

Rule of thumb

The general rule of thumb on when you should consider usingSUBQUERYis this:

If you have a collection (A) of objects, and each object has a collection (B) of other objects, and you’re trying to filterAbased on some varying attributes (at least 2) of the objects inB, then you should probably be usingSUBQUERY.

Example

Let’s say you have a bunch ofProjectobjects, and eachProjecthas a bunch ofToDoitems. AToDoitem has acompletionDate(anNSDate) and auser(a name). You want to find all projects that have a todo item that was completed by Joey (socompletionDateis notnilanduseris “Joey”). We’re going to display these in a “Joey’s Recent Projects” group (or something).

Our first reaction might be a predicate that usesANYin there, like:

ANY todos.completionDate != nil AND ANY todos.user == joey

Unfortunately, that would give us projects that have at least one completedToDoand that has aToDowhoseuseris Joey. However, they don’t have to be the sameToDo.

The proper predicate is:

SUBQUERY(todos, $todo, $todo.completionDate != nil AND $todo.user = ‘Joey’).@count > 0

This predicate will be evaluated against eachProject. First, we’ll get the collection ofToDoobjects by evaluating thetodokeyPath on theProject. Then for each item ($todo) in the array ofToDoobjects, we’re going to check and see if that object’scompletionDateis non-nil and if that object’s user is"Joey". If that’s true, then it’ll be added to the resulting collection.

When theSUBQUERYcompletes, we’ll have an array ofToDoitems that were completed by Joey. At this point, we retrieve the number of items in that collection (via the@countkeyPath) and see if it’s greater than 0. If it is, then the correspondingProjectwill be added to the final array. In this manner, we can retrieve all Projects that have ToDos completed by Joey.

夫妇一条心,泥土变黄金。

What is SUBQUERY?

相关文章:

你感兴趣的文章:

标签云: