Search Composition Starter Pack

Build and inspect practical query blocks on top of llamaverse content

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

Each composed query is shown alongside the data and output it depends on, so you can trace the logic instead of reassembling it from scratch.

This pack is all about composing cts queries that stay readable at 2 a.m. when the pager goes off.

The examples below explain any query-shaping options where they matter and keep the output focused on the values that show how the composition behaves.

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. The examples below use the Aaron llama document from llamaverse so you can see the document, query, and result together while you trace each composed search step. The lexicon-first examples also show how URI and collection patterns can be pulled into the same query plan without turning the logic into a scavenger hunt.

API Context for This Pack

Context ItemWhat this pack uses
Data setllamaverse v2.0+ sample data
Primary document scopewild llama JSON documents (/cleverllamas/llamaverse/raw/wild-llamas/llamas/{uuid}.json)
Primary constraintscollection, directory, and composed cts query clauses
Fields usedNone in these examples
Index contextResolved through cts query/index evaluation for fast composition and estimate checks

If these examples return no rows, first confirm the collection and directory paths in the sample data match your environment.

For option-level text matching behaviour (case, diacritic, wildcard, stemming, and related tuning assumptions), see Word Search Configuration Settings That Affect These Samples.

Functions in This Pack

The classic. Still the fastest way to stop pretending full-text is easy.

Source docs: https://docs.marklogic.com/11.0/cts:search

Option / ArgumentWhat it controlsUsed here
$exprRoot expression or document sequence to searchfn:doc() sequence for the sample scope
$queryCTS query that defines match criteriaComposed query built in each sample
$optionsSearch options as strings and/or cts:order constructorsNot set in this sample (defaults apply)
$quality-weightQuality contribution to scoring (1.0 default)Not set in this sample
$forest-idsForest IDs to constrain search scopeNot set in this sample
$options ValueWhat it controlsDefault when omitted
"filtered" / "unfiltered"Match verification mode"filtered"
"score-logtfidf" / "score-logtf" / "score-simple" / "score-random" / "score-zero"Scoring algorithm"score-logtfidf"
"checked" / "unchecked"Position checking during search evaluation"checked"
"faceted" / "unfaceted"Facet metadata behaviour"unfaceted"
"any" / "document" / "properties" / "locks"Fragment type scope"document" when $query is supplied; otherwise "any"
cts:order constructors (for example cts:index-order(...), cts:score-order(...), cts:document-order(...), cts:quality-order(...), cts:confidence-order(...), cts:fitness-order(...))Result ordering directivesNo explicit order override

cts:search documented option interactions

  1. "filtered" and "unfiltered" select the match-verification mode.
  2. "checked" and "unchecked" control position checking.
  3. Fragment scope options ("any", "document", "properties", "locks") control which fragment classes are included.
  4. Score options ("score-logtfidf", "score-logtf", "score-simple", "score-random", "score-zero") select the scoring algorithm.
  5. cts:order constructors apply ordering directives during result evaluation.
{
  "envelope": {
    "headers": { "type": "llamas" },
    "instance": {
      "llamas": {
        "id": "0c8bdb0d-ac62-49b7-ac74-94dbba46efa5",
        "name": "Aaron",
        "heightCm": 180,
        "weightKg": 138,
        "eyeColor": "Amber",
        "hairColor": "Blonde",
        "breed": "Huacaya",
        "placeOfBirth": "Cusco, Peru",
        "interests": ["playing chess", "birdwatching", "writing poetry"],
        "medicalCondition": null,
        "relatedTo": {
          "id": "a6c69bb3-fa75-4327-bed6-1f0623ae2c6c",
          "relationship": "father"
        },
        "description": "Aaron is an amber-eyed llama with blonde hair, standing 180 cm tall. Originally from Cusco, Peru, Aaron enjoys playing chess, birdwatching, and writing poetry.",
        "secretPower": {
          "name": "Snake Charmer",
          "id": "f6236908-9c0e-416e-a591-a1bd3986dc05"
        }
      }
    }
  }
}
xquery version "1.0-ml";

for $doc in cts:search(
  collection("wild-llamas"),
  cts:true-query(),
  "unfiltered"
)[1 to 10]
return xdmp:node-uri($doc)
'use strict';

const results = cts.search(fn.doc(), cts.trueQuery()).toArray().slice(0, 10).map((doc) => xdmp.nodeUri(doc));

({
  sample: 'cts/search-composition-starter-pack/assets/cts-search.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/4429e229-490a-4398-992e-84d0f4737d6d.json
/cleverllamas/llamaverse/raw/wild-llamas/llamas/67384a21-62fe-4f80-a964-72783cfb076f.json
/cleverllamas/llamaverse/raw/wild-llamas/llamas/b3591799-54a8-44ea-87a1-d1c05659f781.json
/cleverllamas/llamaverse/raw/wild-llamas/llamas/c45a4f65-d14a-4206-b921-ffc9470e14bb.json
/cleverllamas/llamaverse/raw/wild-llamas/movement/2022_W12_batch6.json
/cleverllamas/llamaverse/raw/wild-llamas/movement/2022_W15_batch4.json
/cleverllamas/llamaverse/raw/wild-llamas/movement/2022_W18_batch2.json
/cleverllamas/llamaverse/raw/wild-llamas/movement/2023_W41_batch1.json
/cleverllamas/llamaverse/raw/wild-llamas/movement/2024_W24_batch5.json
/cleverllamas/llamaverse/raw/wild-llamas/movement/2024_W27_batch3.json
#URI
1/cleverllamas/llamaverse/raw/wild-llamas/llamas/4429e229-490a-4398-992e-84d0f4737d6d.json
2/cleverllamas/llamaverse/raw/wild-llamas/llamas/67384a21-62fe-4f80-a964-72783cfb076f.json
3/cleverllamas/llamaverse/raw/wild-llamas/llamas/b3591799-54a8-44ea-87a1-d1c05659f781.json
4/cleverllamas/llamaverse/raw/wild-llamas/llamas/c45a4f65-d14a-4206-b921-ffc9470e14bb.json
5/cleverllamas/llamaverse/raw/wild-llamas/movement/2022_W12_batch6.json
6/cleverllamas/llamaverse/raw/wild-llamas/movement/2022_W15_batch4.json
7/cleverllamas/llamaverse/raw/wild-llamas/movement/2022_W18_batch2.json
8/cleverllamas/llamaverse/raw/wild-llamas/movement/2023_W41_batch1.json
9/cleverllamas/llamaverse/raw/wild-llamas/movement/2024_W24_batch5.json
10/cleverllamas/llamaverse/raw/wild-llamas/movement/2024_W27_batch3.json

What to notice: this returns URI-backed nodes from the wild-llamas collection and keeps query logic index-driven.

cts:and-query

The query equivalent of "yes, and" for people who enjoy precision.

Source docs: https://docs.marklogic.com/11.0/cts:and-query

Option / ArgumentWhat it controlsUsed here
$queriesThe list of sub-queries that must all matchCollection + directory constraints
$optionsConjunction behaviour optionsNot set in this sample (default applies)
$options ValueWhat it controlsDefault when omitted
"ordered" / "unordered"Whether sub-query matches must appear in order"unordered"
{
  "envelope": {
    "headers": { "type": "llamas" },
    "instance": {
      "llamas": {
        "id": "0c8bdb0d-ac62-49b7-ac74-94dbba46efa5",
        "name": "Aaron",
        "heightCm": 180,
        "weightKg": 138,
        "eyeColor": "Amber",
        "hairColor": "Blonde",
        "breed": "Huacaya",
        "placeOfBirth": "Cusco, Peru",
        "interests": ["playing chess", "birdwatching", "writing poetry"],
        "medicalCondition": null,
        "relatedTo": {
          "id": "a6c69bb3-fa75-4327-bed6-1f0623ae2c6c",
          "relationship": "father"
        },
        "description": "Aaron is an amber-eyed llama with blonde hair, standing 180 cm tall. Originally from Cusco, Peru, Aaron enjoys playing chess, birdwatching, and writing poetry.",
        "secretPower": {
          "name": "Snake Charmer",
          "id": "f6236908-9c0e-416e-a591-a1bd3986dc05"
        }
      }
    }
  }
}
xquery version "1.0-ml";

let $query := cts:and-query((
  cts:collection-query("wild-llamas"),
  cts:directory-query("/cleverllamas/llamaverse/raw/wild-llamas/llamas/", "infinity")
))
return cts:uris((), (), $query)[1 to 20]
'use strict';

const query = cts.andQuery([
  cts.collectionQuery('llamaverse'),
  cts.directoryQuery('/cleverllamas/llamaverse/', 'infinity')
]);

const results = cts.uris(null, ['limit=10'], query).toArray();

({
  sample: 'cts/search-composition-starter-pack/assets/cts-and-query.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
/cleverllamas/llamaverse/raw/wild-llamas/llamas/0005e261-4302-4ae8-9574-732a54040423.json
/cleverllamas/llamaverse/raw/wild-llamas/llamas/000eca10-d166-469f-b17a-3c3b35ee0883.json
/cleverllamas/llamaverse/raw/wild-llamas/llamas/002c7133-e985-4bd6-9ea8-f057fa0c6ee7.json
/cleverllamas/llamaverse/raw/wild-llamas/llamas/0043e95e-3209-4b97-8871-f9b7073377f2.json
/cleverllamas/llamaverse/raw/wild-llamas/llamas/006c5ffa-6c5e-4169-8a08-eec49f48148c.json
/cleverllamas/llamaverse/raw/wild-llamas/llamas/007229e2-ee25-47c1-b18a-2e0c0ec0dbcd.json
/cleverllamas/llamaverse/raw/wild-llamas/llamas/00863414-306e-49cb-acd0-6579bea5421e.json
/cleverllamas/llamaverse/raw/wild-llamas/llamas/0087ad24-a720-4d82-b513-79ae5feebcc6.json
/cleverllamas/llamaverse/raw/wild-llamas/llamas/009cf2b9-d4db-4491-9cc2-34e2f43a5ee1.json
/cleverllamas/llamaverse/raw/wild-llamas/llamas/00a0fe3a-da61-493c-990e-af7719db0d18.json
/cleverllamas/llamaverse/raw/wild-llamas/llamas/00cf82a3-4937-40c6-80fe-6ae0f1100364.json
... (8 more URIs)
#URI
1/cleverllamas/llamaverse/raw/wild-llamas/llamas/00048384-cb13-4557-805e-a6b4e57f7eab.json
2/cleverllamas/llamaverse/raw/wild-llamas/llamas/0005e261-4302-4ae8-9574-732a54040423.json
3/cleverllamas/llamaverse/raw/wild-llamas/llamas/000eca10-d166-469f-b17a-3c3b35ee0883.json
4/cleverllamas/llamaverse/raw/wild-llamas/llamas/002c7133-e985-4bd6-9ea8-f057fa0c6ee7.json
5/cleverllamas/llamaverse/raw/wild-llamas/llamas/0043e95e-3209-4b97-8871-f9b7073377f2.json
6/cleverllamas/llamaverse/raw/wild-llamas/llamas/006c5ffa-6c5e-4169-8a08-eec49f48148c.json
7/cleverllamas/llamaverse/raw/wild-llamas/llamas/007229e2-ee25-47c1-b18a-2e0c0ec0dbcd.json
8/cleverllamas/llamaverse/raw/wild-llamas/llamas/00863414-306e-49cb-acd0-6579bea5421e.json
9/cleverllamas/llamaverse/raw/wild-llamas/llamas/0087ad24-a720-4d82-b513-79ae5feebcc6.json
10/cleverllamas/llamaverse/raw/wild-llamas/llamas/009cf2b9-d4db-4491-9cc2-34e2f43a5ee1.json

What to notice: cts:and-query narrows scope cleanly by combining collection and directory constraints.

cts:estimate

Fast cardinality checks without dragging every llama through memory.

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

Option / ArgumentWhat it controlsUsed here
$queryThe CTS query to estimate cardinality forComposed collection/directory query
$optionsSearch options applied during estimateNot set in this sample (defaults apply)
$quality-weightContribution of quality to scoring during estimate evaluation (1.0 default)Not set in this sample
$forest-idsForest IDs to constrain estimate scopeNot set in this sample
$maximumStop counting after this threshold is reachedNot set in this sample
$options ValueWhat it controlsDefault when omitted
"filtered" / "unfiltered"Match verification mode"filtered"
"score-logtfidf" / "score-logtf" / "score-simple" / "score-random" / "score-zero"Scoring algorithm"score-logtfidf"
"checked" / "unchecked"Position checking during estimate evaluation"checked"
"faceted" / "unfaceted"Facet metadata behaviour"unfaceted"
"any" / "document" / "properties" / "locks"Fragment type scope"document" when $query is supplied; otherwise "any"
cts:order constructors (for example cts:index-order(...), cts:score-order(...), cts:document-order(...), cts:quality-order(...), cts:confidence-order(...), cts:fitness-order(...))Ordering directives used during evaluationNo explicit order override

cts:estimate documented option interactions

  1. cts:estimate accepts the same search-option families as cts:search for verification, scoring, position checking, fragment scope, and ordering.
  2. $maximum sets an upper bound for counting work by stopping after the specified threshold.
  3. $forest-ids constrains the estimate to the specified forests.
  4. Option/default behaviour follows the function signatures and defaults documented for cts:estimate.
{
  "envelope": {
    "headers": { "type": "llamas" },
    "instance": {
      "llamas": {
        "id": "0c8bdb0d-ac62-49b7-ac74-94dbba46efa5",
        "name": "Aaron",
        "heightCm": 180,
        "weightKg": 138,
        "eyeColor": "Amber",
        "hairColor": "Blonde",
        "breed": "Huacaya",
        "placeOfBirth": "Cusco, Peru",
        "interests": ["playing chess", "birdwatching", "writing poetry"],
        "medicalCondition": null,
        "relatedTo": {
          "id": "a6c69bb3-fa75-4327-bed6-1f0623ae2c6c",
          "relationship": "father"
        },
        "description": "Aaron is an amber-eyed llama with blonde hair, standing 180 cm tall. Originally from Cusco, Peru, Aaron enjoys playing chess, birdwatching, and writing poetry.",
        "secretPower": {
          "name": "Snake Charmer",
          "id": "f6236908-9c0e-416e-a591-a1bd3986dc05"
        }
      }
    }
  }
}
xquery version "1.0-ml";

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

const results = cts.estimate(cts.collectionQuery('llamaverse'));

({
  sample: 'cts/search-composition-starter-pack/assets/cts-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: estimates give fast counts for planning and diagnostics without materialising result sets.

cts:contains

Use this when you already have candidate nodes and want a straight yes/no test against a composed query.

Source docs: https://docs.marklogic.com/11.0/cts:contains

Option / ArgumentWhat it controlsUsed here
$nodesNodes (or values) tested for query matchA single llama document node from cts:search(...)
$queryThe query applied to each nodeWord, near, and impossible-match checks
xquery version "1.0-ml";

let $doc := cts:search(
  fn:doc(),
  cts:and-query((
    cts:collection-query("wild-llamas"),
    cts:word-query("sung")
  ))
)[1]
return
  map:entry("uri", xdmp:node-uri($doc))
  => map:with("containsSung", cts:contains($doc, cts:word-query("sung")))
  => map:with("containsNearPair", cts:contains($doc, cts:near-query((cts:word-query("sung"), cts:word-query("llama")), 10)))
  => map:with("containsImpossible", cts:contains($doc, cts:word-query("zozzlefrob")))


{"uri":"/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json", "containsImpossible":false, "containsNearPair":false, "containsSung":true}
uricontainsImpossiblecontainsNearPaircontainsSung
/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.jsonfalsefalsetrue

What to notice: this is ideal for compositional guardrails such as pre-checking whether a candidate node satisfies a costly follow-up path.

cts:walk

Think of this as a query-driven iterator over matched text positions.

Source docs: https://docs.marklogic.com/11.0/cts:walk

Option / ArgumentWhat it controlsUsed here
$nodeNode to traverse for matchesA llama document node
$queryMatch criteria used while walking textcts:word-query("sung")
$exprExpression evaluated per matchEmits match text + offsets; uses $cts:action to cap output
xquery version "1.0-ml";

let $doc := cts:search(
  fn:doc(),
  cts:and-query((
    cts:collection-query("wild-llamas"),
    cts:word-query("sung")
  ))
)[1]
let $match := 0
return
  cts:walk(
    $doc,
    cts:word-query("sung"),
    if ($match ge 5)
    then xdmp:set($cts:action, "break")
    else (
      xdmp:set($match, $match + 1),
      map:entry("uri", xdmp:node-uri($doc))
      => map:with("match", $match)
      => map:with("start", $cts:start)
      => map:with("text", $cts:text)
    )
  )


{"uri":"/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json", "start":114, "match":1, "text":"sung"}
uristartmatchtext
/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json1141sung

What to notice: $cts:start gives deterministic character offsets, which is perfect for building explainability payloads and UI hit snippets.

cts:highlight

This is the readable way to turn query matches into visible evidence.

Source docs: https://docs.marklogic.com/11.0/cts:highlight

Option / ArgumentWhat it controlsUsed here
$nodeNode whose matching text is replacedA compact snippet node derived from llama content
$queryWhat should be highlightedcts:word-query("sung")
$exprReplacement expression for each match<mark>{$cts:text}</mark>
xquery version "1.0-ml";

let $doc := cts:search(
  fn:doc(),
  cts:and-query((
    cts:collection-query("wild-llamas"),
    cts:word-query("sung")
  ))
)[1]
let $source-text := fn:head($doc//text()[fn:contains(fn:lower-case(.), "sung")])
let $snippet :=
  if (fn:exists($source-text))
  then <snippet>{$source-text}</snippet>
  else <snippet>{fn:substring(fn:string($doc), 1, 180)}</snippet>
let $highlighted := cts:highlight($snippet, cts:word-query("sung"), <mark>{$cts:text}</mark>)
return (
  map:entry("uri", xdmp:node-uri($doc))
  => map:with("highlightCount", fn:count($highlighted//mark))
  => map:with("firstHighlight", fn:string(($highlighted//mark)[1])),
  xdmp:quote($highlighted)
)


{"uri":"/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json", "firstHighlight":"sung", "highlightCount":1}

<snippet>Kyle is a gray-eyed llama with gray hair, standing 175 cm tall. Originally from Lynnmouth, Myanmar where he once <mark>sung</mark> for a group of prestegious penguins, knitting, hiking. Known for their playful and energetic personality, Kyle is a beloved member of the llama community.</snippet>
LineValue
1{"uri":"/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json", "firstHighlight":"sung", "highlightCount":1}
2&lt;snippet&gt;Kyle is a gray-eyed llama with gray hair, standing 175 cm tall. Originally from Lynnmouth, Myanmar where he once &lt;mark&gt;sung&lt;/mark&gt; for a group of prestegious penguins, kni...

What to notice: unlike plain regex replacement, this follows full query semantics (stemming, case behaviour, and token matching rules).

cts:parse

cts:parse is where search text stops being vibes and becomes an explicit query tree.

Source docs: https://docs.marklogic.com/11.0/cts:parse

Option / ArgumentWhat it controlsUsed here
$querySearch text to parseAND/OR/NEAR expressions
$bindingsOptional mapping for tagged and untagged termsShown in the synonym/lexicon binding sample below

cts:parse deep dive: token text to nested query constructors

flowchart LR
    A[search text] --> B[tokenization]
    B --> C[operator precedence]
    C --> D[nested cts query tree]
    D --> E[cts.search execution]
    E --> F[ranked nodes and scores]

In practice, this means text such as sung AND llama, sung OR wool, and sung NEAR/8 wool becomes explicit constructor trees (cts:and-query, cts:or-query, cts:near-query) before execution.

xquery version "1.0-ml";

let $queries := (
  "sung AND llama",
  "sung OR wool",
  "sung NEAR/8 wool",
  "(sung OR wool) AND llama"
)
for $query-text in $queries
let $parsed := cts:parse($query-text)
let $sample-uris :=
  for $doc in cts:search(
    fn:doc(),
    cts:and-query((
      cts:collection-query("wild-llamas"),
      $parsed
    ))
  )[1 to 3]
  return xdmp:node-uri($doc)
return
  map:entry("queryText", $query-text)
  => map:with("parsedPreview", fn:substring(xdmp:quote($parsed), 1, 260))
  => map:with("sampleUris", $sample-uris)
[
  {
    "queryText": "sung AND llama",
    "parsedPreview": "cts:and-query((cts:word-query(\"sung\", (\"lang=en\"), 1), cts:word-query(\"llama\", (\"lang=en\"), 1)), (\"unordered\"))",
    "sampleUris": [
      "/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json",
      "/cleverllamas/llamaverse/raw/wild-llamas/llamas/507cf4de-4469-497f-9888-7756c592b119.json",
      "/cleverllamas/llamaverse/raw/wild-llamas/llamas/19be9dcc-fd35-4c54-b503-63a6a8a1043d.json"
    ]
  },
  {
    "queryText": "sung OR wool",
    "parsedPreview": "cts:or-query((cts:word-query(\"sung\", (\"lang=en\"), 1), cts:word-query(\"wool\", (\"lang=en\"), 1)), ())",
    "sampleUris": [
      "/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json",
      "/cleverllamas/llamaverse/raw/wild-llamas/llamas/507cf4de-4469-497f-9888-7756c592b119.json",
      "/cleverllamas/llamaverse/raw/wild-llamas/llamas/19be9dcc-fd35-4c54-b503-63a6a8a1043d.json"
    ]
  },
  {
    "queryText": "sung NEAR/8 wool",
    "parsedPreview": "cts:near-query((cts:word-query(\"sung\", (\"lang=en\"), 1), cts:word-query(\"wool\", (\"lang=en\"), 1)), 8, (\"unordered\"), 1)"
  },
  {
    "queryText": "(sung OR wool) AND llama",
    "parsedPreview": "cts:and-query((cts:or-query((cts:word-query(\"sung\", (\"lang=en\"), 1), cts:word-query(\"wool\", (\"lang=en\"), 1)), ()), cts:word-query(\"llama\", (\"lang=en\"), 1)), (\"unordered\"))",
    "sampleUris": [
      "/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json",
      "/cleverllamas/llamaverse/raw/wild-llamas/llamas/507cf4de-4469-497f-9888-7756c592b119.json",
      "/cleverllamas/llamaverse/raw/wild-llamas/llamas/19be9dcc-fd35-4c54-b503-63a6a8a1043d.json"
    ]
  }
]
queryTextparsedPreviewsampleUris
sung AND llamacts:and-query((cts:word-query("sung", ("lang=en"), 1), cts:word-query("llama", ("lang=en"), 1)), ("unordered"))/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/507cf4de-4469-497f-9888-7756c592b119.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/19be9dcc-fd35-4c54-b503-63a6a8a1043d.json
sung OR woolcts:or-query((cts:word-query("sung", ("lang=en"), 1), cts:word-query("wool", ("lang=en"), 1)), ())/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/507cf4de-4469-497f-9888-7756c592b119.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/19be9dcc-fd35-4c54-b503-63a6a8a1043d.json
sung NEAR/8 woolcts:near-query((cts:word-query("sung", ("lang=en"), 1), cts:word-query("wool", ("lang=en"), 1)), 8, ("unordered"), 1)
(sung OR wool) AND llamacts:and-query((cts:or-query((cts:word-query("sung", ("lang=en"), 1), cts:word-query("wool", ("lang=en"), 1)), ()), cts:word-query("llama", ("lang=en"), 1)), ("unordered"))/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/507cf4de-4469-497f-9888-7756c592b119.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/19be9dcc-fd35-4c54-b503-63a6a8a1043d.json

What to notice: for each text form you can inspect the parsed constructor preview and then immediately sample resulting URIs from the same composed query.

cts:parse bindings: lexicon-aware tags plus synonym defaults

This sample uses tagged parsing (name:...) and an untagged default binding that emits synonym-aware word queries.

xquery version "1.0-ml";

let $bindings :=
  let $map := map:map()
  return (
    map:put($map, "name", function($operator as xs:string, $values as xs:string*, $options as xs:string*) as cts:query {
      cts:json-property-word-query("name", $values)
    }),
    map:put($map, "", function($operator as xs:string, $values as xs:string*, $options as xs:string*) as cts:query {
      cts:word-query($values, ("stemmed", "synonym"))
    }),
    $map
  )
let $query-text := "name:Debra sung"
let $parsed := cts:parse($query-text, $bindings)
let $search-query := cts:and-query((cts:collection-query("wild-llamas"), $parsed))
let $sample-uris :=
  for $doc in cts:search(fn:doc(), $search-query)[1 to 5]
  return xdmp:node-uri($doc)
return
  map:entry("queryText", $query-text)
  => map:with("parsedPreview", fn:substring(xdmp:quote($parsed), 1, 320))
  => map:with("estimate", cts:estimate($search-query))
  => map:with("sampleUris", $sample-uris)


{"estimate":3, "queryText":"name:Debra sung", "parsedPreview":"cts:and-query((cts:json-property-word-query(\"name\", \"Debra\", (\"lang=en\"), 1), cts:word-query(\"sung\", (\"synonym\",\"stemmed\",\"lang=en\"), 1)), (\"unordered\"))", "sampleUris":["/cleverllamas/llamaverse/raw/wild-llamas/llamas/507cf4de-4469-497f-9888-7756c592b119.json", "/cleverllamas/llamaverse/raw/wild-llamas/llamas/0f58de15-1f66-4613-9d28-99ef161a7f0e.json", "/cleverllamas/llamaverse/raw/wild-llamas/llamas/eaa45d04-a899-46e4-b03c-519066d56854.json"]}
estimatequeryTextparsedPreviewsampleUris
3name:Debra sungcts:and-query((cts:json-property-word-query("name", "Debra", ("lang=en"), 1), cts:word-query("sung", ("synonym","stemmed","lang=en"), 1)), ("unordered"))/cleverllamas/llamaverse/raw/wild-llamas/llamas/507cf4de-4469-497f-9888-7756c592b119.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/0f58de15-1f66-4613-9d28-99ef161a7f0e.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/eaa45d04-a899-46e4-b03c-519066d56854.json

What to notice: the parsed preview shows both behaviours in the emitted tree: a property-scoped query for name: and a synonym/stemmed fallback for untagged terms.

Lexicon-first scope composition

When query scope should be stable and fast, build your scope from lexicons first and layer text criteria second.

This sample makes the URI and collection scope explicit before adding text evidence.

Scope helperWhat it returnsWhy it matters
cts:uri-match()URI strings from the URI lexiconLets you start from a URI pattern instead of a full document walk
cts:collection-match()Collection names from the collection lexiconKeeps collection filtering pattern-driven and reusable
cts:document-query()Document-fragment scope from a URI-based queryBridges lexicon values into a searchable fragment constraint
cts:collection-query()Collection-fragment scope from collection valuesTurns matched collection names into a composable search clause
cts:uris()Ordered URI valuesMaterialises the final URI list without loading the documents themselves

This sample combines:

  1. cts:uri-match for URI-pattern scope.
  2. cts:collection-match for collection-pattern scope.
  3. cts:word-query for text evidence.
xquery version "1.0-ml";

let $uri-pattern := "/cleverllamas/llamaverse/raw/wild-llamas/llamas/*.json"
let $collection-pattern := "wild-*"
let $uri-matches := cts:uri-match($uri-pattern)[1 to 5]
let $collection-matches := cts:collection-match($collection-pattern)[1 to 5]
let $composed := cts:and-query((
  cts:document-query(cts:uri-match($uri-pattern)),
  cts:collection-query($collection-matches),
  cts:word-query("sung")
))
let $sample-uris :=
  for $doc in cts:search(fn:doc(), $composed)[1 to 5]
  return xdmp:node-uri($doc)
return
  map:entry("uriPattern", $uri-pattern)
  => map:with("collectionPattern", $collection-pattern)
  => map:with("uriMatches", $uri-matches)
  => map:with("collectionMatches", $collection-matches)
  => map:with("estimate", cts:estimate($composed))
  => map:with("sampleUris", $sample-uris)
'use strict';

const uriPattern = '/cleverllamas/llamaverse/raw/wild-llamas/llamas/*.json';
const collectionPattern = 'wild-*';
const uriMatches = cts.uriMatch(uriPattern).toArray().slice(0, 5);
const collectionMatches = cts.collectionMatch(collectionPattern).toArray().slice(0, 5);
const query = cts.andQuery([
  cts.documentQuery(cts.uriMatch(uriPattern)),
  cts.collectionQuery(collectionMatches),
  cts.wordQuery('sung')
]);
const sampleUris = cts.uris(null, null, query).toArray().slice(0, 5);

({
  uriPattern,
  collectionPattern,
  uriMatches,
  collectionMatches,
  estimate: cts.estimate(query),
  sampleUris
});


{"estimate":992, "uriPattern":"/cleverllamas/llamaverse/raw/wild-llamas/llamas/*.json", "collectionPattern":"wild-*", "uriMatches":["/cleverllamas/llamaverse/raw/wild-llamas/llamas/00048384-cb13-4557-805e-a6b4e57f7eab.json", "/cleverllamas/llamaverse/raw/wild-llamas/llamas/0005e261-4302-4ae8-9574-732a54040423.json", "/cleverllamas/llamaverse/raw/wild-llamas/llamas/000eca10-d166-469f-b17a-3c3b35ee0883.json", "/cleverllamas/llamaverse/raw/wild-llamas/llamas/002c7133-e985-4bd6-9ea8-f057fa0c6ee7.json", "/cleverllamas/llamaverse/raw/wild-llamas/llamas/0043e95e-3209-4b97-8871-f9b7073377f2.json"], "collectionMatches":"wild-llamas", "sampleUris":["/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json", "/cleverllamas/llamaverse/raw/wild-llamas/llamas/507cf4de-4469-497f-9888-7756c592b119.json", "/cleverllamas/llamaverse/raw/wild-llamas/llamas/19be9dcc-fd35-4c54-b503-63a6a8a1043d.json", "/cleverllamas/llamaverse/raw/wild-llamas/llamas/24a25bb9-b35f-48b3-b01c-d8f4a64d7a2d.json", "/cleverllamas/llamaverse/raw/wild-llamas/llamas/cd37f616-e707-47f3-99c0-d20def77c1a6.json"]}
estimateuriPatterncollectionPatternuriMatchescollectionMatchessampleUris
992/cleverllamas/llamaverse/raw/wild-llamas/llamas/*.jsonwild-*/cleverllamas/llamaverse/raw/wild-llamas/llamas/00048384-cb13-4557-805e-a6b4e57f7eab.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/0005e261-4302-4ae8-9574-732a54040423.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/000eca10-d166-469f-b17a-3c3b35ee0883.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/002c7133-e985-4bd6-9ea8-f057fa0c6ee7.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/0043e95e-3209-4b97-8871-f9b7073377f2.jsonwild-llamas/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/507cf4de-4469-497f-9888-7756c592b119.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/19be9dcc-fd35-4c54-b503-63a6a8a1043d.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/24a25bb9-b35f-48b3-b01c-d8f4a64d7a2d.json,/cleverllamas/llamaverse/raw/wild-llamas/llamas/cd37f616-e707-47f3-99c0-d20def77c1a6.json

What to notice: this keeps scope constraints declarative and reusable. You can inspect the URI and collection lexicon values first, then carry the same scope into cts:estimate() and cts:uris() without losing sight of which part of the plan is doing the work.

Ready for the Next Chapter?

Decision rule: compose constraints first, then tune options, and only then profile cost. That order prevents noisy optimisation work.

Once you're comfortable composing queries, these related topics open new doors:

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!