Database and Environment Starter Pack
Database identity, forest topology, and deliberate cross-database execution
A cluster with several App Servers, a dozen databases, and code that hops between them mid-request makes it easy to lose track of exactly where a query is running. Before you change anything, it's worth knowing for certain.
This pack covers the functions that give you a straight answer: which database and forest you're in, which system databases back it, which database an App Server defaults to, and — for the three functions that let you deliberately run code elsewhere — xdmp:eval(), xdmp:invoke(), and xdmp:spawn(), exactly where that code will land.
The examples in this article assume the llamaverse (v2.0+) is deployed. The llamaverse sample data is freely available from github.com/cleverllamas/llamaverse - see the llamaverse article for full setup instructions.
API Context for This Pack
| Context Item | What this pack uses |
|---|---|
| Data set | llamaverse v2.0+ sample data (used as a realistic target context) |
| Primary focus | database and server runtime metadata, plus deliberate cross-database execution |
| Views used | None |
| Fields used | None |
| Index context | Not index-driven; these functions inspect environment, topology, and database identity, or change which database code runs against |
If values differ from expectation, first check which App Server and content database your request is currently running against.
Database Identity Lookups
The two directions of the same lookup, plus the full inventory. Resolve a name to an ID before you trust it, resolve an ID back to a name for logs, or list everything on the host when you are not sure what exists.
xdmp:database
Turn a database name into an ID before your script accidentally points at the wrong target.
Source docs: https://docs.marklogic.com/11.0/xdmp:database
| Option / Argument | What it controls | Used here |
|---|---|---|
$name | Database name to resolve to a database ID | A known content database name |
xquery version "1.0-ml";
let $sample-node := collection("wild-llamas")[1]
return
if (empty($sample-node)) then
error(xs:QName("NO-LLAMAVERSE"), "No documents found in collection 'wild-llamas'.")
else
(
"Content",
xdmp:database("Documents"),
"Schemas",
xdmp:schema-database(),
"Security",
xdmp:security-database()
)
'use strict';
const sampleNode = fn.collection('wild-llamas').toArray()[0];
if (!sampleNode) {
throw new Error('NO-LLAMAVERSE: No documents found in collection wild-llamas.');
}
({
contentDatabase: xdmp.database('Documents'),
schemasDatabase: xdmp.schemaDatabase(),
securityDatabase: xdmp.securityDatabase()
});
Content
14411921375042116334
Schemas
4847239026034777645
Security
8278156514315716652
| Line | Value |
|---|---|
| 1 | Content |
| 2 | 14411921375042116334 |
| 3 | Schemas |
| 4 | 4847239026034777645 |
| 5 | Security |
| 6 | 8278156514315716652 |
What to notice: an explicit ID beats a script's assumption every time — resolve it, assert it, then act. Guessing which database you're pointed at is how content ends up in the wrong place.
xdmp:database-name
The reverse lookup that keeps logs readable and operators calm.
Source docs: https://docs.marklogic.com/11.0/xdmp:database-name
| Option / Argument | What it controls | Used here |
|---|---|---|
$id | Database ID to resolve back to a database name | ID resolved by xdmp:database(...) |
xquery version "1.0-ml";
let $sample-uri := cts:uris((), (), cts:collection-query("wild-llamas"))[1]
return
if (empty($sample-uri)) then
error(xs:QName("NO-LLAMAVERSE"), "No documents found in collection 'wild-llamas'.")
else
(
"Current content database:",
xdmp:database-name(xdmp:database()),
"Sample URI:",
$sample-uri
)
'use strict';
const sampleUri = cts.uris(null, null, cts.collectionQuery('wild-llamas')).toArray()[0];
if (!sampleUri) {
throw new Error('NO-LLAMAVERSE: No documents found in collection wild-llamas.');
}
const currentDbName = xdmp.databaseName(xdmp.database());
({
currentContentDatabase: currentDbName,
sampleDocumentUri: sampleUri
});
Current content database:
cleverllamas-content
Sample URI:
/cleverllamas/llamaverse/raw/wild-llamas/llamas/00048384-cb13-4557-805e-a6b4e57f7eab.json
| Line | Value |
|---|---|
| 1 | Current content database: |
| 2 | cleverllamas-content |
| 3 | Sample URI: |
| 4 | /cleverllamas/llamaverse/raw/wild-llamas/llamas/00048384-cb13-4557-805e-a6b4e57f7eab.json |
What to notice: nobody wants to grep a log file for an 18-digit database ID at 2am. Resolve it to a name before it goes anywhere near an alert or a ticket.
xdmp:databases
The full inventory. Useful when you are not sure what exists on the host, or when a health check needs to loop over every database.
Source docs: https://docs.marklogic.com/11.0/xdmp:databases
| Option / Argument | What it controls | Used here |
|---|---|---|
| None | Returns the ID of every database configured on the host | Filtered down to the cleverllamas- prefixed databases for a readable example |
xquery version "1.0-ml";
let $sample-node := collection("wild-llamas")[1]
return
if (empty($sample-node)) then
error(xs:QName("NO-LLAMAVERSE"), "No documents found in collection 'wild-llamas'.")
else
let $all-database-ids := xdmp:databases()
let $cleverllamas-databases :=
for $id in $all-database-ids
let $name := xdmp:database-name($id)
where fn:starts-with($name, "cleverllamas")
order by $name
return ($name, xs:string($id))
return
(
"Total databases on this host:",
xs:string(fn:count($all-database-ids)),
$cleverllamas-databases
)
'use strict';
const sampleNode = fn.collection('wild-llamas').toArray()[0];
if (!sampleNode) {
throw new Error('NO-LLAMAVERSE: No documents found in collection wild-llamas.');
}
const allDatabaseIds = xdmp.databases().toArray();
const cleverllamasDatabases = allDatabaseIds
.map((id) => ({ name: xdmp.databaseName(id), id: String(id) }))
.filter((db) => db.name.startsWith('cleverllamas'))
.sort((a, b) => a.name.localeCompare(b.name));
({
totalDatabases: allDatabaseIds.length,
cleverllamasDatabases: cleverllamasDatabases
});
Total databases on this host:
13
cleverllamas-content
2736149102113944426
cleverllamas-modules
1806839910529930456
cleverllamas-schemas
4847239026034777645
| Line | Value |
|---|---|
| 1 | Total databases on this host: |
| 2 | 13 |
| 3 | cleverllamas-content |
| 4 | 2736149102113944426 |
| 5 | cleverllamas-modules |
| 6 | 1806839910529930456 |
| 7 | cleverllamas-schemas |
| 8 | 4847239026034777645 |
What to notice: a fresh MarkLogic host already has several built-in databases (Documents, Modules, Security, Schemas, Triggers, and more) before your application adds its own. Filtering by name prefix keeps the output relevant.
Database Topology: Forests
Databases are made of forests. These two functions cross that boundary in each direction, and the third resolves a document node straight back to its owning database.
xdmp:database-forests
Which forests actually hold the data for a database.
Source docs: https://docs.marklogic.com/11.0/xdmp:database-forests
| Option / Argument | What it controls | Used here |
|---|---|---|
$database-id | Database ID whose forests you want | cleverllamas-content |
xquery version "1.0-ml";
let $sample-node := collection("wild-llamas")[1]
return
if (empty($sample-node)) then
error(xs:QName("NO-LLAMAVERSE"), "No documents found in collection 'wild-llamas'.")
else
let $content-db-id := xdmp:database("cleverllamas-content")
let $forest-ids := xdmp:database-forests($content-db-id)
return
(
"Content database:",
xdmp:database-name($content-db-id),
"Forests:",
for $forest-id in $forest-ids
return xdmp:forest-name($forest-id)
)
'use strict';
const sampleNode = fn.collection('wild-llamas').toArray()[0];
if (!sampleNode) {
throw new Error('NO-LLAMAVERSE: No documents found in collection wild-llamas.');
}
const contentDbId = xdmp.database('cleverllamas-content');
const forestIds = xdmp.databaseForests(contentDbId).toArray();
({
contentDatabase: xdmp.databaseName(contentDbId),
forests: forestIds.map((id) => xdmp.forestName(id))
});
Content database:
cleverllamas-content
Forests:
cleverllamas-content-1
| Line | Value |
|---|---|
| 1 | Content database: |
| 2 | cleverllamas-content |
| 3 | Forests: |
| 4 | cleverllamas-content-1 |
What to notice: this local llamaverse deployment has a single forest per database. Production topologies routinely return several.
xdmp:forest-databases
The reverse of xdmp:database-forests: given a forest, which database owns it.
Source docs: https://docs.marklogic.com/11.0/xdmp:forest-databases
| Option / Argument | What it controls | Used here |
|---|---|---|
$forest-id | Forest ID to resolve to its owning database | The first forest returned by xdmp:database-forests(...) |
xquery version "1.0-ml";
let $sample-node := collection("wild-llamas")[1]
return
if (empty($sample-node)) then
error(xs:QName("NO-LLAMAVERSE"), "No documents found in collection 'wild-llamas'.")
else
let $content-db-id := xdmp:database("cleverllamas-content")
let $forest-id := xdmp:database-forests($content-db-id)[1]
return
(
"Forest:",
xdmp:forest-name($forest-id),
"Owning database:",
xdmp:database-name(xdmp:forest-databases($forest-id))
)
'use strict';
const sampleNode = fn.collection('wild-llamas').toArray()[0];
if (!sampleNode) {
throw new Error('NO-LLAMAVERSE: No documents found in collection wild-llamas.');
}
const contentDbId = xdmp.database('cleverllamas-content');
const forestId = xdmp.databaseForests(contentDbId).toArray()[0];
({
forest: xdmp.forestName(forestId),
owningDatabase: xdmp.databaseName(xdmp.forestDatabases(forestId))
});
Forest:
cleverllamas-content-1
Owning database:
cleverllamas-content
| Line | Value |
|---|---|
| 1 | Forest: |
| 2 | cleverllamas-content-1 |
| 3 | Owning database: |
| 4 | cleverllamas-content |
What to notice: this round-trip is the fastest way to confirm a forest hasn't been reattached to the wrong database after a restore or reconfiguration.
xdmp:node-database
Given an in-memory document node, which database it actually lives in.
Source docs: https://docs.marklogic.com/11.0/xdmp:node-database
| Option / Argument | What it controls | Used here |
|---|---|---|
$node | A node whose owning database you want to identify | A sample llama document node from wild-llamas |
{
"name": "Aaron",
"breed": "Huacaya",
"placeOfBirth": "Cusco, Peru",
"secretPowerId": "d8839ba6-2b77-4bcc-9927-b86cdfecb9fb"
}
xquery version "1.0-ml";
let $sample-node := collection("wild-llamas")[1]
return
if (empty($sample-node)) then
error(xs:QName("NO-LLAMAVERSE"), "No documents found in collection 'wild-llamas'.")
else
(
"Sample URI:",
xdmp:node-uri($sample-node),
"Database holding this node:",
xdmp:database-name(xdmp:node-database($sample-node))
)
'use strict';
const sampleNode = fn.collection('wild-llamas').toArray()[0];
if (!sampleNode) {
throw new Error('NO-LLAMAVERSE: No documents found in collection wild-llamas.');
}
({
sampleUri: xdmp.nodeUri(sampleNode),
database: xdmp.databaseName(xdmp.nodeDatabase(sampleNode))
});
Sample URI:
/cleverllamas/llamaverse/raw/wild-llamas/llamas/67384a21-62fe-4f80-a964-72783cfb076f.json
Database holding this node:
cleverllamas-content
| Line | Value |
|---|---|
| 1 | Sample URI: |
| 2 | /cleverllamas/llamaverse/raw/wild-llamas/llamas/67384a21-62fe-4f80-a964-72783cfb076f.json |
| 3 | Database holding this node: |
| 4 | cleverllamas-content |
What to notice: this only works for a persistent, fragment-rooted node. An in-memory node built with a constructor has no owning database and returns the empty sequence.
Special-Purpose System Databases
Every content database is paired with several system databases: modules, schemas, security, and (optionally) triggers. These functions resolve each one from the current context, without you having to hardcode names.
xdmp:modules-database
Because knowing where code executes from is half of debugging.
Source docs: https://docs.marklogic.com/11.0/xdmp:modules-database
| Option / Argument | What it controls | Used here |
|---|---|---|
| None | Returns the modules database ID in current server context | Read directly from current App Server |
xquery version "1.0-ml";
(
"Modules DB ID:",
xdmp:modules-database(),
"Modules DB name:",
if (empty(xdmp:modules-database())) then "filesystem"
else xdmp:database-name(xdmp:modules-database())
)
'use strict';
const modulesDbId = xdmp.modulesDatabase();
const modulesDbName = fn.empty(modulesDbId) ? 'filesystem' : xdmp.databaseName(modulesDbId);
({
modulesDatabaseId: modulesDbId,
modulesDatabaseName: modulesDbName
});
Modules DB ID:
13911541745128587861
Modules DB name:
Modules
| Line | Value |
|---|---|
| 1 | Modules DB ID: |
| 2 | 13911541745128587861 |
| 3 | Modules DB name: |
| 4 | Modules |
What to notice: this result came from the App Server that evaluates ad hoc /v1/eval requests, so it reports the built-in Modules database. The cleverllamas application's own App Server is configured with cleverllamas-modules instead — see xdmp:server-modules-database in the App Server to Database Mapping section below. Same function, different App Server, different answer. That is exactly the kind of drift this pack exists to catch.
xdmp:schema-database
Resolves the schema database backing the current (or a specified) content database, used to validate documents against registered XML schemas.
Source docs: https://docs.marklogic.com/11.0/xdmp:schema-database
| Option / Argument | What it controls | Used here |
|---|---|---|
$database-id (optional) | Content database whose schema database you want; defaults to the current database | Omitted, so it resolves against the current database |
xquery version "1.0-ml";
let $sample-node := collection("wild-llamas")[1]
return
if (empty($sample-node)) then
error(xs:QName("NO-LLAMAVERSE"), "No documents found in collection 'wild-llamas'.")
else
(
"Schema database ID:",
xs:string(xdmp:schema-database()),
"Schema database name:",
xdmp:database-name(xdmp:schema-database())
)
'use strict';
const sampleNode = fn.collection('wild-llamas').toArray()[0];
if (!sampleNode) {
throw new Error('NO-LLAMAVERSE: No documents found in collection wild-llamas.');
}
const schemaDbId = xdmp.schemaDatabase();
({
schemaDatabaseId: String(schemaDbId),
schemaDatabaseName: xdmp.databaseName(schemaDbId)
});
Schema database ID:
4847239026034777645
Schema database name:
cleverllamas-schemas
| Line | Value |
|---|---|
| 1 | Schema database ID: |
| 2 | 4847239026034777645 |
| 3 | Schema database name: |
| 4 | cleverllamas-schemas |
What to notice: this is one of the databases xdmp:eval, xdmp:invoke, and xdmp:spawn can target further down this pack — resolving it here means you never have to hardcode its ID.
xdmp:security-database
Resolves the security database that authenticates and authorizes the current request.
Source docs: https://docs.marklogic.com/11.0/xdmp:security-database
| Option / Argument | What it controls | Used here |
|---|---|---|
$database-id (optional) | Content database whose security database you want; defaults to the current database | Omitted, so it resolves against the current database |
xquery version "1.0-ml";
let $sample-node := collection("wild-llamas")[1]
return
if (empty($sample-node)) then
error(xs:QName("NO-LLAMAVERSE"), "No documents found in collection 'wild-llamas'.")
else
(
"Security database ID:",
xs:string(xdmp:security-database()),
"Security database name:",
xdmp:database-name(xdmp:security-database())
)
'use strict';
const sampleNode = fn.collection('wild-llamas').toArray()[0];
if (!sampleNode) {
throw new Error('NO-LLAMAVERSE: No documents found in collection wild-llamas.');
}
const securityDbId = xdmp.securityDatabase();
({
securityDatabaseId: String(securityDbId),
securityDatabaseName: xdmp.databaseName(securityDbId)
});
Security database ID:
8278156514315716652
Security database name:
Security
| Line | Value |
|---|---|
| 1 | Security database ID: |
| 2 | 8278156514315716652 |
| 3 | Security database name: |
| 4 | Security |
What to notice: almost every MarkLogic deployment shares a single Security database across all content databases on the cluster. Seeing a different name here is a strong signal of a deliberately isolated security configuration.
xdmp:triggers-database
Resolves the triggers database configured for a content database, if one is configured at all.
Source docs: https://docs.marklogic.com/11.0/xdmp:triggers-database
| Option / Argument | What it controls | Used here |
|---|---|---|
| None | Returns the triggers database ID for the current database, or 0 if none is configured | Read directly from current database context |
xquery version "1.0-ml";
let $sample-node := collection("wild-llamas")[1]
return
if (empty($sample-node)) then
error(xs:QName("NO-LLAMAVERSE"), "No documents found in collection 'wild-llamas'.")
else
let $triggers-db-id := xdmp:triggers-database()
return
(
"Triggers database ID:",
xs:string($triggers-db-id),
"Triggers database name:",
if ($triggers-db-id eq 0) then "(none configured)" else xdmp:database-name($triggers-db-id)
)
'use strict';
const sampleNode = fn.collection('wild-llamas').toArray()[0];
if (!sampleNode) {
throw new Error('NO-LLAMAVERSE: No documents found in collection wild-llamas.');
}
const triggersDbId = xdmp.triggersDatabase();
({
triggersDatabaseId: String(triggersDbId),
triggersDatabaseName: String(triggersDbId) === '0' ? '(none configured)' : xdmp.databaseName(triggersDbId)
});
Triggers database ID:
0
Triggers database name:
(none configured)
| Line | Value |
|---|---|
| 1 | Triggers database ID: |
| 2 | 0 |
| 3 | Triggers database name: |
| 4 | (none configured) |
What to notice: an ID of 0 is not an error, it means no triggers database is attached to this content database. Calling xdmp:database-name(0) directly throws XDMP-NODB, so always check for 0 before resolving the name.
App Server to Database Mapping
Every App Server has its own default content database and modules database, independent of whichever database the current request happens to be running against. xdmp:server-database() and xdmp:server-modules-database() read that configuration directly, and combining them with xdmp:servers() turns it into a full map of the cluster's App Servers at once.
xdmp:server-database and xdmp:server-modules-database
The content database and modules database an App Server is configured to use by default.
Source docs:
- https://docs.marklogic.com/11.0/xdmp:server-database
- https://docs.marklogic.com/11.0/xdmp:server-modules-database
| Option / Argument | What it controls | Used here |
|---|---|---|
$server-id | App Server whose database configuration you want | Every ID returned by xdmp:servers(), so the whole cluster is covered in one pass |
xquery version "1.0-ml";
declare function local:database-label($db-id as xs:unsignedLong?) as xs:string {
if (empty($db-id)) then
"n/a"
else if ($db-id eq 0) then
"(none - filesystem)"
else
xdmp:database-name($db-id)
};
for $server-id in xdmp:servers()
let $name := xdmp:server-name($server-id)
order by $name
return
concat(
$name, " | ",
local:database-label(xdmp:server-database($server-id)), " | ",
local:database-label(xdmp:server-modules-database($server-id))
)
'use strict';
function databaseLabel(dbId) {
if (fn.empty(dbId)) {
return 'n/a';
}
if (dbId.valueOf() === 0) {
return '(none - filesystem)';
}
return xdmp.databaseName(dbId);
}
const rows = xdmp.servers()
.toArray()
.map(serverId => ({
appServer: xdmp.serverName(serverId),
contentDatabase: databaseLabel(xdmp.serverDatabase(serverId)),
modulesDatabase: databaseLabel(xdmp.serverModulesDatabase(serverId))
}))
.sort((a, b) => a.appServer.localeCompare(b.appServer));
rows;
Admin | Security | (none - filesystem)
App-Services | Documents | Modules
cleverllamas | cleverllamas-content | cleverllamas-modules
HealthCheck | App-Services | (none - filesystem)
Manage | App-Services | (none - filesystem)
TaskServer | n/a | n/a
| App Server | Content Database | Modules Database |
|---|---|---|
| Admin | Security | (none - filesystem) |
| App-Services | Documents | Modules |
| cleverllamas | cleverllamas-content | cleverllamas-modules |
| HealthCheck | App-Services | (none - filesystem) |
| Manage | App-Services | (none - filesystem) |
| TaskServer | n/a | n/a |
What to notice: this is configuration, not runtime context. It tells you what each App Server is set up to use, regardless of what a specific request has overridden with the database option. A modules database of (none - filesystem) means that App Server loads modules straight from disk rather than from a database — normal for the built-in Admin, Manage, and HealthCheck servers. TaskServer reports n/a for both, because scheduled tasks each specify their own database rather than inheriting one default.
Cross-Database Execution: eval, invoke, spawn
These three functions run code somewhere other than "here, right now." They can each target a different content database, a different modules database, or both, using the same database and modules options. All three require dedicated privileges, and switching database or modules context requires additional ones on top of the base privilege.
Required Privileges
Each function requires its own base execute privilege (xdmp-eval, xdmp-invoke, xdmp-spawn). Using the database option to target a database other than the current App Server's default requires an additional -in privilege (xdmp-eval-in, xdmp-invoke-in, xdmp-spawn-in). Using modules or root to point at a different modules database or the filesystem requires a further -modules-change privilege. None of this is optional or implicit — MarkLogic checks each capability independently.
The three code samples below all call the same tiny scratch module, so the only thing that changes between them is which database and modules options are set.
The scratch module
A minimal module deployed to cleverllamas-modules at /cleverllamas/llamaverse/scratch/starter-pack-cross-db-probe.xqy. It reports the content database it is executing against, plus whatever value was passed in as an external variable.
xquery version "1.0-ml";
declare variable $target as xs:string external;
(xdmp:database-name(xdmp:database()), $target)
xdmp:eval
Evaluate a string of XQuery (or use xdmp:javascript-eval/xdmp.eval for JavaScript) against a specific database, without needing a separately deployed module.
Source docs: https://docs.marklogic.com/11.0/xdmp:eval
| Option / Argument | What it controls | Used here |
|---|---|---|
$xquery | The code string to evaluate | "xdmp:database-name(xdmp:database())" |
$options → database | Content database the code runs against | xdmp:security-database() |
xquery version "1.0-ml";
let $sample-node := collection("wild-llamas")[1]
return
if (empty($sample-node)) then
error(xs:QName("NO-LLAMAVERSE"), "No documents found in collection 'wild-llamas'.")
else
let $current-database := xdmp:database-name(xdmp:database())
let $security-database-id := xdmp:security-database()
let $eval-result :=
xdmp:eval(
"xdmp:database-name(xdmp:database())",
(),
<options xmlns="xdmp:eval">
<database>{$security-database-id}</database>
</options>
)
return
(
"Current database:", $current-database,
"xdmp:eval into Security database:", $eval-result
)
'use strict';
const sampleNode = fn.collection('wild-llamas').toArray()[0];
if (!sampleNode) {
throw new Error('NO-LLAMAVERSE: No documents found in collection wild-llamas.');
}
const currentDatabase = xdmp.databaseName(xdmp.database());
const securityDatabaseId = xdmp.securityDatabase();
const evalResult = xdmp.eval(
'xdmp.databaseName(xdmp.database())',
{},
{ database: securityDatabaseId }
);
({
currentDatabase: currentDatabase,
evalIntoSecurityDatabase: evalResult
});
Current database:
cleverllamas-content
xdmp:eval into Security database:
Security
| Line | Value |
|---|---|
| 1 | Current database: |
| 2 | cleverllamas-content |
| 3 | xdmp:eval into Security database: |
| 4 | Security |
What to notice: the evaluated code has no knowledge of where it was called from. xdmp:database() inside the string genuinely returns Security, because the database option changed the execution context, not just what the code can see.
xdmp:invoke
Run a module file synchronously, optionally against a different content database and a different modules database than the calling App Server's defaults.
Source docs: https://docs.marklogic.com/11.0/xdmp:invoke
| Option / Argument | What it controls | Used here |
|---|---|---|
$path | Module URI to execute | /cleverllamas/llamaverse/scratch/starter-pack-cross-db-probe.xqy |
$vars | External variables passed into the module | target, a string echoed back by the probe |
$options → modules | Modules database the path is resolved against | xdmp:database("cleverllamas-modules"), since the default /v1/eval context uses a different modules database |
$options → database | Content database the module executes against | Omitted for the first call (uses the App Server default), then xdmp:security-database() for the second |
xquery version "1.0-ml";
declare variable $target as xs:string external;
(xdmp:database-name(xdmp:database()), $target)
xquery version "1.0-ml";
let $sample-node := collection("wild-llamas")[1]
return
if (empty($sample-node)) then
error(xs:QName("NO-LLAMAVERSE"), "No documents found in collection 'wild-llamas'.")
else
let $modules-database-id := xdmp:database("cleverllamas-modules")
let $security-database-id := xdmp:security-database()
let $module-uri := "/cleverllamas/llamaverse/scratch/starter-pack-cross-db-probe.xqy"
let $invoke-default :=
xdmp:invoke(
$module-uri,
(xs:QName("target"), "invoked-into-default"),
<options xmlns="xdmp:eval">
<modules>{$modules-database-id}</modules>
</options>
)
let $invoke-security :=
xdmp:invoke(
$module-uri,
(xs:QName("target"), "invoked-into-security"),
<options xmlns="xdmp:eval">
<database>{$security-database-id}</database>
<modules>{$modules-database-id}</modules>
</options>
)
return
(
"Invoked with default database:", $invoke-default,
"Invoked with database option set to Security:", $invoke-security
)
'use strict';
const sampleNode = fn.collection('wild-llamas').toArray()[0];
if (!sampleNode) {
throw new Error('NO-LLAMAVERSE: No documents found in collection wild-llamas.');
}
const modulesDatabaseId = xdmp.database('cleverllamas-modules');
const securityDatabaseId = xdmp.securityDatabase();
const moduleUri = '/cleverllamas/llamaverse/scratch/starter-pack-cross-db-probe.xqy';
const invokeDefault = xdmp.invoke(
moduleUri,
{ target: 'invoked-into-default' },
{ modules: modulesDatabaseId }
);
const invokeSecurity = xdmp.invoke(
moduleUri,
{ target: 'invoked-into-security' },
{ database: securityDatabaseId, modules: modulesDatabaseId }
);
({
invokedWithDefaultDatabase: Array.from(invokeDefault),
invokedWithSecurityDatabase: Array.from(invokeSecurity)
});
Invoked with default database:
cleverllamas-content
invoked-into-default
Invoked with database option set to Security:
Security
invoked-into-security
| Line | Value |
|---|---|
| 1 | Invoked with default database: |
| 2 | cleverllamas-content |
| 3 | invoked-into-default |
| 4 | Invoked with database option set to Security: |
| 5 | Security |
| 6 | invoked-into-security |
What to notice: the exact same module, called twice, reports a different content database each time. Only the database option changed. This is the core mental model for xdmp:invoke — the module's code never changes, but where it runs entirely depends on the options you pass.
xdmp:invoke-function() is a close relative worth knowing about: it takes an in-memory function reference instead of a module URI, but accepts the same database and modules options shown above. It isn't covered here — see the Security Model article for how it behaves around amp propagation across execution boundaries.
xdmp:spawn
Place a module on the task server queue for asynchronous evaluation. Use the result option to get a value back synchronously for demonstration purposes; in production, spawned tasks normally run fire-and-forget.
Source docs: https://docs.marklogic.com/11.0/xdmp:spawn
| Option / Argument | What it controls | Used here |
|---|---|---|
$path | Module URI to execute | /cleverllamas/llamaverse/scratch/starter-pack-cross-db-probe.xqy |
$vars | External variables passed into the module | target, a string echoed back by the probe |
$options → database | Content database the spawned task executes against | xdmp:schema-database() |
$options → modules | Modules database the path is resolved against | xdmp:database("cleverllamas-modules") |
$options → result | Waits for the task and returns its result instead of running fully asynchronously | fn:true() |
xquery version "1.0-ml";
declare variable $target as xs:string external;
(xdmp:database-name(xdmp:database()), $target)
xquery version "1.0-ml";
let $sample-node := collection("wild-llamas")[1]
return
if (empty($sample-node)) then
error(xs:QName("NO-LLAMAVERSE"), "No documents found in collection 'wild-llamas'.")
else
let $modules-database-id := xdmp:database("cleverllamas-modules")
let $schema-database-id := xdmp:schema-database()
let $module-uri := "/cleverllamas/llamaverse/scratch/starter-pack-cross-db-probe.xqy"
let $spawn-result :=
xdmp:spawn(
$module-uri,
(xs:QName("target"), "spawned-into-schemas"),
<options xmlns="xdmp:eval">
<database>{$schema-database-id}</database>
<modules>{$modules-database-id}</modules>
<result>true</result>
</options>
)
return
("Spawned with database option set to Schemas:", $spawn-result)
'use strict';
const sampleNode = fn.collection('wild-llamas').toArray()[0];
if (!sampleNode) {
throw new Error('NO-LLAMAVERSE: No documents found in collection wild-llamas.');
}
const modulesDatabaseId = xdmp.database('cleverllamas-modules');
const schemaDatabaseId = xdmp.schemaDatabase();
const moduleUri = '/cleverllamas/llamaverse/scratch/starter-pack-cross-db-probe.xqy';
const spawnResult = xdmp.spawn(
moduleUri,
{ target: 'spawned-into-schemas' },
{ database: schemaDatabaseId, modules: modulesDatabaseId, result: true }
);
({
spawnedWithSchemaDatabase: Array.from(spawnResult)
});
Spawned with database option set to Schemas:
cleverllamas-schemas
spawned-into-schemas
| Line | Value |
|---|---|
| 1 | Spawned with database option set to Schemas: |
| 2 | cleverllamas-schemas |
| 3 | spawned-into-schemas |
What to notice: without the result option, xdmp:spawn returns immediately and you never see this output directly — the task runs on its own timeline on the task server. Once spawned, the task cannot be rolled back even if the calling transaction fails, so use it deliberately, not as a casual substitute for xdmp:invoke.
xdmp:spawn-function() is the equivalent for an in-memory function reference rather than a module URI, and it accepts the same database and modules options shown above. It isn't covered here, and the amp-propagation caution noted for xdmp:invoke-function() in the Security Model article applies to it as well.
Know Your Terrain
Decision rule: resolve database identity first, forest topology second, and only reach for xdmp:eval/xdmp:invoke/xdmp:spawn once you know exactly which database and modules context you intend to target. Context drift creates the most expensive false conclusions, and cross-database execution makes that drift a lot easier to cause by accident.
Once you've verified your database context, these related topics help you inspect what's inside:
- Identity and Context Starter Pack — Check user, role, and server identity alongside your database context.
- Document Inspection Starter Pack — Inspect the documents and collections that live in your verified database.
- Runtime and Storage Starter Pack — Check storage volume and placement within your target database.
Need Some Help?
Looking for more information on this subject or any other topic related to MarkLogic? Contact Us (info@cleverllamas.com) to find out how we can assist you with consulting or training!
- API Context for This Pack
- Database Identity Lookups
- xdmp:database
- xdmp:database-name
- xdmp:databases
- Database Topology: Forests
- xdmp:database-forests
- xdmp:forest-databases
- xdmp:node-database
- Special-Purpose System Databases
- xdmp:modules-database
- xdmp:schema-database
- xdmp:security-database
- xdmp:triggers-database
- App Server to Database Mapping
- xdmp:server-database and xdmp:server-modules-database
- Cross-Database Execution: eval, invoke, spawn
- The scratch module
- xdmp:eval
- xdmp:invoke
- xdmp:spawn
- Know Your Terrain