Getting Started with NotesQueryResultsProcessor: Life After DQL

Why NQRP exists

DQL gives you a SQL-like syntax to find documents, but it returns a plain NotesDocumentCollection — there is no built-in way to sort, categorise, project columns, or serialise to JSON. If you also want to:

  • Sort the results on a field (DQL itself does not guarantee order)
  • Group results into categories
  • Apply @Formula to compute or normalise field values
  • Stream the results out as JSON for a frontend or REST endpoint
  • Materialise the results as a temporary view that users can browse

…you traditionally had to write the loop yourself or build a dedicated view. NotesQueryResultsProcessor (NQRP), introduced in Domino V12, packages all of those post-processing steps into a single fluent API.

Create an NQRP instance

NQRP comes from NotesDatabase.CreateQueryResultsProcessor():

Dim s As New NotesSession
Dim db As NotesDatabase
Set db = s.GetDatabase("myserver", "mydb.nsf")
Dim qrp As NotesQueryResultsProcessor
Set qrp = db.CreateQueryResultsProcessor()

The pattern from there on is always add inputs → define columns → execute.

Add inputs: three sources

1. AddCollection — an existing document collection

Dim view As NotesView
Set view = db.GetView("Customers")
Dim col As NotesDocumentCollection
Set col = db.UnprocessedDocuments ' or view.GetAllDocumentsByKey(...) etc.
Call qrp.AddCollection(col)

Accepts either NotesDocumentCollection or NotesViewEntryCollection.

2. AddDominoQuery — feed DQL straight in

Dim dq As NotesDominoQuery
Set dq = db.CreateDominoQuery()
Call qrp.AddDominoQuery(dq, "Form = 'Order' and Total > 10000", "")

NQRP runs the DQL internally and adds the resulting documents to its input set.

3. AddFormula — override input field values

When the source documents need a computed or normalised field:

Call qrp.AddFormula("@LowerCase(CustomerName)", "CustomerName", "*")

The third parameter "*" applies the formula to all input collections; passing a specific collection name limits it to that one.

Define output columns: AddColumn

Call qrp.AddColumn(String Name, _
Optional String Title, _
Optional String Formula, _
Optional Integer SortOrder, _
Optional Boolean Hidden, _
Optional Boolean Categorized)
ParameterPurpose
NameProgrammatic name of the column (must be unique)
TitleDisplay header when materialised as a view
FormulaDefault @Formula for the value (omit to read the field of the same name)
SortOrderSORT_UNORDERED / SORT_ASCENDING / SORT_DESCENDING
Hiddentrue = sort by this column but do not return it
Categorizedtrue = collapse rows with the same value into one entry

Official example:

Set qrp = db.CreateQueryResultsProcessor()
Call qrp.AddColumn("sales_person", "", "", SORT_ASCENDING, False, True)
Call qrp.AddColumn("ordno", "", "", SORT_DESCENDING, False, False)
Call qrp.AddColumn("order_origin", "", "", SORT_UNORDERED, False, False)
Call qrp.AddColumn("partno")

That defines four columns: categorise by sales_person ascending → within each category sort by ordno descending → display order_origin and partno.

Execute: two output formats

A. ExecuteToJSON — for APIs and front-ends

Dim json As NotesJSONNavigator
Set json = qrp.ExecuteToJSON()

Returns a NotesJSONNavigator. The output conforms to RFC 8259 and lives under StreamResults:

{
"StreamResults": [
{
"@nid": "NT0000A572",
"@DbPath": "dev\\ordwrkqrp.nsf",
"sales_person": "Alice",
"ordno": "PO-1042",
"order_origin": "Web",
"partno": "X-99"
}
]
}

Special keys:

  • @nid — document NoteID
  • @DbPath — source database path
  • documents — child rows under a categorised entry
  • Multi-value fields are emitted as JSON arrays
  • Aggregate functions (@@sum(), @@avg(), @@count()) appear as dedicated keys

B. ExecuteToView — materialise as a browsable temp view

Set myview = qrp.ExecuteToView(ByVal Name As String, _
Optional ByVal ExpireHours As Long, _
Optional Readers As Variant, _
Optional DesignSrcDB As NotesDatabase, _
Optional ByVal DesignSrcViewName As String) As NotesView
ParameterPurpose
NameName of the results view to create
ExpireHoursHours before the view auto-expires (default 24)
ReadersSingle name or array of names (canonical) controlling who can read it
DesignSrcDB / DesignSrcViewNameUse an existing view as the design template

Official example, verbatim:

Dim theReaders(1 To 4) As String
theReaders(1) = "CN=User1 UserLN1/O=MYORG"
theReaders(2) = "CN=User2 UserLN2/O=MYORG"
theReaders(3) = "PrivilegedGroup"
theReaders(4) = "CN=User3 UserLN3/O=MYORG"
Dim s As New NotesSession
Dim db As NotesDatabase
Dim qrp As NotesQueryResultsProcessor
Dim myview As NotesView
Set db = s.GetDatabase("myserver", "mydb.nsf")
Set qrp = db.CreateQueryResultsProcessor()
Set myview = qrp.ExecuteToView("MyNewResultsView", 2, theReaders)

The view is auto-cleaned up by the server, so the NSF design does not bloat over time.

Safety knobs: don’t let a query run forever

qrp.MaxEntries = 50000 ' max input documents; throws if exceeded
qrp.TimeOutSec = 30 ' max execution seconds; throws if exceeded

Public-facing endpoints should always set both, otherwise a malicious or accidental query can exhaust the server.

The Java side

The Java class is lotus.domino.QueryResultsProcessor. Naming maps 1:1 from LotusScript: drop the Notes prefix, switch to camelCase, and keep JSON upper-case in method names:

LotusScriptJava
db.CreateQueryResultsProcessor()db.createQueryResultsProcessor()
qrp.AddCollection(col)qrp.addCollection(col)
qrp.AddDominoQuery(dq, q, "")qrp.addDominoQuery(dq, q, "")
qrp.AddColumn(...)qrp.addColumn(...)
qrp.ExecuteToJSON()qrp.executeToJSON()
qrp.ExecuteToView(name, ...)qrp.executeToView(name, ...)
qrp.MaxEntries = 50000qrp.setMaxEntries(50000)
qrp.TimeOutSec = 30qrp.setTimeoutSec(30)

The Java end-to-end equivalent:

import lotus.domino.*;
Session session = NotesFactory.createSession();
Database db = session.getDatabase("", "orders.nsf");
QueryResultsProcessor qrp = db.createQueryResultsProcessor();
qrp.setMaxEntries(10000);
qrp.setTimeoutSec(20);
DominoQuery dq = db.createDominoQuery();
qrp.addDominoQuery(dq,
"Form = 'Order' and OrderDate >= @dt('2026-01-01')", "");
qrp.addColumn("region", "", "", QueryResultsProcessor.SORT_ASCENDING, false, true);
qrp.addColumn("total", "Total NTD", "", QueryResultsProcessor.SORT_DESCENDING, false, false);
qrp.addColumn("orderNo", "Order No", "", QueryResultsProcessor.SORT_UNORDERED, false, false);
JSONNavigator json = qrp.executeToJSON();
// walk the JSONNavigator API ...
qrp.recycle(); // like every lotus.domino.Base, recycle when done

recycle() is the standard lotus.domino.Base discipline: native handles need to be released explicitly so the JVM doesn’t hold onto them longer than necessary.

End-to-end: DQL + sort + JSON (LotusScript)

Sub TopOrdersToJSON
Dim s As New NotesSession
Dim db As NotesDatabase
Set db = s.GetDatabase("", "orders.nsf")
Dim qrp As NotesQueryResultsProcessor
Set qrp = db.CreateQueryResultsProcessor()
qrp.MaxEntries = 10000
qrp.TimeOutSec = 20
Dim dq As NotesDominoQuery
Set dq = db.CreateDominoQuery()
Call qrp.AddDominoQuery(dq, _
"Form = 'Order' and OrderDate >= @dt('2026-01-01')", "")
Call qrp.AddColumn("region", "", "", SORT_ASCENDING, False, True)
Call qrp.AddColumn("total", "Total NTD", "", SORT_DESCENDING, False, False)
Call qrp.AddColumn("orderNo", "Order No", "", SORT_UNORDERED, False, False)
Dim json As NotesJSONNavigator
Set json = qrp.ExecuteToJSON()
' walk the tree with the NotesJSONNavigator API
End Sub

When to reach for NQRP — and when not

NeedPick
Single document by keyview.GetDocumentByKey()
Full-text keyword searchdb.FTSearch()
Structured condition queryDQL (NotesDominoQuery)
DQL results that also need sort / category / projection / JSONNQRP
REST endpoint with categorised outputNQRP + ExecuteToJSON
Temporary browsable result table for usersNQRP + ExecuteToView

NQRP has been stable since V12 and is a key building block for “Domino-as-API” backends, REST API services, and reporting batch jobs.

Sources

← Back to all posts