Database and Environment Starter Pack

Database identity, forest topology, and deliberate cross-database execution

personClever Llamas
CleverLlamasMinimum Llamaverse Version: 2
databaseMinimum MarkLogic Version: 11

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 ItemWhat this pack uses
Data setllamaverse v2.0+ sample data (used as a realistic target context)
Primary focusdatabase and server runtime metadata, plus deliberate cross-database execution
Views usedNone
Fields usedNone
Index contextNot 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 / ArgumentWhat it controlsUsed here
$nameDatabase name to resolve to a database IDA 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
LineValue
1Content
214411921375042116334
3Schemas
44847239026034777645
5Security
68278156514315716652

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 / ArgumentWhat it controlsUsed here
$idDatabase ID to resolve back to a database nameID 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
LineValue
1Current content database:
2cleverllamas-content
3Sample 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 / ArgumentWhat it controlsUsed here
NoneReturns the ID of every database configured on the hostFiltered 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
LineValue
1Total databases on this host:
213
3cleverllamas-content
42736149102113944426
5cleverllamas-modules
61806839910529930456
7cleverllamas-schemas
84847239026034777645

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 / ArgumentWhat it controlsUsed here
$database-idDatabase ID whose forests you wantcleverllamas-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
LineValue
1Content database:
2cleverllamas-content
3Forests:
4cleverllamas-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 / ArgumentWhat it controlsUsed here
$forest-idForest ID to resolve to its owning databaseThe 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
LineValue
1Forest:
2cleverllamas-content-1
3Owning database:
4cleverllamas-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 / ArgumentWhat it controlsUsed here
$nodeA node whose owning database you want to identifyA 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
LineValue
1Sample URI:
2/cleverllamas/llamaverse/raw/wild-llamas/llamas/67384a21-62fe-4f80-a964-72783cfb076f.json
3Database holding this node:
4cleverllamas-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 / ArgumentWhat it controlsUsed here
NoneReturns the modules database ID in current server contextRead 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
LineValue
1Modules DB ID:
213911541745128587861
3Modules DB name:
4Modules

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 / ArgumentWhat it controlsUsed here
$database-id (optional)Content database whose schema database you want; defaults to the current databaseOmitted, 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
LineValue
1Schema database ID:
24847239026034777645
3Schema database name:
4cleverllamas-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 / ArgumentWhat it controlsUsed here
$database-id (optional)Content database whose security database you want; defaults to the current databaseOmitted, 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
LineValue
1Security database ID:
28278156514315716652
3Security database name:
4Security

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 / ArgumentWhat it controlsUsed here
NoneReturns the triggers database ID for the current database, or 0 if none is configuredRead 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)
LineValue
1Triggers database ID:
20
3Triggers 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:

Option / ArgumentWhat it controlsUsed here
$server-idApp Server whose database configuration you wantEvery 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 ServerContent DatabaseModules Database
AdminSecurity(none - filesystem)
App-ServicesDocumentsModules
cleverllamascleverllamas-contentcleverllamas-modules
HealthCheckApp-Services(none - filesystem)
ManageApp-Services(none - filesystem)
TaskServern/an/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 / ArgumentWhat it controlsUsed here
$xqueryThe code string to evaluate"xdmp:database-name(xdmp:database())"
$options → databaseContent database the code runs againstxdmp: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
LineValue
1Current database:
2cleverllamas-content
3xdmp:eval into Security database:
4Security

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 / ArgumentWhat it controlsUsed here
$pathModule URI to execute/cleverllamas/llamaverse/scratch/starter-pack-cross-db-probe.xqy
$varsExternal variables passed into the moduletarget, a string echoed back by the probe
$options → modulesModules database the path is resolved againstxdmp:database("cleverllamas-modules"), since the default /v1/eval context uses a different modules database
$options → databaseContent database the module executes againstOmitted 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
LineValue
1Invoked with default database:
2cleverllamas-content
3invoked-into-default
4Invoked with database option set to Security:
5Security
6invoked-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 / ArgumentWhat it controlsUsed here
$pathModule URI to execute/cleverllamas/llamaverse/scratch/starter-pack-cross-db-probe.xqy
$varsExternal variables passed into the moduletarget, a string echoed back by the probe
$options → databaseContent database the spawned task executes againstxdmp:schema-database()
$options → modulesModules database the path is resolved againstxdmp:database("cleverllamas-modules")
$options → resultWaits for the task and returns its result instead of running fully asynchronouslyfn: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
LineValue
1Spawned with database option set to Schemas:
2cleverllamas-schemas
3spawned-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:

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!