Search Composition Starter Pack
Build and inspect practical query blocks on top of llamaverse content
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 Item | What this pack uses |
|---|---|
| Data set | llamaverse v2.0+ sample data |
| Primary document scope | wild llama JSON documents (/cleverllamas/llamaverse/raw/wild-llamas/llamas/{uuid}.json) |
| Primary constraints | collection, directory, and composed cts query clauses |
| Fields used | None in these examples |
| Index context | Resolved 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
cts:search
The classic. Still the fastest way to stop pretending full-text is easy.
Source docs: https://docs.marklogic.com/11.0/cts:search
| Option / Argument | What it controls | Used here |
|---|---|---|
$expr | Root expression or document sequence to search | fn:doc() sequence for the sample scope |
$query | CTS query that defines match criteria | Composed query built in each sample |
$options | Search options as strings and/or cts:order constructors | Not set in this sample (defaults apply) |
$quality-weight | Quality contribution to scoring (1.0 default) | Not set in this sample |
$forest-ids | Forest IDs to constrain search scope | Not set in this sample |
$options Value | What it controls | Default 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 directives | No explicit order override |
cts:search documented option interactions
"filtered"and"unfiltered"select the match-verification mode."checked"and"unchecked"control position checking.- Fragment scope options (
"any","document","properties","locks") control which fragment classes are included. - Score options (
"score-logtfidf","score-logtf","score-simple","score-random","score-zero") select the scoring algorithm. cts:orderconstructors 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 / Argument | What it controls | Used here |
|---|---|---|
$queries | The list of sub-queries that must all match | Collection + directory constraints |
$options | Conjunction behaviour options | Not set in this sample (default applies) |
$options Value | What it controls | Default 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 / Argument | What it controls | Used here |
|---|---|---|
$query | The CTS query to estimate cardinality for | Composed collection/directory query |
$options | Search options applied during estimate | Not set in this sample (defaults apply) |
$quality-weight | Contribution of quality to scoring during estimate evaluation (1.0 default) | Not set in this sample |
$forest-ids | Forest IDs to constrain estimate scope | Not set in this sample |
$maximum | Stop counting after this threshold is reached | Not set in this sample |
$options Value | What it controls | Default 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 evaluation | No explicit order override |
cts:estimate documented option interactions
cts:estimateaccepts the same search-option families ascts:searchfor verification, scoring, position checking, fragment scope, and ordering.$maximumsets an upper bound for counting work by stopping after the specified threshold.$forest-idsconstrains the estimate to the specified forests.- 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
| Metric | Value |
|---|---|
| Result | 4561 |
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 / Argument | What it controls | Used here |
|---|---|---|
$nodes | Nodes (or values) tested for query match | A single llama document node from cts:search(...) |
$query | The query applied to each node | Word, 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}
| uri | containsImpossible | containsNearPair | containsSung |
|---|---|---|---|
/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json | false | false | true |
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 / Argument | What it controls | Used here |
|---|---|---|
$node | Node to traverse for matches | A llama document node |
$query | Match criteria used while walking text | cts:word-query("sung") |
$expr | Expression evaluated per match | Emits 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"}
| uri | start | match | text |
|---|---|---|---|
/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json | 114 | 1 | sung |
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 / Argument | What it controls | Used here |
|---|---|---|
$node | Node whose matching text is replaced | A compact snippet node derived from llama content |
$query | What should be highlighted | cts:word-query("sung") |
$expr | Replacement 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>
| Line | Value |
|---|---|
| 1 | {"uri":"/cleverllamas/llamaverse/raw/wild-llamas/llamas/0a6911e5-0d17-44e1-a114-cb747490f469.json", "firstHighlight":"sung", "highlightCount":1} |
| 2 | <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, 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 / Argument | What it controls | Used here |
|---|---|---|
$query | Search text to parse | AND/OR/NEAR expressions |
$bindings | Optional mapping for tagged and untagged terms | Shown 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"
]
}
]
| queryText | parsedPreview | sampleUris |
|---|---|---|
| sung AND llama | cts: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 wool | cts: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 wool | cts:near-query((cts:word-query("sung", ("lang=en"), 1), cts:word-query("wool", ("lang=en"), 1)), 8, ("unordered"), 1) | |
| (sung OR wool) AND llama | 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")) | /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"]}
| estimate | queryText | parsedPreview | sampleUris |
|---|---|---|---|
| 3 | name:Debra sung | cts: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 helper | What it returns | Why it matters |
|---|---|---|
cts:uri-match() | URI strings from the URI lexicon | Lets you start from a URI pattern instead of a full document walk |
cts:collection-match() | Collection names from the collection lexicon | Keeps collection filtering pattern-driven and reusable |
cts:document-query() | Document-fragment scope from a URI-based query | Bridges lexicon values into a searchable fragment constraint |
cts:collection-query() | Collection-fragment scope from collection values | Turns matched collection names into a composable search clause |
cts:uris() | Ordered URI values | Materialises the final URI list without loading the documents themselves |
This sample combines:
cts:uri-matchfor URI-pattern scope.cts:collection-matchfor collection-pattern scope.cts:word-queryfor 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"]}
| estimate | uriPattern | collectionPattern | uriMatches | collectionMatches | sampleUris |
|---|---|---|---|---|---|
| 992 | /cleverllamas/llamaverse/raw/wild-llamas/llamas/*.json | wild-* | /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 | wild-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:
- Text Query Building Starter Pack — The building blocks (
cts:word-query,cts:or-query, etc.) that power the composition patterns shown here. - Relevance and Scoring Starter Pack — Understand why certain results rank first once your composed queries return them.
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
- Functions in This Pack
- cts:search
- cts:search documented option interactions
- cts:and-query
- cts:estimate
- cts:estimate documented option interactions
- cts:contains
- cts:walk
- cts:highlight
- cts:parse
- cts:parse deep dive: token text to nested query constructors
- Lexicon-first scope composition