What is SUBQUERY?
2013-08-20 11:48
204 查看
What the heck is SUBQUERY?
One of the lesser known bits ofNSPredicateis the
SUBQUERY()function.
The documentation
for a subquery expression explains 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: NSArrayand
NSSet(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](or
self.contentsif
you prefer) must return an
NSArrayor
NSSet.
The Variable
The SUBQUERYis 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 an
NSExpression(or
NSPredicateformat
string) take the form
$identifier, where
identifieris
a valid C-style identifier. Most examples of
SUBQUERYgenerally
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 includedas part of the resulting collection.
SUBQUERY(contents, $x, $x.property = 'foo' and $x.number = 42)
TL;DR
A SUBQUERY()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 something
So 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 using SUBQUERYis
this:
If you have a collection (
A) of objects, and each object
has a collection (
B) of other objects, and you’re trying
to filter
Abased on some varying attributes (at least
2) of the objects in
B, then you should probably be using
SUBQUERY.
Example
Let’s say you have a bunch of Projectobjects, and each
Projecthas
a bunch of
ToDoitems. A
ToDoitem
has a
completionDate(an
NSDate)
and a
user(a name). You want to find all projects that
have a todo item that was completed by Joey (so
completionDateis
not
niland
useris
“Joey”). We’re going to display these in a “Joey’s Recent Projects” group (or something).
Our first reaction might be a predicate that uses
ANYin
there, like:
ANY todos.completionDate != nil AND ANY todos.user == joey
Unfortunately, that would give us projects that have at least one completed
ToDoand
that has a
ToDowhose
useris
Joey. However, they don’t have to be the same
ToDo.
The proper predicate is:
SUBQUERY(todos, $todo, $todo.completionDate != nil AND $todo.user = 'Joey').@count > 0
This predicate will be evaluated against each
Project.
First, we’ll get the collection of
ToDoobjects by evaluating
the
todokeyPath on the
Project.
Then for each item (
$todo) in the array of
ToDoobjects,
we’re going to check and see if that object’s
completionDateis
non-nil and if that object’s user is
"Joey". If that’s
true, then it’ll be added to the resulting collection.
When the
SUBQUERYcompletes, we’ll have an array of
ToDoitems
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 corresponding
Projectwill
be added to the final array. In this manner, we can retrieve all Projects that have ToDos completed by Joey.
相关文章推荐
- Find Out What Your Entity Framework Query Is Really Doing
- Only one expression can be specified in the select list when the subquery is not introduced with EXI
- Only one expression can be specified in the select list when the subquery is not introduced with EXI
- Only one expression can be specified in the select list when the subquery is not introduced with(求助)
- Packet for query is too large
- What is a pure virtual function?
- What Is a Glue Code?
- What is a Perforce "shelved" fil…
- what is PCI(Peripheral Component Interconnect)PCI是什么
- what is idl file?
- What is English?
- What is a BSP Application?
- [译文]What is RCU, Fundamentally?
- [置顶] What is the difference between Category and Class Extension?
- What is a First Chance Exception
- ZOJ 3785 What day is that day?(最小循环节)
- what is apple lossless audio coding(ALAC)
- org.hibernate.hql.ast.QuerySyntaxException: User is not mapped [from User] 异常总结
- what is this api
- mysql创建视图 :View's SELECT contains a subquery in the FROM clause