sorting

The sort filter lets you sort the output by various features you are interested in. We suggest using sort where possible: it is easy to use, there is no performance penalty, and it can yield greater insight into the output of a search.

More specifically, some filters can be sorted. This means two things:

  1. Such a filter has an associated count in any position that the filter matches.
  2. The games in the output pgn file are sorted in descending order by the maximum attained value of the count of that filter in that game
For example, the following CQL file will sort games with at least four queens in the same position by the maximum number of queens in such a position:
   cql (input i.pgn)
   sort
     countsquares 4 16 [Qq]
That is because the filter
countsquares 4 16 [Qq]
will match a position with at least four queens. Whenever it does, the filter's count is that number of queens. The sort keyword indicates that all games should be sorted by the maximum value which that filter attained on a position that it matches.

Counts associated with a filter

Some filters can be counted (sometimes we say it is countable or sortable). What is counted exactly depends on the nature of the particular filter. The documentation for the filter will say whether it is countable (not all filters are countable, and many are only countable if they use an explicit range.

Most filters that are countable are only countable when a range immediately follows the name of the filter. For example, attack is countable only when the word attack is followed by a range:

    attack (A _)
is true in a position if a white piece attacks an empty square, but it is not countable. Thus,
    sort attack (A _) ; ERROR
would give a syntax error, because the argument to sort is not countable.

However, when attack is followed by a range, then the attack filter is countable. It counts the number of attacks: the number of attacks by a white piece on an empty square.

    sort attack 10 1000 (A _) ; LEGAL
would sort the output by the maximum number of total attacks by a white piece on an empty square.

examples of counts

We discussed above that any count filter (a set filter followed by a range) can be counted. The filters in the following table are also all countable only when their name is followed by a range:
countable filter: initial syntaxwhat is being counted
attack rangeattacks
countsquares range squares in its argument
square range squares matching its arguments
next rangelength of the longest sequence of positions matching its arguments
previous range length of the longest sequence of positions matching its arguments
next2 range length of the longest sequence of positions matching its arguments, but only every other position starting with current position is counted
previous2 range length of the longest sequence of positions matching its arguments, but only every other position starting with current position is counted
next* range occurrences of its argument following current position
previous* range occurrences of its argument following current position
power range total material power of its argument
powerdifference range difference in material power between its first and second arguments
movenumber range move number of the current position
year range year of the current game
elo range elo of the specified player
ray range rays described by its arguments
matchcount range positions that are matched in a game

Sorting

A game can be sorted by any countable filter. Just precede the filter either with word sort or with sort and then a quoted documentation string.

For example:

    sort attack 10 1000 (A _) ; sort by attacks on empty squares
    sort "Number of attacks" attack 10 1000 (A _)  ; Same as above, but clearer output
The second version will annotate the output games with the documentation string and value. (The first version will use a cryptic internal variable name instead of a descriptive annotation).

We suggest always using documentation strings with sort . We find it makes the output easier to read and understand.

Multiple sorts

You can sort by multiple filters. When multiple filters are sorted, the output is sorted by each filter in the order the filters occur in the CQL file. For example, to following CQL file sorts by the maximum number of White pins in a position, and then by year. That is, if two game have the same number of pins, then they will be output together with the most recent first:
    cql(input i.pgn)
     sort "Number of pins" ray 1 10 attack (A a k)
     sort "Year" year 1 2020

Sorting by matchcount

To sort by the matchcount (the number of positions in the game that matched, assuming this number lies within the range of the matchcount, preface the matchcount CQL parameter with sort as in:
  cql(input i.pgn sort matchcount 10 1000)
   check

This CQL code will find output games with at least 10 checks, sorted by the number of such checks.

sort matchcount does not take a documentation string:

    cql(input i.pgn
    sort "Matches" matchcount 10 1000); ERROR
Likewise, matchcount cannot be sorted by minimum value: sort min matchcount 1 10 is illegal.

See matchcount documentation for more information on matchcount.

sorting by minimum in instead of the maximum

By default, sort sorts games by the maximum attained value of its argument filter. However, if the keyword sort is followed by the word min, then games are sorted by the minimum attained value of its argument filter. In this case, the games are sorted in ascending order instead of descending order.

An example of this usage is in the file pinstalemate.cql. The line

sort min "material" power 1 1000 [Aa]
will sort the games in order of increasing material. (Actually, in this example there is another sort by number of pins that comes first, so the games will be sorted first by descending order of the number pins, and then by ascending order by the material.

If you want to clarify that you want the default behavior of sorting by descending order of the maximum value, you can use sort max instead of the lone keyword sort. sort max is just a synonym for sort.

sorting relation filters

A relation filter cannot be sorted directly, but you can sort by maximum value of any LCA parameter (a parameter whose name begins with the characters "lca") by prefacing the name of the LCA parameter with sort or with sort documentation-string. In this case, the output is sorted by the maximum value of that LCA parameter in any matching target.

See the relation sort documentation for more detail and examples.

Pitfalls in using sort

There are two common pitfalls to watch out for when using sort:
  1. Anything used with sort must have a range even when it seems unnecessary. You will get a syntax error if you try sort movenumber instead of say sort movenumber without a range.
  2. A more insidious error that can arise when using sort is when you sort a filter in a position that isn't actually matched. For example, suppose you want to sort stalemates by the number of pieces in the stalemate:
    	cql(input i.pgn) ; WARNING...
    	 sort "Number Pieces" countsquares 1 32 [Aa] 
    	 stalemate
    The above CQL will output all games that end in stalemate, but will sort the games by the maximum number of pieces that arise at any time in that game. This of course is the same as the number of pieces in the initial position of the game, and is not what you want.

    To sort by the number of pieces in the stalemate itself you have to put the sort filter after the stalemate filter:

    	cql(input i.pgn) ; BETTER
    	 stalemate
    	 sort "Number Pieces" countsquares 1 32 [Aa]
    	
    Now the sort filter will only ever be evaluated in positions that are stalemates, so the sorting will be by the number of pieces in the stalemate position itself.

    Examples