Running DQL from Java: DominoQuery and QueryResultsProcessor
The site’s DQL trilogy covers the query language in depth — the syntax, the pitfalls, the production tuning. But all three run DQL from LotusScript or the domino console. Move to Java and the entry point looks different, and it hides a detail a lot of people look for in the wrong place the first time.
In Java, DQL isn’t one method — it’s two classes dividing the work. DominoQuery compiles, tunes, and runs a query; QueryResultsProcessor takes its results and does the sorting, aggregation, cross-database joins, and JSON output. The query string itself is exactly what you learned in the trilogy; what changes is how you run it from Java and how you collect the results.
This piece is about that Java side.
TL;DR
- DQL in Java goes through two classes:
DominoQuery(compile / tune / run) andQueryResultsProcessor(sort / aggregate / join / output). - You get both from the
Database, not theSession—db.createDominoQuery(),db.createQueryResultsProcessor(). This is the most common first-time wrong turn. DominoQuery: the docs call it “a Java class to compile, tune, and run Domino Query Language (DQL) queries.”parse()validates syntax;execute()runs it and returns aDocumentCollection.- Tuning is via properties:
MaxScanDocs(default 500,000),MaxScanEntries(200,000),TimeoutSecs(300s),NoViews… these are the production trilogy piece’s “don’t let one query scan the server to death,” as Java knobs. execute()returns aDocumentCollection, so you’re back to the recycle() piece’s loop discipline: process one, recycle one.QueryResultsProcessoris what gives you the SQL-style “sort + aggregate + join + output” —DominoQueryonly answers “which documents match.”
DominoQuery: get it from the Database
The first step to running DQL in Java is asking for a DominoQuery instance — the docs sum up its job in one line:
A Java class to compile, tune, and run Domino Query Language (DQL) queries.
The key detail: you get it from the Database, not the Session. db.createDominoQuery(). People coming from other APIs instinctively go looking on session and don’t find it, because DQL runs against a specific database, so the entry point naturally hangs off Database.
Once you have it, two main moves:
parse()validates the DQL syntax. A syntax error stops here, before any data is scanned.execute()actually runs it and returns aDocumentCollectionof the matching documents.
Tuning: don’t let one query scan the server to death
The refrain from the production trilogy piece — one un-tuned DQL query can scan a server to its knees — is a set of DominoQuery properties in Java, each with a default:
| Property | Default | What it does |
|---|---|---|
MaxScanDocs | 500,000 | cap on documents scanned; abort past it |
MaxScanEntries | 200,000 | cap on view entries scanned |
TimeoutSecs | 300 | query timeout in seconds |
NoViews | false | forbid DQL from using view indexes (force a document scan) |
For batch DQL in production these are your safety net — better to have a runaway query hit a ceiling and abort than to have it drag the whole server down with it.
execute() returns a DocumentCollection — back to recycle discipline
execute() hands you a DocumentCollection, and from there it’s the standard Java loop — the two-variable pattern from the recycle() piece: hold the next, process the current, recycle the current, advance. A DQL query often returns tens of thousands of documents; skip the recycle here and you get exactly the back-end handle leak that piece describes.
Database db = session.getDatabase(null, "sales.nsf");DominoQuery dq = db.createDominoQuery();dq.setMaxScanDocs(100000); // tuning: set a sane ceilingDocumentCollection col = dq.execute( "Form = 'Order' and OrderDate >= @dt('2026-01-01')");
Document doc = col.getFirstDocument();Document next = null;while (doc != null) { next = col.getNextDocument(doc); // ...process the current doc... doc.recycle(); // recycle each; don't let the result set fill memory doc = next;}dq.recycle();QueryResultsProcessor: sort, aggregate, join across databases
DominoQuery answers one thing: which documents match. It doesn’t sort, aggregate, or reach across databases. That’s the job of QueryResultsProcessor (often QRP) — the docs define it as:
Aggregates, computes, sorts, and formats collections of documents across any set of Domino databases.
You get it from the Database too (db.createQueryResultsProcessor()), then:
addDominoQuery()/addCollection(): feed in one or moreDominoQueryobjects orDocumentCollections — this is how you join across databases and result sets.addColumn(): choose which fields to return and how to sort.addFormula(): use a formula to normalize fields from different databases and designs into one output column.executeToJSON(): output JSON (handy for a front end or API);executeToView(): materialize the results as a view.
So DominoQuery is the filter and QueryResultsProcessor is the sort + combine + emit. To “merge orders from three databases, sort by amount, output JSON,” it’s addDominoQuery three times + addColumn + executeToJSON. It has its own setTimeoutSec() / setMaxEntries() guards against running away, too.
What about LotusScript and SSJS?
The DQL language itself is identical across languages — the syntax you learn in the trilogy is the same string in Java, LotusScript, or SSJS. Only “how you run it” differs:
| Language | How you run DQL |
|---|---|
| LotusScript | NotesDominoQuery + NotesQueryResultsProcessor; the runtime cleans up objects for you |
| SSJS / XPages | the same classes, container-managed memory; in practice DQL more often runs from Java or LS |
Java (lotus.domino) | DominoQuery + QueryResultsProcessor, both from the Database, and you recycle the result DocumentCollection yourself |
Coming from LotusScript, the DQL logic in Java is the same, with two extra things to remember: the entry point is on Database (not Session), and recycling the result set is your job. For how these classes connect, see the class map.