Quick Start

What you need

CQLi searches chess games stored in PGN files using the Chess Query Language (CQL). This page walks you through running one query from start to finish and then lists short examples you can adapt.

Your first query

The command below finds decisive games that lasted more than 100 moves. Replace input.pgn with your PGN file, output.pgn with the file to write, and cqli with the executable for your platform: cqli on Linux, cqli-arm64 (Apple Silicon) or cqli-x86_64 (Intel) on macOS, and cqli.exe on Windows.

cqli -i input.pgn -o output.pgn -cql 'terminal result 1-0 or result 0-1 movenumber > 100'

CQLi checks every position of every game against the query and writes each game with a matching position to output.pgn. A position matches when all of the query's filters match it: here terminal matches only the final position of a game, result 1-0 or result 0-1 requires a decisive result, and movenumber > 100 requires more than 100 moves. In a sample of one million games from lichess.org, 356 games match; the links below open ten of them at the matching move.

356 games out of 1,000,000 (Sample matches: #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 )

Viewing the results

The output file is an ordinary PGN file that can be opened in any chess program that reads PGN, such as ChessBase, Scid, or the Lichess analysis board. CQLi marks the matching position in each game with the comment {CQL}, so you can step straight to it.

Games downloaded from Lichess have a Site tag containing the address of the game on lichess.org, so a matching game can also be opened there and replayed with engine analysis. The sample match links on this page were produced that way.

Useful options

The -i option specifies the input PGN file and the -o option specifies the output file. The -cql option provides the query on the command line; a longer query can instead be saved in a file with a .cql extension, which is named on the command line in place of the -cql option.

On Windows, Windows PowerShell 5.1 (the version included with Windows) removes double quotes from inside a quoted argument before CQLi sees them. If a query contains double quotes, type each one as \", for example -cql 'zobristkey == \"9f2e3a461655ff6b\"', save the query in a .cql file instead, or use PowerShell 7.3 or later, which passes the quotes through unchanged.

The -matchstring option changes the CQL comment added to matching positions, and the -silent option stops CQLi from adding comments at all. By default, CQLi keeps all matching games in memory until processing finishes; if a query matches a very large number of games, the -nosort option writes each game out as soon as it is found instead.

When trying queries on a large PGN file (millions of games), the -g option limits processing to a range of games so you do not have to wait for the whole file. For example, -g 1 100000 processes only the first 100,000 games.

Short Examples

Make the same substitutions for input.pgn, output.pgn, and cqli as above. Most examples show how many of the same one million lichess.org games match, along with links to ten sample matches.

Remove all comments from a PGN file

cqli -i input.pgn -o output.pgn -silent -nosort -cql 'removecomment'

Failed conversion of KBBvK ending

cqli -i input.pgn -o output.pgn -cql 'terminal result 1/2-1/2 flipcolor { A == K a == [kb] == 3 }'
17 games out of 1,000,000 (Sample matches: #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 )

Mate in KNBvK ending

cqli -i input.pgn -o output.pgn -cql 'terminal mate flipcolor { A == K a == 3 n == 1 b == 1 }'
6 games out of 1,000,000 (Sample matches: #1 #2 #3 #4 #5 #6 )

Win by player 400+ Elo lower

cqli -i input.pgn -o output.pgn -cql 'initial flipcolor { result 1-0 elo white + 400 <= elo black }'
2376 games out of 1,000,000 (Sample matches: #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 )

Castling opposite sides

cqli -i input.pgn -o output.pgn -cql 'initial find move o-o find move o-o-o'
148034 games out of 1,000,000 (Sample matches: #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 )

Bishop pins rook to queen

cqli -i input.pgn -o output.pgn -cql 'flipcolor pin from B through r to q'
6736 games out of 1,000,000 (Sample matches: #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 )

Positions that match a specific Zobrist key

cqli -i input.pgn -o output.pgn -cql 'zobristkey == "9f2e3a461655ff6b"'
51 games out of 1,000,000 (Sample matches: #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 )

Populate a tag named EndFEN with the FEN of the ending position

cqli -i input.pgn -o output.pgn -silent -nosort -cql 'terminal settag("EndFEN" currentfen)'

Promotion(s) by both sides

cqli -i input.pgn -o output.pgn -cql 'initial find { wtm move promote A } find { btm move promote A }'
11539 games out of 1,000,000 (Sample matches: #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 )

Both sides have promoted queens on the board

cqli -i input.pgn -o output.pgn -cql 'promotedpieces & Q promotedpieces & q'
7362 games out of 1,000,000 (Sample matches: #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 )

2+ consecutive promotions

cqli -i input.pgn -o output.pgn -cql 'line --> move promote A {2,}'
4307 games out of 1,000,000 (Sample matches: #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 )

Underpromotions

cqli -i input.pgn -o output.pgn -cql 'move promote [BNR]'
3155 games out of 1,000,000 (Sample matches: #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 )

When you're ready for more, the Examples page works through longer queries in detail, and the HTML Reference Manual documents every filter and option used here.