A Brief Example (again)!

So, we're trying to build a crossword puzzle. We've got a list of words appropriate for our difficulty level, and a list of the lengths of the gaps of in the puzzle:
	ArrayList<String> words = dict.getWords(Puzzle.MEDIUM);
ArrayList<Integer> gaplengths = puzzle.getGapLengths();

And we outlined a query to find pairings of words with their lengths:
	List<Object[]> matches = selectAll(String w : words, 
                                   Integer i : gaplengths |
                                   w.length() == i);

To break the query down into its components - firstly, selectAll is a keyword we have added; it indicates that what follows in brackets is a JQL query. Following that, we come to:
String w : words, Integer i : gaplengths
These are Domain Variable Declarations. Domain Variables are variables we declare for use in a query, which will be used to represent ALL possible values from the input collection, which is declared to the right of the colon. Our first domain variable is called 'w', it is of type String, and it takes its values from the collection 'words'.
Finally, separated from the domain variable declarations by a vertical bar '|', we find our query expression:
w.length() == i
This expression is tested for all possible pairings of w and i from the input sets, and if this expression evaluates to true, the pairing is added to the result set for the query.
Queries can be far more complex than this - an arbitrary number of domain variables can be declared, and query expressions can consist of many boolean expressions conjoined with "&&", the boolean AND. To build on our example, now our crossword's target audience has a superstition about words that start with the letter 'R' - none of these are allowed in our crossword. Easily accounted for!
	List<Object[]> matches = selectAll(String w : words, 
                                   Integer i : gaplengths |
                                   w.length() == i &&
                                   w.charAt(0) != 'R' );
We can keep adding conditions to the query expression as we like, and it's here that important advantage of JQL comes into play - the query optimiser. With complex queries, especially those with many domain variables, the ordering of the evaluation of the smaller clauses of the query expression can greatly influence the speed at which we can evaluate the query.

Join Types

When talking about how we evaluate queries, we swipe terminology from the world of databases and refer to operations between pairs of sets as joins between sets. One way that we can accomplish the above query is using a simple nested loop join. This is pretty much what it sounds like - a loop through words, and a loop through gaplengths in the body of that loop. Here's one now:
List<Object[]> matches = new ArrayList<Object[]>();
for(String w : words){
  for(Integer i : gaplengths){
    if(w.length() == i)
      matches.add(new Object[i,w]);
  }
}
This solution springs naturally to mind and is easy to program and to understand. However, it's important to note that this is not necessarily the best way to do this at all.
The Hash Join is a less obvious, less easily programmed but far more efficient join methodology that can be extremely beneficial in the situations in which it can be used. The basic principle is, for joins on equality, we can build a hash table which has as keys the attribute we are joing on, and for each key is a list of objects which have that attribute. A possible hashtable built for a hash join for our crossword example could hash from an integer to a list of words which are as long as that integer. Once we've built our hashtable, we can then loop through the set of objects we didn't build the hashtable from, looking up in the hash each value we are testing for equality. Code-wise this might look a bit like this (generic parameters omitted for clarity):
List matches = new ArrayList();
Hashtable index = new Hashtable();
for(String w : words){
  List l = index.get(w.length());
  if(l==null){
    l = new ArrayList();
    index.put(w.length(),l));
  }
  l.add(w);
}
for(Integer i : words){
  for(String w : index.get(i))
    matches.add(new Object[i,w]);
}
Hash joins have some disadvantages, however - they are harder to program than nested loop joins, they take longer to code, they're less easily understood, and which set should be keyed on and which provides the values can change depending on runtime context. JQL removes all these drawbacks - by abstracting away the specifics of the query evaluation, the query evaluator can decide if a hash join can be done, how to do it, and the specifics of how the table should be built. It can decide, at runtime, which set is the best to be keyed on, and can cope with changes in set sizes - even fall back to a nested loop join if the input sets are small enough that the time taken to build a hash table outweighs the benefit it provides.

JQLMail

For a more substantive example, we created JQLMail - a mail client written using JQL. It's a simple proof of concept - not much to look at, but it demonstrates the usability of JQL in a reasonable sized application.

JQLMail stores emails internally as a completely flat collection of Message objects - all the folders listed in the screenshot are triggers for queries. Messages can be tagged as well, and this allows for folder-like functionality. This is very similar to the functionality offered by Google's GMail service.

Folders on the left hand side are stored queries - mostly they simply select all messages tagged with a certain tag (clicking on the "Sent" folder simply selects all messages with the "Sent" tag, for example), but they can be any query you like. One could store a query for messages tagged with a certain tag, from certain senders, before a certain date, for example.