Runtime and Storage Starter Pack

Fast checks for volume, placement, and document format in llamaverse

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

"How much content is there, where does it live, and what shape is it in?" Three questions, and most MarkLogic operators end up guessing at the answers instead of checking them - usually because the checking functions are scattered across the API reference rather than gathered somewhere useful.

This pack gathers them. Estimated counts from xdmp:estimate, forest and database placement from xdmp:node-database and friends, document format from URI and content inspection, and the binary storage model - including the split between regular and registered (external) binaries that catches people out the first time they hit it. Each example runs against real llamaverse content, so the numbers you get back are numbers you can check against a document you can actually look at.

Scope Note

There is a fourth question this pack deliberately leaves out: who has access to any of it. Permissions, collections, and role-based access are covered in depth in the Document Inspection Starter Pack - reach for that pack when the operational question turns from "what is here" to "who can see it".

The examples in this article assume the llamaverse (v2.6.0+) is deployed. The llamaverse sample data is freely available from github.com/cleverllamas/llamaverse - see the llamaverse article for full setup instructions. The binary storage examples specifically need Aaron's llama photo registered as an external binary - see "What Changed in 2.6.0" in the llamaverse article for the one-time setup step.

API Context for This Pack

Context ItemWhat this pack uses
Data setllamaverse v2.6.0+ sample data
Primary document scopewild llama URIs for estimate/placement checks; llama profile photos for binary storage checks
Primary focusruntime/storage facts (estimated count, forest/database placement, URI format, binary storage model)
Views usedNone
Index contextMixed: xdmp:estimate can use query/index evaluation; other calls inspect storage/runtime metadata

If runtime numbers seem wrong, confirm you are querying the expected content database and not a different environment clone.

Functions in This Pack

xdmp:estimate

The polite way to ask, "roughly how big is this llama pile?"

Source docs: https://docs.marklogic.com/11.0/xdmp:estimate

Option / ArgumentWhat it controlsUsed here
$expressionExpression whose item count is estimatedURI sequence from llamaverse scope
{
  "name": "Aaron",
  "breed": "Huacaya",
  "placeOfBirth": "Cusco, Peru",
  "secretPowerId": "d8839ba6-2b77-4bcc-9927-b86cdfecb9fb"
}
xquery version "1.0-ml";

xdmp:estimate(collection("wild-llamas"))
'use strict';

// Estimate the number of fragments in the wild-llamas collection.
// xdmp:estimate()'s JS equivalent is cts.estimate() - xdmp.estimate is not exposed to JavaScript.
const estimate = cts.estimate(cts.collectionQuery('wild-llamas'));

const results = {
  collectionEstimate: estimate,
  collectionName: 'wild-llamas',
  message: `Estimated size of wild-llamas collection: ${estimate} fragments`
};

({
  sample: 'xdmp/runtime-storage-starter-pack/assets/xdmp-estimate.sjs',
  kind: Array.isArray(results) ? (results.every((item) => typeof item === 'object' && 'subject' in item && 'predicate' in item && 'object' in item) ? 'triples' : 'rows') : ((results !== null && typeof results === 'object') ? 'object' : 'scalar'),
  count: Array.isArray(results) ? results.length : 0,
  data: results
});
4561
MetricValue
Result4561

What to notice: xdmp:estimate() is fast because it never opens a single document — it reads the universal index and stops there. That speed is the entire reason to reach for it over an exact count.

xdmp:document-forest

When you need to know where a document actually lives, not where you hope it lives.

Source docs: https://docs.marklogic.com/11.0/xdmp:document-forest

Option / ArgumentWhat it controlsUsed here
$uriDocument URI to inspect for forest placementA wild-llama document URI
{
  "name": "Aaron",
  "breed": "Huacaya",
  "placeOfBirth": "Cusco, Peru",
  "secretPowerId": "d8839ba6-2b77-4bcc-9927-b86cdfecb9fb"
}
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
    (
      $sample-uri,
      xdmp:document-forest($sample-uri),
      xdmp:forest-name(xdmp:document-forest($sample-uri))
    )
'use strict';

// Find a document from wild-llamas and check its forest placement.
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 forestId = xdmp.documentForest(sampleUri);
const forestName = xdmp.forestName(forestId);

const results = {
  documentUri: sampleUri,
  forestId: forestId,
  forestName: forestName,
  collection: 'wild-llamas'
};

({
  sample: 'xdmp/runtime-storage-starter-pack/assets/xdmp-document-forest.sjs',
  kind: Array.isArray(results) ? (results.every((item) => typeof item === 'object' && 'subject' in item && 'predicate' in item && 'object' in item) ? 'triples' : 'rows') : ((results !== null && typeof results === 'object') ? 'object' : 'scalar'),
  count: Array.isArray(results) ? results.length : 0,
  data: results
});
/cleverllamas/llamaverse/raw/wild-llamas/llamas/00048384-cb13-4557-805e-a6b4e57f7eab.json
1195255089468435289
cleverllamas-content-1
LineValue
1/cleverllamas/llamaverse/raw/wild-llamas/llamas/00048384-cb13-4557-805e-a6b4e57f7eab.json
21195255089468435289
3cleverllamas-content-1

What to notice: having both the forest ID and its name on hand turns a placement question from a lookup exercise into a one-line answer — exactly what you want mid-incident, not three greps deep.

xdmp:uri-format

Tiny function, big value. It settles "xml or json?" arguments in one line.

Source docs: https://docs.marklogic.com/11.0/xdmp:uri-format

Option / ArgumentWhat it controlsUsed here
$uriDocument URI whose format is returnedA wild-llama document URI
{
  "name": "Aaron",
  "breed": "Huacaya",
  "placeOfBirth": "Cusco, Peru",
  "secretPowerId": "d8839ba6-2b77-4bcc-9927-b86cdfecb9fb"
}
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
    (
      $sample-uri,
      xdmp:uri-format($sample-uri)
    )
'use strict';

// Find a document from wild-llamas and check its format.
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 format = xdmp.uriFormat(sampleUri);

const results = {
  documentUri: sampleUri,
  format: format,
  collection: 'wild-llamas',
  message: `Document at ${sampleUri} is in ${format} format`
};

({
  sample: 'xdmp/runtime-storage-starter-pack/assets/xdmp-uri-format.sjs',
  kind: Array.isArray(results) ? (results.every((item) => typeof item === 'object' && 'subject' in item && 'predicate' in item && 'object' in item) ? 'triples' : 'rows') : ((results !== null && typeof results === 'object') ? 'object' : 'scalar'),
  count: Array.isArray(results) ? results.length : 0,
  data: results
});
/cleverllamas/llamaverse/raw/wild-llamas/llamas/00048384-cb13-4557-805e-a6b4e57f7eab.json
json
LineValue
1/cleverllamas/llamaverse/raw/wild-llamas/llamas/00048384-cb13-4557-805e-a6b4e57f7eab.json
2json

What to notice: a pipeline that assumes every URI is JSON will fail quietly on the one document that isn't. This check costs nothing and catches that assumption before it costs you a debugging afternoon.

The rest of this pack goes one level deeper: which database a node actually lives in, and - for binary content - whether it lives inside the database at all or is just registered from a file on disk. For collection membership and general node-kind checks, see the Document Inspection Starter Pack; this pack sticks to physical placement.

xdmp:node-database

Forest placement tells you which physical shard a document lives on. Database placement tells you which logical container that shard belongs to. Different question, same instinct: don't assume, check.

Source docs: https://docs.marklogic.com/11.0/xdmp:node-database

Option / ArgumentWhat it controlsUsed here
$nodeNode whose owning database is returnedKatrina's profile photo binary node
xquery version "1.0-ml";

let $photo-uri := "/cleverllamas/llamaverse/content/llamas/images/1024fa02-cd67-44f2-8d8c-0c5f16ccb7d1.gif"
let $photo := doc($photo-uri)/binary()
return
  if (empty($photo)) then
    error(xs:QName("NO-LLAMAVERSE"), "Katrina's profile photo was not found. Deploy llamaverse 2.6.0 or later.")
  else
    (
      $photo-uri,
      xdmp:node-database($photo),
      xdmp:database-name(xdmp:node-database($photo))
    )
'use strict';

// Which database is Katrina's profile photo actually stored in?
const photoUri = '/cleverllamas/llamaverse/content/llamas/images/1024fa02-cd67-44f2-8d8c-0c5f16ccb7d1.gif';
const photo = fn.head(cts.doc(photoUri));

if (!photo) {
  throw new Error('NO-LLAMAVERSE: Katrina\'s profile photo was not found. Deploy llamaverse 2.6.0 or later.');
}

const databaseId = xdmp.nodeDatabase(photo);

const results = {
  photoUri: photoUri,
  databaseId: databaseId,
  databaseName: xdmp.databaseName(databaseId)
};

({
  sample: 'xdmp/runtime-storage-starter-pack/assets/xdmp-node-database.sjs',
  kind: Array.isArray(results) ? (results.every((item) => typeof item === 'object' && 'subject' in item && 'predicate' in item && 'object' in item) ? 'triples' : 'rows') : ((results !== null && typeof results === 'object') ? 'object' : 'scalar'),
  count: Array.isArray(results) ? results.length : 0,
  data: results
});
/cleverllamas/llamaverse/content/llamas/images/1024fa02-cd67-44f2-8d8c-0c5f16ccb7d1.gif
2736149102113944426
cleverllamas-content
LineValue
1/cleverllamas/llamaverse/content/llamas/images/1024fa02-cd67-44f2-8d8c-0c5f16ccb7d1.gif
22736149102113944426
3cleverllamas-content

What to notice: xdmp:node-database() works on any node, not just documents - useful when you already have a node in hand and don't want to re-resolve its URI first. Pair it with xdmp:document-forest() above when you need both the database and the forest.

xdmp:binary-is-external

Same content type, two different storage stories. Katrina's photo lives inside the database like any other binary; Aaron's lives on the filesystem, and the database document just holds a pointer to it.

Source docs: https://docs.marklogic.com/11.0/xdmp:binary-is-external

Option / ArgumentWhat it controlsUsed here
$sourceBinary node to testKatrina's regular photo and Aaron's registered photo
xquery version "1.0-ml";

let $katrina-uri := "/cleverllamas/llamaverse/content/llamas/images/1024fa02-cd67-44f2-8d8c-0c5f16ccb7d1.gif"
let $aaron-uri := "/cleverllamas/llamaverse/content/llamas/images/0c8bdb0d-ac62-49b7-ac74-94dbba46efa5.gif"
let $katrina := doc($katrina-uri)/binary()
let $aaron := doc($aaron-uri)/binary()
return
  if (empty($katrina) or empty($aaron)) then
    error(xs:QName("NO-LLAMAVERSE"), "Katrina's or Aaron's profile photo was not found. Deploy llamaverse 2.6.0 or later and register Aaron's external binary.")
  else
    (
      "Katrina: " || xdmp:binary-is-external($katrina),
      "Aaron: " || xdmp:binary-is-external($aaron)
    )
'use strict';

// Same content type, two different storage models: compare Katrina (regular) and Aaron (registered/external).
function binaryNode(uri) {
  const doc = fn.head(cts.doc(uri));
  return doc ? fn.head(doc.xpath('node()')) : null;
}

const katrinaUri = '/cleverllamas/llamaverse/content/llamas/images/1024fa02-cd67-44f2-8d8c-0c5f16ccb7d1.gif';
const aaronUri = '/cleverllamas/llamaverse/content/llamas/images/0c8bdb0d-ac62-49b7-ac74-94dbba46efa5.gif';
const katrina = binaryNode(katrinaUri);
const aaron = binaryNode(aaronUri);

if (!katrina || !aaron) {
  throw new Error('NO-LLAMAVERSE: Katrina\'s or Aaron\'s profile photo was not found. Deploy llamaverse 2.6.0 or later and register Aaron\'s external binary.');
}

const results = {
  katrina: { uri: katrinaUri, isExternal: xdmp.binaryIsExternal(katrina) },
  aaron: { uri: aaronUri, isExternal: xdmp.binaryIsExternal(aaron) }
};

({
  sample: 'xdmp/runtime-storage-starter-pack/assets/xdmp-binary-is-external.sjs',
  kind: Array.isArray(results) ? (results.every((item) => typeof item === 'object' && 'subject' in item && 'predicate' in item && 'object' in item) ? 'triples' : 'rows') : ((results !== null && typeof results === 'object') ? 'object' : 'scalar'),
  count: Array.isArray(results) ? results.length : 0,
  data: results
});
Katrina: false
Aaron: true
LlamaBinary typexdmp:binary-is-external()
KatrinaRegular (in-database)false
AaronRegistered (external)true

What to notice: this is the function that actually distinguishes the two storage models. Everything else in this section - size, database, forest - looks the same for both; only xdmp:binary-is-external() tells you the bytes aren't where you'd assume.

xdmp:binary-size

Source docs: https://docs.marklogic.com/11.0/xdmp:binary-size

Option / ArgumentWhat it controlsUsed here
$sourceBinary node whose byte length is returnedKatrina's and Aaron's photos
xquery version "1.0-ml";

let $katrina-uri := "/cleverllamas/llamaverse/content/llamas/images/1024fa02-cd67-44f2-8d8c-0c5f16ccb7d1.gif"
let $aaron-uri := "/cleverllamas/llamaverse/content/llamas/images/0c8bdb0d-ac62-49b7-ac74-94dbba46efa5.gif"
let $katrina := doc($katrina-uri)/binary()
let $aaron := doc($aaron-uri)/binary()
return
  if (empty($katrina) or empty($aaron)) then
    error(xs:QName("NO-LLAMAVERSE"), "Katrina's or Aaron's profile photo was not found. Deploy llamaverse 2.6.0 or later and register Aaron's external binary.")
  else
    (
      "Katrina: " || xdmp:binary-size($katrina),
      "Aaron: " || xdmp:binary-size($aaron)
    )
'use strict';

// Binary size in bytes is the same kind of number regardless of storage model.
function binaryNode(uri) {
  const doc = fn.head(cts.doc(uri));
  return doc ? fn.head(doc.xpath('node()')) : null;
}

const katrinaUri = '/cleverllamas/llamaverse/content/llamas/images/1024fa02-cd67-44f2-8d8c-0c5f16ccb7d1.gif';
const aaronUri = '/cleverllamas/llamaverse/content/llamas/images/0c8bdb0d-ac62-49b7-ac74-94dbba46efa5.gif';
const katrina = binaryNode(katrinaUri);
const aaron = binaryNode(aaronUri);

if (!katrina || !aaron) {
  throw new Error('NO-LLAMAVERSE: Katrina\'s or Aaron\'s profile photo was not found. Deploy llamaverse 2.6.0 or later and register Aaron\'s external binary.');
}

const results = {
  katrina: { uri: katrinaUri, sizeBytes: xdmp.binarySize(katrina) },
  aaron: { uri: aaronUri, sizeBytes: xdmp.binarySize(aaron) }
};

({
  sample: 'xdmp/runtime-storage-starter-pack/assets/xdmp-binary-size.sjs',
  kind: Array.isArray(results) ? (results.every((item) => typeof item === 'object' && 'subject' in item && 'predicate' in item && 'object' in item) ? 'triples' : 'rows') : ((results !== null && typeof results === 'object') ? 'object' : 'scalar'),
  count: Array.isArray(results) ? results.length : 0,
  data: results
});
Katrina: 331994
Aaron: 488467
LlamaSize (bytes)
Katrina331994
Aaron488467

What to notice: xdmp:binary-size() returns the same kind of number regardless of storage model - it doesn't care whether the bytes sit in the database or on disk.

xdmp:binary-is-large and xdmp:binary-is-small

These two answer "does this binary get special large-object treatment?" The cleverllamas-content database's large-size-threshold is configured at 1024 KB (1 MB); both sample photos sit comfortably under that, so neither is flagged large.

Source docs: https://docs.marklogic.com/11.0/xdmp:binary-is-large and https://docs.marklogic.com/11.0/xdmp:binary-is-small

Option / ArgumentWhat it controlsUsed here
$sourceBinary node to testKatrina's and Aaron's photos
xquery version "1.0-ml";

let $katrina-uri := "/cleverllamas/llamaverse/content/llamas/images/1024fa02-cd67-44f2-8d8c-0c5f16ccb7d1.gif"
let $aaron-uri := "/cleverllamas/llamaverse/content/llamas/images/0c8bdb0d-ac62-49b7-ac74-94dbba46efa5.gif"
let $katrina := doc($katrina-uri)/binary()
let $aaron := doc($aaron-uri)/binary()
return
  if (empty($katrina) or empty($aaron)) then
    error(xs:QName("NO-LLAMAVERSE"), "Katrina's or Aaron's profile photo was not found. Deploy llamaverse 2.6.0 or later and register Aaron's external binary.")
  else
    (
      "Katrina: large=" || xdmp:binary-is-large($katrina) || " small=" || xdmp:binary-is-small($katrina),
      "Aaron: large=" || xdmp:binary-is-large($aaron) || " small=" || xdmp:binary-is-small($aaron)
    )
'use strict';

// A binary can be neither large nor small - both flags describe internal storage tiering.
function binaryNode(uri) {
  const doc = fn.head(cts.doc(uri));
  return doc ? fn.head(doc.xpath('node()')) : null;
}

const katrinaUri = '/cleverllamas/llamaverse/content/llamas/images/1024fa02-cd67-44f2-8d8c-0c5f16ccb7d1.gif';
const aaronUri = '/cleverllamas/llamaverse/content/llamas/images/0c8bdb0d-ac62-49b7-ac74-94dbba46efa5.gif';
const katrina = binaryNode(katrinaUri);
const aaron = binaryNode(aaronUri);

if (!katrina || !aaron) {
  throw new Error('NO-LLAMAVERSE: Katrina\'s or Aaron\'s profile photo was not found. Deploy llamaverse 2.6.0 or later and register Aaron\'s external binary.');
}

const results = {
  katrina: { uri: katrinaUri, sizeBytes: xdmp.binarySize(katrina), isLarge: xdmp.binaryIsLarge(katrina), isSmall: xdmp.binaryIsSmall(katrina) },
  aaron: { uri: aaronUri, sizeBytes: xdmp.binarySize(aaron), isLarge: xdmp.binaryIsLarge(aaron), isSmall: xdmp.binaryIsSmall(aaron) }
};

({
  sample: 'xdmp/runtime-storage-starter-pack/assets/xdmp-binary-is-large-and-small.sjs',
  kind: Array.isArray(results) ? (results.every((item) => typeof item === 'object' && 'subject' in item && 'predicate' in item && 'object' in item) ? 'triples' : 'rows') : ((results !== null && typeof results === 'object') ? 'object' : 'scalar'),
  count: Array.isArray(results) ? results.length : 0,
  data: results
});
Katrina: large=false small=true
Aaron: large=false small=false
LlamaSize (bytes)is-largeis-small
Katrina331994falsetrue
Aaron488467falsefalse

What to notice: "small" and "large" aren't opposites here - a binary can be neither, sitting in the middle range. Both flags describe internal storage tiering, not whether the content feels big to a human.

xdmp:external-binary-path

Source docs: https://docs.marklogic.com/11.0/xdmp:external-binary-path

Option / ArgumentWhat it controlsUsed here
$sourceBinary node to inspectAaron's registered photo, and Katrina's regular one for contrast
xquery version "1.0-ml";

let $katrina-uri := "/cleverllamas/llamaverse/content/llamas/images/1024fa02-cd67-44f2-8d8c-0c5f16ccb7d1.gif"
let $aaron-uri := "/cleverllamas/llamaverse/content/llamas/images/0c8bdb0d-ac62-49b7-ac74-94dbba46efa5.gif"
let $katrina := doc($katrina-uri)/binary()
let $aaron := doc($aaron-uri)/binary()
return
  if (empty($katrina) or empty($aaron)) then
    error(xs:QName("NO-LLAMAVERSE"), "Katrina's or Aaron's profile photo was not found. Deploy llamaverse 2.6.0 or later and register Aaron's external binary.")
  else
    (
      "Aaron: " || (xdmp:external-binary-path($aaron), "(no path returned)")[1],
      "Katrina: " || (xdmp:external-binary-path($katrina), "(empty sequence - not a registered binary)")[1]
    )
'use strict';

// xdmp.externalBinaryPath() quietly returns an empty sequence for a regular binary instead of raising an error.
function binaryNode(uri) {
  const doc = fn.head(cts.doc(uri));
  return doc ? fn.head(doc.xpath('node()')) : null;
}

const katrinaUri = '/cleverllamas/llamaverse/content/llamas/images/1024fa02-cd67-44f2-8d8c-0c5f16ccb7d1.gif';
const aaronUri = '/cleverllamas/llamaverse/content/llamas/images/0c8bdb0d-ac62-49b7-ac74-94dbba46efa5.gif';
const katrina = binaryNode(katrinaUri);
const aaron = binaryNode(aaronUri);

if (!katrina || !aaron) {
  throw new Error('NO-LLAMAVERSE: Katrina\'s or Aaron\'s profile photo was not found. Deploy llamaverse 2.6.0 or later and register Aaron\'s external binary.');
}

const aaronPath = fn.head(xdmp.externalBinaryPath(aaron));
const katrinaPath = fn.head(xdmp.externalBinaryPath(katrina));

const results = {
  aaron: { uri: aaronUri, externalBinaryPath: aaronPath !== null ? aaronPath : '(no path returned)' },
  katrina: { uri: katrinaUri, externalBinaryPath: katrinaPath !== null ? katrinaPath : '(empty sequence - not a registered binary)' }
};

({
  sample: 'xdmp/runtime-storage-starter-pack/assets/xdmp-external-binary-path.sjs',
  kind: Array.isArray(results) ? (results.every((item) => typeof item === 'object' && 'subject' in item && 'predicate' in item && 'object' in item) ? 'triples' : 'rows') : ((results !== null && typeof results === 'object') ? 'object' : 'scalar'),
  count: Array.isArray(results) ? results.length : 0,
  data: results
});
Aaron: /var/opt/MarkLogic/external-binaries/llamas/0c8bdb0d-ac62-49b7-ac74-94dbba46efa5.gif
Katrina: (empty sequence - not a registered binary)
Llamaxdmp:external-binary-path()
Aaron/var/opt/MarkLogic/external-binaries/llamas/0c8bdb0d-ac62-49b7-ac74-94dbba46efa5.gif
Katrinaempty sequence

What to notice: the function quietly returns an empty sequence for a regular binary instead of raising an error - check xdmp:binary-is-external() first if you need to tell "not registered" apart from "registered but the path is unavailable."

xdmp:filesystem-file-exists

The natural next question once you have a path: is the file actually still there? Registering an external binary only stores a path - MarkLogic doesn't guarantee the file hasn't been moved, renamed, or deleted underneath it.

Source docs: https://docs.marklogic.com/11.0/xdmp:filesystem-file-exists

Option / ArgumentWhat it controlsUsed here
$pathnameFilesystem path to checkAaron's registered photo path, plus a deliberately made-up path
$hostHost to check on (defaults to the current host)Not supplied - uses the current host
xquery version "1.0-ml";

let $aaron-uri := "/cleverllamas/llamaverse/content/llamas/images/0c8bdb0d-ac62-49b7-ac74-94dbba46efa5.gif"
let $aaron := doc($aaron-uri)/binary()
return
  if (empty($aaron)) then
    error(xs:QName("NO-LLAMAVERSE"), "Aaron's profile photo was not found. Deploy llamaverse 2.6.0 or later and register Aaron's external binary.")
  else
    let $real-path := xdmp:external-binary-path($aaron)
    let $made-up-path := "/var/opt/MarkLogic/external-binaries/llamas/does-not-exist.gif"
    return
      (
        "Real path exists: " || xdmp:filesystem-file-exists($real-path),
        "Made-up path exists: " || xdmp:filesystem-file-exists($made-up-path)
      )
'use strict';

// The check happens on the MarkLogic host's filesystem, not the client's.
function binaryNode(uri) {
  const doc = fn.head(cts.doc(uri));
  return doc ? fn.head(doc.xpath('node()')) : null;
}

const aaronUri = '/cleverllamas/llamaverse/content/llamas/images/0c8bdb0d-ac62-49b7-ac74-94dbba46efa5.gif';
const aaron = binaryNode(aaronUri);

if (!aaron) {
  throw new Error('NO-LLAMAVERSE: Aaron\'s profile photo was not found. Deploy llamaverse 2.6.0 or later and register Aaron\'s external binary.');
}

const realPath = xdmp.externalBinaryPath(aaron);
const madeUpPath = '/var/opt/MarkLogic/external-binaries/llamas/does-not-exist.gif';

const results = {
  realPath: { path: realPath, exists: xdmp.filesystemFileExists(realPath) },
  madeUpPath: { path: madeUpPath, exists: xdmp.filesystemFileExists(madeUpPath) }
};

({
  sample: 'xdmp/runtime-storage-starter-pack/assets/xdmp-filesystem-file-exists.sjs',
  kind: Array.isArray(results) ? (results.every((item) => typeof item === 'object' && 'subject' in item && 'predicate' in item && 'object' in item) ? 'triples' : 'rows') : ((results !== null && typeof results === 'object') ? 'object' : 'scalar'),
  count: Array.isArray(results) ? results.length : 0,
  data: results
});
Real path exists: true
Made-up path exists: false
PathExists
Aaron's real external-binary pathtrue
A deliberately made-up pathfalse

What to notice: this check runs against the MarkLogic host's filesystem, not the client's - useful for catching a registered binary whose backing file quietly went missing during a migration or host move.

Operational Wisdom Awaits

Decision rule: measure placement and format before tuning performance assumptions; wrong storage assumptions compound quickly.

Now that you understand volume and placement, take these insights deeper:

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!