diff --git a/.gitignore b/.gitignore index 99abc08..899a4ac 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,9 @@ models/ embeddings/ node_modules/ .claude + +# Transient eval output — result.jsonl and compare.html/.md (recreated on demand) +evals/runs/ + +# local file for source map generation +llms-full.txt diff --git a/evals/bin/build-source-tree.js b/evals/bin/build-source-tree.js new file mode 100644 index 0000000..3bb53ea --- /dev/null +++ b/evals/bin/build-source-tree.js @@ -0,0 +1,102 @@ +/* eslint-disable no-console */ +// Build a Source tree from docs-resources/llms-full.txt so the eval can resolve +// EVERY doc section a corpus chunk covers — not just the one on its first +// `> Source:` line. A chunk is a slice of llms-full.txt spanning several +// headings; deeper headings carry their own `> Source:` line, but a chunk +// boundary can split a heading from its Source line. The tree recovers those. +// +// Output (evals/data/source-tree.json): +// { +// source: "", +// tree: { "": ["", ...] }, // page → its anchors +// byHeadingInPage: { "": { "": "" } }, +// byBreadcrumb: { " b > c lc>": "" }, // full heading path → url +// byLeaf: { "": "" } // leaf heading → url +// } +// +// byBreadcrumb / byLeaf let the eval score the live LLM-summary corpus, whose +// chunks are keyed by a breadcrumb ("The Bookshop Sample > Databases") instead of +// a `> Source:` URL. Regenerate with `npm run evals:build-source-tree`. Do not hand-edit. +import fs from 'fs/promises' +import path from 'path' +import { fileURLToPath } from 'url' + +const HERE = path.dirname(fileURLToPath(import.meta.url)) +const OUT = path.join(HERE, '..', 'data', 'source-tree.json') + +// Default location of the docs export; override with argv[2]. +const DEFAULT_SRC = '/Users/i543501/SAPDevelop/docs-resources/llms-full.txt' + +const HEADING = /^(#{1,6})\s+(.*\S)\s*$/ +const SOURCE = /^>\s*Source:\s*(\S+)/ + +// Strip a trailing VitePress heading attribute like `{.subtitle}`. +function cleanHeading(t) { + return t.replace(/\s*\{[^}]*\}\s*$/, '').trim() +} + +export function buildSourceTree(text) { + const tree = {} + const byHeadingInPage = {} + const byBreadcrumb = {} // full heading path ("A > B > C") → url + const byLeaf = {} // leaf heading ("C") → url (first occurrence wins) + const stack = [] // [{ level, text }] heading ancestry + let lastHeading = null // leaf text of the most recent heading + let lastCrumb = null // full path of the most recent heading + for (const line of text.split('\n')) { + const h = HEADING.exec(line) + if (h) { + const level = h[1].length + const txt = cleanHeading(h[2]) + while (stack.length && stack[stack.length - 1].level >= level) stack.pop() + stack.push({ level, text: txt }) + lastHeading = txt + lastCrumb = stack.map(s => s.text).join(' > ') + continue + } + const s = SOURCE.exec(line) + if (s) { + const url = s[1] + const page = url.split('#')[0] + const anchors = (tree[page] ||= []) + if (url.includes('#') && !anchors.includes(url)) anchors.push(url) + if (lastHeading !== null) { + const map = (byHeadingInPage[page] ||= {}) + // Page-scoped, so cross-page heading collisions can't occur; a heading + // repeated on ONE page keeps the first occurrence (the un-suffixed anchor). + const key = lastHeading.toLowerCase() + if (!(key in map)) map[key] = url + const crumbKey = lastCrumb.toLowerCase() + if (!(crumbKey in byBreadcrumb)) byBreadcrumb[crumbKey] = url + if (!(key in byLeaf)) byLeaf[key] = url + } + lastHeading = null + lastCrumb = null + } + } + return { tree, byHeadingInPage, byBreadcrumb, byLeaf } +} + +async function main() { + const srcPath = process.argv[2] ? path.resolve(process.argv[2]) : DEFAULT_SRC + const text = await fs.readFile(srcPath, 'utf8') + const { tree, byHeadingInPage, byBreadcrumb, byLeaf } = buildSourceTree(text) + const pages = Object.keys(tree).length + const anchors = Object.values(tree).reduce((n, a) => n + a.length, 0) + await fs.writeFile( + OUT, + JSON.stringify({ source: srcPath, tree, byHeadingInPage, byBreadcrumb, byLeaf }, null, 2) + ) + console.log( + `Wrote ${path.relative(process.cwd(), OUT)} — ${pages} pages, ${anchors} anchors, ` + + `${Object.keys(byBreadcrumb).length} breadcrumbs` + ) +} + +// Run as a script; importable for tests. +if (import.meta.url === `file://${process.argv[1]}`) { + main().catch(e => { + console.error(e) + process.exit(1) + }) +} diff --git a/evals/bin/compare.js b/evals/bin/compare.js new file mode 100644 index 0000000..8e4c4bf --- /dev/null +++ b/evals/bin/compare.js @@ -0,0 +1,40 @@ +/* eslint-disable no-console */ +// Entry point for `npm run evals:compare`: (re)build the comparison report. +// +// Optional CLI args: +// --runs path to a result.jsonl file OR a runs dir +// --out output path for compare.html / compare.md +// +// Examples: +// npm run evals:compare +// node evals/bin/compare.js --runs runs-xenova/result.jsonl +// node evals/bin/compare.js --runs runs-pplx/ --out runs-pplx/compare.html +import path from 'path' +import { compare } from '../lib/compare.js' + +const args = process.argv.slice(2) +const get = flag => { const i = args.indexOf(flag); return i !== -1 ? args[i + 1] : null } +const runsArg = get('--runs') +const outArg = get('--out') + +// runsArg may be a result.jsonl file or a directory — normalise to a dir. +let overrides = {} +let outPath = outArg || undefined + +if (runsArg) { + const abs = path.resolve(runsArg) + const isJsonl = abs.endsWith('.jsonl') + const runsDir = isJsonl ? path.dirname(abs) : abs + const resultsName = isJsonl ? path.basename(abs) : undefined + overrides.paths = { runsDir } + if (resultsName) overrides.output = { resultsName } + // default output alongside the jsonl when --out not given + if (!outPath && isJsonl) outPath = path.join(runsDir, 'compare.html') +} + +compare({ overrides, outPath }) + .then(r => process.exit(r.code)) + .catch(e => { + console.error(e) + process.exit(3) + }) diff --git a/evals/bin/eval.js b/evals/bin/eval.js new file mode 100644 index 0000000..136788a --- /dev/null +++ b/evals/bin/eval.js @@ -0,0 +1,19 @@ +/* eslint-disable no-console */ +// Entry point for `npm run evals`: evaluate once, then build the comparison report. +// +// This thin wrapper exists for one reason: search_docs reads CDS_MCP_OFFLINE at +// MODULE LOAD. It must be set before ./evaluate.js (which transitively imports +// the search tool) is loaded — hence the env assignment followed by a dynamic +// import. A static import here, or setting the env inside evaluate.js, would run +// too late. Offline scoring against the already-downloaded corpus keeps runs +// deterministic (no mid-run re-download). +process.env.CDS_MCP_OFFLINE = 'true' + +const { evaluateAndCompare } = await import('../lib/evaluate.js') + +evaluateAndCompare() + .then(r => process.exit(r.code)) + .catch(e => { + console.error(e) + process.exit(3) + }) diff --git a/evals/config.json b/evals/config.json new file mode 100644 index 0000000..29cff0f --- /dev/null +++ b/evals/config.json @@ -0,0 +1,20 @@ +{ + "k": 5, + "capire_version": "2026.5.0", + "paths": { + "goldenSet": "data/golden-set.json", + "runsDir": "runs" + }, + "gates": { + "recall_at_k": 0.8, + "mrr": 0.5, + "hit_rate_at_k": 0.8, + "precision_at_k": null, + "ndcg_at_k": null + }, + "output": { + "keepRuns": 100, + "resultsName": "result.jsonl", + "compareFormat": "html" + } +} diff --git a/evals/data/golden-set.json b/evals/data/golden-set.json new file mode 100644 index 0000000..f20a91d --- /dev/null +++ b/evals/data/golden-set.json @@ -0,0 +1,720 @@ +{ + "golden_set": "cap-golden-v3", + "description": "Frozen golden set for CAP MCP search_docs retrieval eval. Relevance ('relevant_doc_ids') is authored ONCE by a human and stored here; never recomputed at eval time. IDs are the deterministic 'Source:' URL parsed from each chunk's first line (which already carries the #section anchor; see evals/lib/ids.js). Ranking of retrieved docs comes from the embedding retriever, but identity/matching is pure string parsing. Labels favour canonical reference/guide pages over release-notes where a canonical page exists. If a re-index changes a page's URL, the id changes and the runner's pre-flight check flags it.", + "questions": [ + { + "id": "capire-1", + "question": "How to define key fields of domain entities?", + "relevant_doc_ids": [ + "/docs/guides/domain/#primary-keys", + "/docs/guides/databases/cdl-to-ddl#primary-key-constraints", + "/docs/cds/cdl#entity-definitions" + ] + }, + { + "id": "capire-2", + "question": "How to model relationships between entities in cds files?", + "relevant_doc_ids": [ + "/docs/guides/domain#associations", + "/docs/cds/cdl#associations" + ] + }, + { + "id": "capire-3", + "question": "Query field values of nested children", + "relevant_doc_ids": [ + "/docs/guides/services/served-ootb#deep-reads-and-writes", + "/docs/cds/cxl#path-navigation" + ] + }, + { + "id": "capire-4", + "question": "How do I register a before handler in CAP Node.js to validate incoming request data before the on handler runs?", + "relevant_doc_ids": [ + "/docs/node.js/core-services#srv-before-request", + "/docs/guides/services/custom-code#hooks-on-before-after" + ] + }, + { + "id": "capire-5", + "question": "What is the difference between on, before, and after event handlers in CAP Node.js?", + "relevant_doc_ids": [ + "/docs/node.js/core-services#srv-on-before-after", + "/docs/guides/services/custom-code#hooks-on-before-after" + ] + }, + { + "id": "capire-6", + "question": "How do I define a calculated element in CDS?", + "relevant_doc_ids": [ + "/docs/cds/cql#exists-predicate", + "/docs/cds/cdl#calculated-elements", + "/docs/guides/databases/performance#case-statement" + ] + }, + { + "id": "capire-7", + "question": "How do I query for Authors that have Books with a specific stock level in CAP?", + "relevant_doc_ids": [ + "/docs/cds/cxl#exists-infix-filter", + "/docs/cds/cxl#in-exists-predicates" + ] + }, + { + "id": "capire-8", + "question": "What type should I use for a currency field in my CDS entity?", + "relevant_doc_ids": [ + "/docs/cds/types", + "/docs/cds/common#code-types" + ] + }, + { + "id": "capire-9", + "question": "How do I configure messaging in my CAP Node.js application?", + "relevant_doc_ids": [ + "/docs/node.js/messaging#configuring-message-brokers", + "/docs/guides/events/event-queues#by-configuration" + ] + }, + { + "id": "capire-10", + "question": "What is the difference between IAS and XSUAA authentication in CAP and how do I set it up?", + "relevant_doc_ids": [ + "/docs/node.js/authentication#ias", + "/docs/node.js/authentication#xsuaa", + "/docs/guides/security/remote-authentication#ias-app-2-app" + ] + }, + { + "id": "capire-11", + "question": "How does cds watch react when I save a domain model and what is the inner development loop in CAP?", + "relevant_doc_ids": [ + "/docs/get-started/bookshop#databases", + "/docs/get-started/bookshop#inner-loop" + ] + }, + { + "id": "capire-12", + "question": "How can customers and partners extend SAP CAP applications?", + "relevant_doc_ids": [ + "/docs/get-started/features#intrinsic-extensibility" + ] + }, + { + "id": "capire-13", + "question": "How can I start Node.js apps on different ports?", + "relevant_doc_ids": [ + "/docs/get-started/get-help#nodejs" + ] + }, + { + "id": "capire-14", + "question": " (requested)'?", + "relevant_doc_ids": [ + "/docs/get-started/get-help#deployment-fails--version-incompatibility" + ] + }, + { + "id": "capire-15", + "question": "What is the difference between capture intent and how-to in CAP documentation?", + "relevant_doc_ids": [ + "/docs/guides/domain/#introduction" + ] + }, + { + "id": "capire-16", + "question": "How do I add UI annotations to my CDS entities?", + "relevant_doc_ids": [ + "/docs/guides/domain/#fiori-annotations" + ] + }, + { + "id": "capire-17", + "question": "How does deleting a root of a composition hierarchy affect the nested children?", + "relevant_doc_ids": [ + "/docs/guides/services/served-ootb#deep-delete" + ] + }, + { + "id": "capire-18", + "question": "How do I add constraints for input validation using annotations in CAP?", + "relevant_doc_ids": [ + "/docs/guides/services/constraints#input-validation" + ] + }, + { + "id": "capire-19", + "question": "How are localized models constructed in CAP?", + "relevant_doc_ids": [ + "/docs/guides/uis/i18n#merging-algorithm" + ] + }, + { + "id": "capire-20", + "question": "How do I use the common CDS annotations @title and @description?", + "relevant_doc_ids": [ + "/docs/guides/uis/fiori#prefer-title-and-description" + ] + }, + { + "id": "capire-21", + "question": "How can I use the ternary conditional operator in CQL?", + "relevant_doc_ids": [ + "/docs/guides/databases/cap-level-dbs#ternary--operator" + ] + }, + { + "id": "capire-22", + "question": "How do I skip generating a table for a CDS entity?", + "relevant_doc_ids": [ + "/docs/guides/databases/cdl-to-ddl#customizing-options" + ] + }, + { + "id": "capire-23", + "question": "How to deploy entities as hdbmigrationtable?", + "relevant_doc_ids": [ + "/docs/guides/databases/hana#enabling-hdbmigrationtable-generation" + ] + }, + { + "id": "capire-24", + "question": "How do I use a persistent SQLite database with CAP?", + "relevant_doc_ids": [ + "/docs/guides/databases/sqlite#using-persistent-databases" + ] + }, + { + "id": "capire-25", + "question": "How do I enable automatic schema evolution in my CAP application?", + "relevant_doc_ids": [ + "/docs/guides/databases/schema-evolution#automatic-migration" + ] + }, + { + "id": "capire-26", + "question": "How do I update a collection of entities with a single PATCH request in OData v4?", + "relevant_doc_ids": [ + "/docs/guides/protocols/odata#odata-patch-collection" + ] + }, + { + "id": "capire-27", + "question": "How are JSON payloads deserialized to Java types in CAP?", + "relevant_doc_ids": [ + "/docs/guides/protocols/odata#simple-types" + ] + }, + { + "id": "capire-28", + "question": "How are CDS types mapped to AsyncAPI supported types?", + "relevant_doc_ids": [ + "/docs/guides/protocols/asyncapi#mapping" + ] + }, + { + "id": "capire-29", + "question": "How do I create a denormalized view in a CAP service?", + "relevant_doc_ids": [ + "/docs/guides/integration/calesi#defining-service-apis" + ] + }, + { + "id": "capire-30", + "question": "What are the prerequisites for using SAP CAP?", + "relevant_doc_ids": [ + "/docs/guides/integration/data-federation#preliminaries" + ] + }, + { + "id": "capire-31", + "question": "How do I replace a service implementation in SAP CAP?", + "relevant_doc_ids": [ + "/docs/guides/integration/reuse-and-compose#in-nodejs" + ] + }, + { + "id": "capire-32", + "question": "How can I test updating a review in the reviews service sample?", + "relevant_doc_ids": [ + "/docs/guides/events/core-concepts#2-add-reviews" + ] + }, + { + "id": "capire-33", + "question": "How do I register event handlers for declared events in the messaging service?", + "relevant_doc_ids": [ + "/docs/guides/events/messaging#receive-events-from-messaging-service" + ] + }, + { + "id": "capire-34", + "question": "How do I override CAP's default security configuration?", + "relevant_doc_ids": [ + "/docs/guides/security/overview#key-concept-secure-by-default" + ] + }, + { + "id": "capire-35", + "question": "How do I configure the Application Router with XSUAA in a CAP project?", + "relevant_doc_ids": [ + "/docs/guides/security/authentication#ui-level-testing-1" + ] + }, + { + "id": "capire-36", + "question": "How do I call a public service without sharing the current user's information?", + "relevant_doc_ids": [ + "/docs/guides/security/cap-users#switching-to-anonymous-user" + ] + }, + { + "id": "capire-37", + "question": "How do I configure XSUAA attributes as optional?", + "relevant_doc_ids": [ + "/docs/guides/security/authorization#unrestricted-xsuaa-attributes" + ] + }, + { + "id": "capire-38", + "question": "How do I set up an Audit Log Service on SAP BTP for my CAP application?", + "relevant_doc_ids": [ + "/docs/guides/security/dpp-audit-logging#use-sap-audit-log-service" + ] + }, + { + "id": "capire-39", + "question": "What identity services does SAP BTP offer for managing and authenticating platform and business users?", + "relevant_doc_ids": [ + "/docs/guides/security/data-protection#authenticate-requests" + ] + }, + { + "id": "capire-40", + "question": "How do I add test data for local testing in CAP?", + "relevant_doc_ids": [ + "/docs/guides/extensibility/customization#add-test-data" + ] + }, + { + "id": "capire-41", + "question": "How can SaaS providers extend base CAP models with new fields, entities, and annotations?", + "relevant_doc_ids": [ + "/docs/guides/extensibility/feature-toggles#introduction-and-overview" + ] + }, + { + "id": "capire-42", + "question": "How does CAP select the native fetch client for outgoing requests?", + "relevant_doc_ids": [ + "/docs/guides/deploy/to-cf#native-fetch" + ] + }, + { + "id": "capire-43", + "question": "How can I customize the Helm chart generated by cds add kyma?", + "relevant_doc_ids": [ + "/docs/guides/deploy/to-kyma#modify" + ] + }, + { + "id": "capire-44", + "question": "What happens when a tenant container is deleted?", + "relevant_doc_ids": [ + "/docs/guides/multitenancy/#delete-sap-hana-tenants" + ] + }, + { + "id": "capire-45", + "question": "How do I configure an application using profiles in SAP CAP?", + "relevant_doc_ids": [ + "/docs/guides/multitenancy/mtxs#presets" + ] + }, + { + "id": "capire-46", + "question": "How do I configure the prefer header to trigger asynchronous extension activation?", + "relevant_doc_ids": [ + "/docs/guides/multitenancy/mtxs#http-request-options-1" + ] + }, + { + "id": "capire-47", + "question": "What does the technical tenant t0 store?", + "relevant_doc_ids": [ + "/docs/guides/multitenancy/mtxs#about-technical-tenant-t0" + ] + }, + { + "id": "capire-48", + "question": "How can I develop CAP applications using Java?", + "relevant_doc_ids": [ + "/docs/java/getting-started#introduction" + ] + }, + { + "id": "capire-49", + "question": "How is the active feature set represented in CAP Java requests?", + "relevant_doc_ids": [ + "/docs/java/reflection-api#feature-toggles-info-provider" + ] + }, + { + "id": "capire-50", + "question": "How can I use typed access to process query results in SAP CAP?", + "relevant_doc_ids": [ + "/docs/java/cds-data#typed-access-to-query-results" + ] + }, + { + "id": "capire-51", + "question": "How can I use a path expression to select from an entity set in CQl?", + "relevant_doc_ids": [ + "/docs/java/working-with-cql/query-api#from-reference" + ] + }, + { + "id": "capire-52", + "question": "How do I perform bulk upserts in CAP Java?", + "relevant_doc_ids": [ + "/docs/java/working-with-cql/query-api#bulk-upsert" + ] + }, + { + "id": "capire-53", + "question": "What types of string expressions are supported in CAP?", + "relevant_doc_ids": [ + "/docs/java/working-with-cql/query-api#concat-expression" + ] + }, + { + "id": "capire-54", + "question": "How do I query parameterized views in SAP HANA with named parameters?", + "relevant_doc_ids": [ + "/docs/java/working-with-cql/query-execution#querying-views" + ] + }, + { + "id": "capire-55", + "question": "How do I resolve CDS entities in a CQL query?", + "relevant_doc_ids": [ + "/docs/java/working-with-cql/query-introspection#usage" + ] + }, + { + "id": "capire-56", + "question": "What is an example configuration for using SQLite with CAP?", + "relevant_doc_ids": [ + "/docs/java/cqn-services/persistence-services#file-based-storage" + ] + }, + { + "id": "capire-57", + "question": "How do I consume a remote API exposed by a BTP reuse service in a CAP application?", + "relevant_doc_ids": [ + "/docs/java/cqn-services/remote-services#binding-to-a-reuse-service" + ] + }, + { + "id": "capire-58", + "question": "How do I use service instances in event handlers for CDS services?", + "relevant_doc_ids": [ + "/docs/java/event-handlers/#servicearguments" + ] + }, + { + "id": "capire-59", + "question": "How can I control how long a processing entry is held before another instance picks it up?", + "relevant_doc_ids": [ + "/docs/java/event-queues#status-lock-timeout" + ] + }, + { + "id": "capire-60", + "question": "How do I configure message routing between multiple message brokers in SAP CAP?", + "relevant_doc_ids": [ + "/docs/java/messaging#composite-messaging-service" + ] + }, + { + "id": "capire-61", + "question": "How do I define advanced identifiers for associated entities in SAP CAP?", + "relevant_doc_ids": [ + "/docs/java/change-tracking#tips-and-tricks" + ] + }, + { + "id": "capire-62", + "question": "How do I transform an authenticated user and inject it as UserInfo for a request in CAP?", + "relevant_doc_ids": [ + "/docs/java/security#custom-users" + ] + }, + { + "id": "capire-63", + "question": "How do I use the CDS Maven plugin to watch my application and automatically rebuild on changes?", + "relevant_doc_ids": [ + "/docs/java/developing-applications/running#cds-watch" + ] + }, + { + "id": "capire-64", + "question": "How are correlation IDs handled in CAP Java SDK?", + "relevant_doc_ids": [ + "/docs/java/operating-applications/observability#correlation-ids" + ] + }, + { + "id": "capire-65", + "question": "How do I implement a protocol adapter in CAP?", + "relevant_doc_ids": [ + "/docs/java/building-plugins#protocol-adapter" + ] + }, + { + "id": "capire-66", + "question": "How does the new behavior of expand and inline affect draft-enabled entities in CAP?", + "relevant_doc_ids": [ + "/docs/java/migration#star-expand-and-inline-all-are-no-longer-permitted" + ] + }, + { + "id": "capire-67", + "question": "How do I get the CdsModelProvider with user info and feature toggles?", + "relevant_doc_ids": [ + "/docs/java/migration#comsapcdsservicesruntime" + ] + }, + { + "id": "capire-68", + "question": "What are the submodules referenced in the cds object?", + "relevant_doc_ids": [ + "/docs/node.js/cds-facade#refs-to-submodules" + ] + }, + { + "id": "capire-69", + "question": "How do I convert a CSN file to an AsyncAPI document in SAP CAP?", + "relevant_doc_ids": [ + "/docs/node.js/cds-compile#asyncapi" + ] + }, + { + "id": "capire-70", + "question": "What are the convenient shortcuts for accessing entity definitions in CAP?", + "relevant_doc_ids": [ + "/docs/node.js/cds-reflect#-actions-1" + ] + }, + { + "id": "capire-71", + "question": "How can I customize server startup using the cds-plugin package?", + "relevant_doc_ids": [ + "/docs/node.js/cds-server#see-also" + ] + }, + { + "id": "capire-72", + "question": "How do I specify a service name in the requires configuration?", + "relevant_doc_ids": [ + "/docs/node.js/cds-connect#cdsrequiressrvservice" + ] + }, + { + "id": "capire-73", + "question": "What is executed before a request is handled in an SAP CAP service?", + "relevant_doc_ids": [ + "/docs/node.js/core-services#srv-before-request" + ] + }, + { + "id": "capire-74", + "question": "How do I configure CSRF-token handling for a remote service in CAP?", + "relevant_doc_ids": [ + "/docs/node.js/remote-services#remoteservice-configuration" + ] + }, + { + "id": "capire-75", + "question": "How do I start a transaction manually in SAP CAP?", + "relevant_doc_ids": [ + "/docs/node.js/databases#db-begin" + ] + }, + { + "id": "capire-76", + "question": "How do I record multiple errors in a CAP request?", + "relevant_doc_ids": [ + "/docs/node.js/events#req-error" + ] + }, + { + "id": "capire-77", + "question": "How do I create an object stream and call a callback for each object in SAP CAP?", + "relevant_doc_ids": [ + "/docs/node.js/cds-ql#foreach" + ] + }, + { + "id": "capire-78", + "question": "How do I set the format for new loggers in CAP?", + "relevant_doc_ids": [ + "/docs/node.js/cds-log#cds-log-format" + ] + }, + { + "id": "capire-79", + "question": "How do I customize the default locale in CDS?", + "relevant_doc_ids": [ + "/docs/node.js/cds-i18n#using-default-locales" + ] + }, + { + "id": "capire-80", + "question": "How do I set environment variables for the CDS server on UNIX-based systems?", + "relevant_doc_ids": [ + "/docs/node.js/cds-env#on-the-command-line" + ] + }, + { + "id": "capire-81", + "question": "How do I delete messages from the outbox in SAP CAP?", + "relevant_doc_ids": [ + "/docs/node.js/event-queues#inspecting-cdsoutboxmessages" + ] + }, + { + "id": "capire-82", + "question": "How can I check if a user has a specific role in my CAP application?", + "relevant_doc_ids": [ + "/docs/node.js/authentication#user-is" + ] + }, + { + "id": "capire-83", + "question": "How do I test my CAP application with cds.test?", + "relevant_doc_ids": [ + "/docs/node.js/cds-plugins#usage-in-a-cap-project" + ] + }, + { + "id": "capire-84", + "question": "How do I add TypeScript support to my SAP CAP project?", + "relevant_doc_ids": [ + "/docs/node.js/typescript#enable-typescript-support" + ] + }, + { + "id": "capire-85", + "question": "How do I use the Audit Logging plugin for CAP Node.js?", + "relevant_doc_ids": [ + "/docs/plugins/#as-cds-plugins-for-nodejs" + ] + }, + { + "id": "capire-86", + "question": "When are the planned release schedules for major versions of SAP CAP?", + "relevant_doc_ids": [ + "/docs/releases/#major-versions" + ] + }, + { + "id": "capire-87", + "question": "What built-in or native alternatives has SAP CAP replaced third-party dependencies with?", + "relevant_doc_ids": [ + "/docs/releases/2026/jun26#going-native" + ] + }, + { + "id": "capire-88", + "question": "How do I extend a view definition with CQL clauses?", + "relevant_doc_ids": [ + "/docs/releases/2026/apr26#extending-views-with-cql-clauses" + ] + }, + { + "id": "capire-89", + "question": "How has the SAP CAP documentation been updated and improved?", + "relevant_doc_ids": [ + "/docs/releases/2026/jan26#capire-renovations" + ] + }, + { + "id": "capire-90", + "question": "How do I implement deep updates for draft data in SAP CAP?", + "relevant_doc_ids": [ + "/docs/releases/2025/dec25#important-changes-in-java" + ] + }, + { + "id": "capire-91", + "question": "How can I improve the localized data documentation?", + "relevant_doc_ids": [ + "/docs/releases/2025/nov25#capire-updates" + ] + }, + { + "id": "capire-92", + "question": "What happened to the previous SAP CAP samples repository?", + "relevant_doc_ids": [ + "/docs/releases/2025/aug25#github-discussions" + ] + }, + { + "id": "capire-93", + "question": "How do I access the Implementations (Experimental) section in SAP CAP?", + "relevant_doc_ids": [ + "/docs/releases/2025/jul25#tools" + ] + }, + { + "id": "capire-94", + "question": "How do I use the Technical Outbox API in CAP Java?", + "relevant_doc_ids": [ + "/docs/releases/2025/may25#in-java" + ] + }, + { + "id": "capire-95", + "question": "How does the CDS Code Formatter format case expressions?", + "relevant_doc_ids": [ + "/docs/releases/2025/may25#compact-formatting-of-case-expressions" + ] + }, + { + "id": "capire-96", + "question": "What resources are available for learning SAP CAP?", + "relevant_doc_ids": [ + "/docs/resources/#if-you-are-new-to-cap" + ] + }, + { + "id": "capire-97", + "question": "How do I check the installed version of the SAP CAP packages?", + "relevant_doc_ids": [ + "/docs/tools/cds-cli#cds-version" + ] + }, + { + "id": "capire-98", + "question": "How do I start a Maven-based Spring Boot application in debug mode with cds debug?", + "relevant_doc_ids": [ + "/docs/tools/cds-cli#local-applications-1" + ] + }, + { + "id": "capire-99", + "question": "How do I add and stop at breakpoints in my service implementation?", + "relevant_doc_ids": [ + "/docs/tools/cds-editors#debug-services" + ] + }, + { + "id": "capire-100", + "question": "How do I use Markdown rendering in CAP code?", + "relevant_doc_ids": [ + "/docs/tools/cds-editors#github-integration" + ] + } + ] +} \ No newline at end of file diff --git a/evals/data/sourceMap.json b/evals/data/sourceMap.json new file mode 100644 index 0000000..9408274 --- /dev/null +++ b/evals/data/sourceMap.json @@ -0,0 +1,19512 @@ +[ + { + "source": "/docs/get-started/", + "title": "Getting Started", + "depth": 1 + }, + { + "source": "/docs/get-started/#initial-setup", + "title": "Initial Setup", + "depth": 2 + }, + { + "source": "/docs/get-started/#nodejs-and-cds-dk", + "title": "Node.js and _cds-dk_", + "depth": 3 + }, + { + "source": "/docs/get-started/#java-and-maven", + "title": "Java and Maven", + "depth": 3 + }, + { + "source": "/docs/get-started/#git-and-github", + "title": "Git and GitHub", + "depth": 3 + }, + { + "source": "/docs/get-started/#visual-studio-code", + "title": "Visual Studio Code", + "depth": 3 + }, + { + "source": "/docs/get-started/#visual-studio-code-proposed-extensions", + "title": "Visual Studio Code Proposed Extensions", + "depth": 4 + }, + { + "source": "/docs/get-started/#command-line-interface", + "title": "Command Line Interface", + "depth": 2 + }, + { + "source": "/docs/get-started/#the-cds-command", + "title": "The `cds` command", + "depth": 3 + }, + { + "source": "/docs/get-started/#cds-version", + "title": "`cds version`", + "depth": 3 + }, + { + "source": "/docs/get-started/#jumpstart-projects", + "title": "Jumpstart Projects", + "depth": 2 + }, + { + "source": "/docs/get-started/#cds-init", + "title": "`cds init`", + "depth": 3 + }, + { + "source": "/docs/get-started/#cds-watch", + "title": "`cds watch`", + "depth": 3 + }, + { + "source": "/docs/get-started/#grow-as-you-go", + "title": "Grow as You Go...", + "depth": 2 + }, + { + "source": "/docs/get-started/#cds-add", + "title": "`cds add`", + "depth": 3 + }, + { + "source": "/docs/get-started/#cds-up", + "title": "`cds up`", + "depth": 3 + }, + { + "source": "/docs/get-started/#stay-up-to-date", + "title": "Stay up to Date!", + "depth": 2 + }, + { + "source": "/docs/get-started/#next-bookshop", + "title": "Next: Bookshop", + "depth": 2 + }, + { + "source": "/docs/get-started/bookshop", + "title": "The Bookshop Sample", + "depth": 1 + }, + { + "source": "/docs/get-started/bookshop#jumpstarting-projects", + "title": "Jumpstarting Projects", + "depth": 2 + }, + { + "source": "/docs/get-started/bookshop#domain-models", + "title": "Domain Models", + "depth": 2 + }, + { + "source": "/docs/get-started/bookshop#entity-relationship-models", + "title": "Entity-Relationship Models", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#complete-domain-model", + "title": "Complete Domain Model", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#focus-on-domain", + "title": "Focus on Domain", + "depth": 6 + }, + { + "source": "/docs/get-started/bookshop#compile-to-csn-", + "title": "Compile to CSN, ...", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#databases", + "title": "Databases", + "depth": 2 + }, + { + "source": "/docs/get-started/bookshop#inner-loop", + "title": "Inner Loop", + "depth": 6 + }, + { + "source": "/docs/get-started/bookshop#compile-to-sql", + "title": "Compile to SQL", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#add-initial-data", + "title": "Add Initial Data", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#querying-data", + "title": "Querying Data", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#using-cds-repl", + "title": "Using cds repl", + "depth": 6 + }, + { + "source": "/docs/get-started/bookshop#services", + "title": "Services", + "depth": 2 + }, + { + "source": "/docs/get-started/bookshop#use-case-specific-services", + "title": "Use Case-Specific Services", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#services-as-interfaces", + "title": "Services as Interfaces", + "depth": 6 + }, + { + "source": "/docs/get-started/bookshop#services-as-facades", + "title": "Services as Facades", + "depth": 6 + }, + { + "source": "/docs/get-started/bookshop#use-case-oriented-services", + "title": "Use Case-Oriented Services", + "depth": 6 + }, + { + "source": "/docs/get-started/bookshop#served-out-of-the-box", + "title": "Served Out-of-the-Box", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#send-requests-from-browser", + "title": "Send Requests from Browser", + "depth": 4 + }, + { + "source": "/docs/get-started/bookshop#send-requests-from-rest-client", + "title": "Send Requests from REST Client", + "depth": 4 + }, + { + "source": "/docs/get-started/bookshop#compile-to-edmx", + "title": "Compile to EDMX", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#querying", + "title": "Querying", + "depth": 2 + }, + { + "source": "/docs/get-started/bookshop#querying-primary-database", + "title": "Querying Primary Database", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#querying-app-services", + "title": "Querying App Services", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#pushed-down-to-db-1", + "title": "Pushed down to DB 1", + "depth": 6 + }, + { + "source": "/docs/get-started/bookshop#cap-level-integration", + "title": "CAP-level Integration", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#cap-level-service-integration", + "title": "CAP-level Service Integration", + "depth": 6 + }, + { + "source": "/docs/get-started/bookshop#calesi", + "title": "Calesi", + "depth": 6 + }, + { + "source": "/docs/get-started/bookshop#serving-uis", + "title": "Serving UIs", + "depth": 2 + }, + { + "source": "/docs/get-started/bookshop#generic-indexhtml", + "title": "Generic *index.html*", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#sap-fiori-uis", + "title": "SAP Fiori UIs", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#vuejs-uis", + "title": "Vue.js UIs", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#vuejs-uis-1", + "title": "Vuejs UIs", + "depth": 6 + }, + { + "source": "/docs/get-started/bookshop#custom-logic", + "title": "Custom Logic", + "depth": 2 + }, + { + "source": "/docs/get-started/bookshop#declarative-constraints", + "title": "Declarative Constraints", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#pushed-down-to-db-2", + "title": "Pushed down to DB 2", + "depth": 6 + }, + { + "source": "/docs/get-started/bookshop#separation-of-concerns", + "title": "Separation of Concerns", + "depth": 6 + }, + { + "source": "/docs/get-started/bookshop#custom-handlers-in-nodejs", + "title": "Custom Handlers in Node.js", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#custom-handlers-in-java", + "title": "Custom Handlers in Java", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#custom-actions", + "title": "Custom Actions", + "depth": 3 + }, + { + "source": "/docs/get-started/bookshop#summary", + "title": "Summary", + "depth": 2 + }, + { + "source": "/docs/get-started/concepts", + "title": "Core Concepts of CAP", + "depth": 1 + }, + { + "source": "/docs/get-started/concepts#introduction", + "title": "Introduction", + "depth": 2 + }, + { + "source": "/docs/get-started/concepts#primary-building-blocks", + "title": "Primary Building Blocks", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#models-fuel-runtimes", + "title": "Models fuel Runtimes", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#concepts-overview", + "title": "Concepts Overview", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#domain-models", + "title": "Domain Models", + "depth": 2 + }, + { + "source": "/docs/get-started/concepts#definition-language-cdl", + "title": "Definition Language (CDL)", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#associations", + "title": "Associations", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#aspects", + "title": "Aspects", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#services", + "title": "Services", + "depth": 2 + }, + { + "source": "/docs/get-started/concepts#services-as-interfaces", + "title": "Services as Interfaces", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#services-as-facades", + "title": "Services as Facades", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#service-providers", + "title": "Service Providers", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#not-microservices", + "title": "Not Microservices", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#events", + "title": "Events", + "depth": 2 + }, + { + "source": "/docs/get-started/concepts#event-handlers", + "title": "Event Handlers", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#event-listeners", + "title": "Event Listeners", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#sync--async", + "title": "Sync / Async", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#local--remote", + "title": "Local / Remote", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#data", + "title": "Data", + "depth": 2 + }, + { + "source": "/docs/get-started/concepts#extensible-data", + "title": "Extensible Data", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#queried-data", + "title": "Queried Data", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#passive-data", + "title": "Passive Data", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#querying", + "title": "Querying", + "depth": 2 + }, + { + "source": "/docs/get-started/concepts#query-language-cql", + "title": "Query Language (CQL)", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#queries-at-runtime", + "title": "Queries at Runtime", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#push-down-to-databases", + "title": "Push-Down to Databases", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#agnostic-by-design", + "title": "Agnostic by Design", + "depth": 2 + }, + { + "source": "/docs/get-started/concepts#hexagonal-architecture", + "title": "Hexagonal Architecture", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#cap-as-an-implementation-of-hexagonal-architecture", + "title": "CAP as an implementation of Hexagonal Architecture", + "depth": 4 + }, + { + "source": "/docs/get-started/concepts#application-domain", + "title": "Application Domain", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#see-also", + "title": "See Also...", + "depth": 4 + }, + { + "source": "/docs/get-started/concepts#core-domain-model", + "title": "Entities ⇒ Core Domain Model", + "depth": 4 + }, + { + "source": "/docs/get-started/concepts#services--application-model", + "title": "Services ⇒ Application Model", + "depth": 4 + }, + { + "source": "/docs/get-started/concepts#protocol-adapters", + "title": "Protocol Adapters", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#framework-services", + "title": "Framework Services", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#intrinsic-extensibility", + "title": "Intrinsic Extensibility", + "depth": 2 + }, + { + "source": "/docs/get-started/concepts#extending-models", + "title": "Extending Models", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#extension-logic", + "title": "Extension Logic", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#extensible-framework", + "title": "Extensible Framework", + "depth": 3 + }, + { + "source": "/docs/get-started/concepts#the-calesi-pattern", + "title": "The Calesi Pattern", + "depth": 2 + }, + { + "source": "/docs/get-started/features", + "title": "Introduction to CAP", + "depth": 1 + }, + { + "source": "/docs/get-started/features#what-is-cap", + "title": "What is CAP?", + "depth": 2 + }, + { + "source": "/docs/get-started/features#jumpstart--grow-as-you-go", + "title": "Jumpstart & Grow As You Go...", + "depth": 2 + }, + { + "source": "/docs/get-started/features#grow-as-you-go", + "title": "grow-as-you-go", + "depth": 6 + }, + { + "source": "/docs/get-started/features#jumpstarting-projects", + "title": "Jumpstarting Projects", + "depth": 3 + }, + { + "source": "/docs/get-started/features#growing-as-you-go", + "title": "Growing as You Go...", + "depth": 3 + }, + { + "source": "/docs/get-started/features#fast-inner-loops", + "title": "Fast Inner Loops", + "depth": 3 + }, + { + "source": "/docs/get-started/features#agnostic-microservices", + "title": "Agnostic Microservices", + "depth": 3 + }, + { + "source": "/docs/get-started/features#late-cut-microservices", + "title": "Late-cut Microservices", + "depth": 3 + }, + { + "source": "/docs/get-started/features#parallelized-workflows", + "title": "Parallelized Workflows", + "depth": 3 + }, + { + "source": "/docs/get-started/features#proven-best-practices", + "title": "Proven Best Practices", + "depth": 2 + }, + { + "source": "/docs/get-started/features#served-out-of-the-box", + "title": "Served Out Of The Box", + "depth": 3 + }, + { + "source": "/docs/get-started/features#enterprise-best-practices", + "title": "Enterprise Best Practices", + "depth": 3 + }, + { + "source": "/docs/get-started/features#the-calesi-effect", + "title": "The 'Calesi' Effect", + "depth": 3 + }, + { + "source": "/docs/get-started/features#intrinsic-extensibility", + "title": "Intrinsic Extensibility ", + "depth": 3 + }, + { + "source": "/docs/get-started/features#cloud-native-by-design", + "title": "Cloud-Native by Design", + "depth": 3 + }, + { + "source": "/docs/get-started/features#open-and-opinionated", + "title": "Open _and_ Opinionated", + "depth": 3 + }, + { + "source": "/docs/get-started/features#focus-on-domain", + "title": "Focus on Domain", + "depth": 2 + }, + { + "source": "/docs/get-started/features#conceptual-modeling-by-cds", + "title": "Conceptual Modeling by CDS ", + "depth": 3 + }, + { + "source": "/docs/get-started/features#domain-driven-design", + "title": "Domain-Driven Design ", + "depth": 3 + }, + { + "source": "/docs/get-started/features#rapid-development", + "title": "Rapid Development ", + "depth": 3 + }, + { + "source": "/docs/get-started/features#minimal-distraction", + "title": "Minimal Distraction ", + "depth": 3 + }, + { + "source": "/docs/get-started/features#avoid-technical-debt", + "title": "Avoid Technical Debt", + "depth": 2 + }, + { + "source": "/docs/get-started/features#less-code--less-mistakes", + "title": "Less Code → Less Mistakes", + "depth": 3 + }, + { + "source": "/docs/get-started/features#single-points-to-fix", + "title": "Single Points to Fix", + "depth": 3 + }, + { + "source": "/docs/get-started/features#minimized-lock-ins", + "title": "Minimized Lock-Ins", + "depth": 3 + }, + { + "source": "/docs/get-started/features#what-about-ai", + "title": "What about AI? ", + "depth": 2 + }, + { + "source": "/docs/get-started/learn-more", + "title": "Learning Sources", + "depth": 1 + }, + { + "source": "/docs/get-started/learn-more#the-capire-documentation", + "title": "The _capire_ Documentation", + "depth": 2 + }, + { + "source": "/docs/get-started/learn-more#callouts-and-alerts", + "title": "Callouts and Alerts", + "depth": 4 + }, + { + "source": "/docs/get-started/learn-more#the-capire-samples", + "title": "The _capire_ Samples", + "depth": 2 + }, + { + "source": "/docs/get-started/learn-more#featured-samples", + "title": "Featured Samples", + "depth": 2 + }, + { + "source": "/docs/get-started/learn-more#partner-reference-app", + "title": "Partner Reference App", + "depth": 3 + }, + { + "source": "/docs/get-started/learn-more#star-wars-app", + "title": "Star Wars App", + "depth": 3 + }, + { + "source": "/docs/get-started/learn-more#btp-susaas-app", + "title": "BTP SuSaaS App", + "depth": 3 + }, + { + "source": "/docs/get-started/learn-more#the-qmacro-series", + "title": "The *qmacro* Series", + "depth": 2 + }, + { + "source": "/docs/get-started/learn-more#videos", + "title": "Videos", + "depth": 3 + }, + { + "source": "/docs/get-started/learn-more#blog-post-series", + "title": "Blog post series", + "depth": 3 + }, + { + "source": "/docs/get-started/learn-more#selected-individual-articles", + "title": "Selected individual articles", + "depth": 3 + }, + { + "source": "/docs/get-started/learn-more#workshop-exercise-content", + "title": "Workshop exercise content", + "depth": 3 + }, + { + "source": "/docs/get-started/learn-more#miscellaneous", + "title": "Miscellaneous", + "depth": 3 + }, + { + "source": "/docs/get-started/learn-more#sap-learning-sources", + "title": "SAP Learning Sources", + "depth": 2 + }, + { + "source": "/docs/get-started/learn-more#hands-ons--codejams", + "title": "Hands-Ons & CodeJams", + "depth": 2 + }, + { + "source": "/docs/get-started/learn-more#blog-posts--other-material", + "title": "Blog Posts & Other Material", + "depth": 2 + }, + { + "source": "/docs/get-started/get-help", + "title": "Getting Help", + "depth": 1 + }, + { + "source": "/docs/get-started/get-help#setup", + "title": "Setup", + "depth": 2 + }, + { + "source": "/docs/get-started/get-help#vscode-macos", + "title": "Can't start VS Code from Command Line on macOS", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#node-version", + "title": "Check the Node.js version", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#check-access-permissions-on-macos-or-linux", + "title": "Check access permissions on macOS or Linux", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#check-if-your-environment-variables-are-properly-set-on-windows", + "title": "Check if your environment variables are properly set on Windows", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#cds-versions", + "title": "Updating CDS Versions", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#nodejs", + "title": "Node.js", + "depth": 2 + }, + { + "source": "/docs/get-started/get-help#how-can-i-start-nodejs-apps-on-different-ports", + "title": "How can I start Node.js apps on different ports?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-do-i-lose-registered-event-handlers", + "title": "Why do I lose registered event handlers?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#do", + "title": "DO:", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#dont", + "title": "DON'T:", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#why-does-my-app-not-show-up-in-dynatrace", + "title": "Why does my app not show up in Dynatrace?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-are-requests-rejected-with-hana-timeout-errors", + "title": "Why are requests rejected with HANA timeout errors?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-are-requests-rejected-with-431-and-not-logged", + "title": "Why are requests rejected with `431` and not logged?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-are-requests-rejected-with-502", + "title": "Why are requests rejected with `502`?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-are-requests-rejected-with-504", + "title": "Why are requests rejected with `504`?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#how-to-fix-no-service-definition-found-for-xyz", + "title": "How to fix `no service definition found for `?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-does-my-remote-service-call-not-work", + "title": "Why does my remote service call not work?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-is-a-destination-not-correctly-retrieved-by-sap-cloud-sdk", + "title": "Why is a destination not correctly retrieved by SAP Cloud SDK?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-are-type-definitions-for-sapcds-not-found-or-incomplete", + "title": "Why are type definitions for `@sap/cds` not found or incomplete?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#install-as-dev-dependency", + "title": "Install as dev dependency", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#fix-missing-symlink", + "title": "Fix missing symlink", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#how-to-fix-tar-error-is-not-recoverable-exiting-now", + "title": "How to fix \"`tar: Error is not recoverable: exiting now`\"?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#how-to-fix-sqlerror-invalid-table-name-could-not-find-tableview-", + "title": "How to fix \"SqlError: invalid table name: Could not find table/view ...\"?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#how-to-fix-error-could-not-locate-the-bindings-file-tried-", + "title": "How to fix \"`Error: Could not locate the bindings file. Tried: ...`\"", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#java", + "title": "Java", + "depth": 2 + }, + { + "source": "/docs/get-started/get-help#how-to-bypass-authorization-checks", + "title": "How to bypass authorization checks?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-do-i-get-a-user-should-not-exist-error-during-build-time", + "title": "Why do I get a \"User should not exist\" error during build time?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-do-i-get-an-error-on-server-start", + "title": "Why do I get an \"Error on server start\"?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#how-can-i-expose-custom-rest-apis-with-cap", + "title": "How can I expose custom REST APIs with CAP?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#how-can-i-build-a-cap-java-application-without-sql-database", + "title": "How can I build a CAP Java application without SQL database?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#what-to-do-about-maven-related-errors-in-eclipses-problems-view", + "title": "What to do about Maven-related errors in Eclipse's _Problems_ view?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#odata", + "title": "OData", + "depth": 2 + }, + { + "source": "/docs/get-started/get-help#how-do-i-generate-an-odata-response-in-nodejs-for-error-404", + "title": "How do I generate an OData response in Node.js for Error 404?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-do-some-requests-fail-if-i-set-odatadraftenabled-on-my-entity", + "title": "Why do some requests fail if I set `@odata.draft.enabled` on my entity?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#sqlite", + "title": "SQLite", + "depth": 2 + }, + { + "source": "/docs/get-started/get-help#how-do-i-install-sqlite-on-windows", + "title": "How do I install SQLite on Windows?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#hana", + "title": "SAP HANA", + "depth": 2 + }, + { + "source": "/docs/get-started/get-help#get-hana", + "title": "How to get an SAP HANA Cloud instance for SAP BTP?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#how-do-i-resolve-deployment-errors", + "title": "How do I resolve deployment errors?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#deployment-fails--cyclic-dependencies-found-or-cycle-between-files", + "title": "Deployment fails — _Cyclic dependencies found_ or _Cycle between files_", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#deployment-fails--version-incompatibility", + "title": "Deployment fails — _Version incompatibility_", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#root-cert-change", + "title": "Deployment fails - _unable to get local issuer certificate_", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#cannot-create-certificate-store", + "title": "Deployment fails — _Cannot create certificate store_", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#deployment-fails-", + "title": "Deployment fails —", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#deployment-fails--ssl-certificate-validation-failed-error-code-337047686", + "title": "Deployment fails — SSL certificate validation failed: error code: 337047686", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#deployment-fails--cannot-create-ssl-engine-received-invalid-ssl-record-header", + "title": "Deployment fails — _Cannot create SSL engine: Received invalid SSL Record Header_", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#deployment-fails--error-hdi-make-failed", + "title": "Deployment fails — _Error: HDI make failed_", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#connection-failed-89008", + "title": "Deployment fails — _Connection failed (RTE:[89008] Socket closed by peer_", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#hybrid-testing-connectivity-issue--resourcerequest-timed-out", + "title": "Hybrid testing connectivity issue — _ResourceRequest timed out_ {}", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#missingPlugin", + "title": "Deployment fails — _... build plugin for file suffix \"hdbmigrationtable\" [8210015]_", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#deployment-fails--in-using-declarations-only-main-artifacts-can-be-accessed-not-sub-artifacts-of-name", + "title": "Deployment fails — _In USING declarations only main artifacts can be accessed, not sub artifacts of \\_", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#deployment-fails--the-include_filter-definitions--use-key-values-that-are-not-disjunct", + "title": "Deployment fails — _The include_filter definitions ... use key values that are not disjunct_", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#hana-csv", + "title": "Why is removed sample _.csv_ deployed and overwriting existing data?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#how-do-i-keep-existing-data", + "title": "How do I keep existing data?", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#how-can-a-table-function-access-the-logged-in-user", + "title": "How can a table function access the logged in user?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#mtxs", + "title": "MTXS", + "depth": 2 + }, + { + "source": "/docs/get-started/get-help#why-is-my-mtx-sidecar-is-killed-with-exit-status-137", + "title": "Why is my MTX sidecar is killed with 'Exit status 137'?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#how-do-i-get-detailed-sap-hana-deployment-logs", + "title": "How do I get detailed SAP HANA deployment logs", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-do-i-get-extensions-exist-but-extensibility-is-disabled", + "title": "Why do I get 'Extensions exist, but extensibility is disabled'?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#mtxs-sidecar-approuter-401", + "title": "Why does `cds login` fail with a 401 error?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-does-my-subscription-fail-with-subaccount-verification-failed", + "title": "Why does my subscription fail with \"Subaccount verification failed\"", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#btp", + "title": "BTP", + "depth": 2 + }, + { + "source": "/docs/get-started/get-help#how-do-i-get-an-account-on-the-sap-business-technology-platform", + "title": "How do I get an account on the SAP Business Technology Platform?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#mta", + "title": "MTA", + "depth": 2 + }, + { + "source": "/docs/get-started/get-help#why-does-my-mta-build-fail-with-package-lockjson-issues", + "title": "Why does my MTA build fail with _package-lock.json_ issues?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-does-my-mta-build-fail-for-other-reasons", + "title": "Why does my MTA build fail for other reasons?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#how-can-i-define-the-build-order-between-mta-modules", + "title": "How can I define the build order between MTA modules?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#how-do-i-undeploy-an-mta", + "title": "How do I undeploy an MTA?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#reduce-mta-size", + "title": "How can I reduce MTA archive size during development?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#cloud-foundry", + "title": "Cloud Foundry", + "depth": 2 + }, + { + "source": "/docs/get-started/get-help#cflogs-recent", + "title": "How do I get logs from my application in Cloud Foundry?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#cf-cli", + "title": "How do I resolve errors with the `cf` CLI?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#installation-fails--mkdir--the-system-cannot-find-the-path-specified", + "title": "Installation fails — _mkdir ... The system cannot find the path specified_", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#cf-commands-fail--error-writing-config", + "title": "`cf` commands fail — _Error writing config_", + "depth": 4 + }, + { + "source": "/docs/get-started/get-help#why-does-my-app-deployment-fail-with-no-space-left-on-device", + "title": "Why does my app deployment fail with \"No space left on device\"?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-do-i-get-404-not-found-requested-route-does-not-exist", + "title": "Why do I get \"404 Not Found: Requested route does not exist\"?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#why-do-i-get-404-cannot-get-", + "title": "Why do I get \"_404 Cannot GET /_\"?", + "depth": 3 + }, + { + "source": "/docs/get-started/get-help#kyma--k8s", + "title": "Kyma / K8s", + "depth": 2 + }, + { + "source": "/docs/get-started/get-help#why-do-i-get-packagejson-and-package-lockjson-arent-in-sync", + "title": "Why do I get \"package.json and package-lock.json aren't in sync\"?", + "depth": 3 + }, + { + "source": "/docs/guides/", + "title": "The CAP Cookbook", + "depth": 1 + }, + { + "source": "/docs/guides/domain/", + "title": "Domain Modeling", + "depth": 1 + }, + { + "source": "/docs/guides/domain/#introduction", + "title": "Introduction", + "depth": 2 + }, + { + "source": "/docs/guides/domain/#capture-intent--what-not-how", + "title": "Capture Intent — *What, not How!*", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#entity-relationship-modeling", + "title": "Entity-Relationship Modeling", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#aspect-oriented-modeling", + "title": "Aspect-oriented Modeling", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#fuelling-generic-providers", + "title": "Fuelling Generic Providers", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#domain-driven-design", + "title": "Domain-Driven Design", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#best-practices", + "title": "Best Practices", + "depth": 2 + }, + { + "source": "/docs/guides/domain/#keep-it-simple-stupid", + "title": "Keep it Simple, Stupid", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#prefer-flat-models", + "title": "Prefer Flat Models", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#good", + "title": "**Good:**", + "depth": 5 + }, + { + "source": "/docs/guides/domain/#bad", + "title": "**Bad:**", + "depth": 5 + }, + { + "source": "/docs/guides/domain/#separation-of-concerns", + "title": "Separation of Concerns", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#naming-conventions", + "title": "Naming Conventions", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#core-concepts", + "title": "Core Concepts", + "depth": 2 + }, + { + "source": "/docs/guides/domain/#namespaces", + "title": "Namespaces", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#domain-entities", + "title": "Domain Entities", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#views--projections", + "title": "Views / Projections", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#primary-keys", + "title": "Primary Keys", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#do", + "title": "Do:", + "depth": 5 + }, + { + "source": "/docs/guides/domain/#dont", + "title": "Don't:", + "depth": 5 + }, + { + "source": "/docs/guides/domain/#prefer-simple-technical-keys", + "title": "Prefer Simple, Technical Keys", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#prefer-canonic-keys", + "title": "Prefer Canonic Keys", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#prefer-uuids-for-keys", + "title": "Prefer UUIDs for Keys", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#dont-interpret-uuids", + "title": "Don't Interpret UUIDs!", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#data-types", + "title": "Data Types", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#standard-built-in-types", + "title": "Standard Built-in Types", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#common-reuse-types", + "title": "Common Reuse Types", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#custom-defined-types", + "title": "Custom-defined Types", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#use-custom-types-reasonably", + "title": "Use Custom Types Reasonably", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#associations", + "title": "Associations", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#managed-1-associations", + "title": "Managed :1 Associations", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#to-many-associations", + "title": "To-Many Associations", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#many-to-many-associations", + "title": "Many-to-Many Associations", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#compositions", + "title": "Compositions", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#modeling-document-structures", + "title": "Modeling Document Structures", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#composition-of-aspects", + "title": "Composition of Aspects", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#aspects", + "title": "Aspects", + "depth": 2 + }, + { + "source": "/docs/guides/domain/#authorization", + "title": "Authorization", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#fiori-annotations", + "title": "Fiori Annotations", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#localized-data", + "title": "Localized Data", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#do-1", + "title": "**Do:**", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#dont-1", + "title": "**Don't:**", + "depth": 4 + }, + { + "source": "/docs/guides/domain/#managed-data", + "title": "Managed Data", + "depth": 2 + }, + { + "source": "/docs/guides/domain/#cdsoninsert", + "title": "`@cds.on.insert`", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#cdsonupdate", + "title": "`@cds.on.update`", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#aspect-managed", + "title": "Aspect _`managed`_ {}", + "depth": 3 + }, + { + "source": "/docs/guides/domain/#pseudo-variables", + "title": "Pseudo Variables", + "depth": 2 + }, + { + "source": "/docs/guides/domain/temporal-data", + "title": "Temporal Data", + "depth": 1 + }, + { + "source": "/docs/guides/domain/temporal-data#timeless-model", + "title": "Starting with 'Timeless' Models", + "depth": 2 + }, + { + "source": "/docs/guides/domain/temporal-data#timeless-model-1", + "title": "Timeless Model", + "depth": 3 + }, + { + "source": "/docs/guides/domain/temporal-data#timeless-data", + "title": "Timeless Data", + "depth": 3 + }, + { + "source": "/docs/guides/domain/temporal-data#declaring-temporal-entities", + "title": "Declaring Temporal Entities", + "depth": 2 + }, + { + "source": "/docs/guides/domain/temporal-data#using-annotations-cdsvalidfromto", + "title": "Using Annotations `@cds.valid.from/to`", + "depth": 3 + }, + { + "source": "/docs/guides/domain/temporal-data#using-common-aspect-temporal", + "title": "Using Common Aspect `temporal`", + "depth": 3 + }, + { + "source": "/docs/guides/domain/temporal-data#separate-temporal-details", + "title": "Separate Temporal Details", + "depth": 3 + }, + { + "source": "/docs/guides/domain/temporal-data#serving-temporal-data", + "title": "Serving Temporal Data", + "depth": 2 + }, + { + "source": "/docs/guides/domain/temporal-data#reading-temporal-data", + "title": "Reading Temporal Data", + "depth": 2 + }, + { + "source": "/docs/guides/domain/temporal-data#as-of-now-queries", + "title": "As-of-now Queries", + "depth": 3 + }, + { + "source": "/docs/guides/domain/temporal-data#time-travel-queries", + "title": "Time-Travel Queries", + "depth": 3 + }, + { + "source": "/docs/guides/domain/temporal-data#time-period-queries", + "title": "Time-Period Queries", + "depth": 3 + }, + { + "source": "/docs/guides/domain/temporal-data#transitive-temporal-data", + "title": "Transitive Temporal Data", + "depth": 3 + }, + { + "source": "/docs/guides/domain/temporal-data#primary-keys-of-time-slices", + "title": "Primary Keys of Time Slices", + "depth": 2 + }, + { + "source": "/docs/guides/services/", + "title": "Providing and Consuming Services", + "depth": 1 + }, + { + "source": "/docs/guides/services/providing-services", + "title": "Define Provided Services", + "depth": 1 + }, + { + "source": "/docs/guides/services/providing-services#services-as-apis", + "title": "Services as APIs", + "depth": 2 + }, + { + "source": "/docs/guides/services/providing-services#services-as-facades", + "title": "Services as Facades", + "depth": 2 + }, + { + "source": "/docs/guides/services/providing-services#denormalized-views", + "title": "Denormalized Views", + "depth": 2 + }, + { + "source": "/docs/guides/services/providing-services#auto-exposed-entities", + "title": "Auto-Exposed Entities", + "depth": 2 + }, + { + "source": "/docs/guides/services/providing-services#redirected-associations", + "title": "Redirected Associations", + "depth": 2 + }, + { + "source": "/docs/guides/services/providing-services#use-case-oriented-services", + "title": "Use Case-oriented Services", + "depth": 2 + }, + { + "source": "/docs/guides/services/providing-services#dont-single-services-exposing-all-entities-11", + "title": "**DON'T:** Single Services Exposing All Entities 1:1", + "depth": 4 + }, + { + "source": "/docs/guides/services/providing-services#do-one-service-per-use-case", + "title": "**DO:** One Service Per Use Case", + "depth": 4 + }, + { + "source": "/docs/guides/services/served-ootb", + "title": "Generic Service Providers", + "depth": 1 + }, + { + "source": "/docs/guides/services/served-ootb#introduction", + "title": "Introduction", + "depth": 2 + }, + { + "source": "/docs/guides/services/served-ootb#serving-crud", + "title": "Serving CRUD Requests", + "depth": 2 + }, + { + "source": "/docs/guides/services/served-ootb#deep-reads-and-writes", + "title": "Deep Reads and Writes", + "depth": 2 + }, + { + "source": "/docs/guides/services/served-ootb#deep-read", + "title": "Deep `READ`", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#deep-insert", + "title": "Deep `INSERT`", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#deep-update", + "title": "Deep `UPDATE`", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#deep-delete", + "title": "Deep `DELETE`", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#limitations", + "title": "Limitations", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#auto-generated-keys", + "title": "Auto-Generated Keys", + "depth": 2 + }, + { + "source": "/docs/guides/services/served-ootb#searching-data", + "title": "Searching Data", + "depth": 2 + }, + { + "source": "/docs/guides/services/served-ootb#cds-search", + "title": "The `@cds.search` Annotation", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#including-fields", + "title": "Including Fields", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#extend-search-to-associated-entities", + "title": "Extend Search to *Associated* Entities", + "depth": 4 + }, + { + "source": "/docs/guides/services/served-ootb#extend-to-individual-elements-in-associated-entities", + "title": "Extend to Individual Elements in Associated Entities", + "depth": 4 + }, + { + "source": "/docs/guides/services/served-ootb#excluding-fields", + "title": "Excluding Fields", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#the-commontext-annotation", + "title": "The `@Common.Text` Annotation", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#fuzzy-search", + "title": "Fuzzy Search on SAP HANA Cloud", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#pagination--sorting", + "title": "Pagination & Sorting", + "depth": 2 + }, + { + "source": "/docs/guides/services/served-ootb#implicit-pagination", + "title": "Implicit Pagination", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#reliable-pagination", + "title": "Reliable Pagination", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#paging-limits", + "title": "Paging Limits", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#annotation-cds-query-limit", + "title": "Annotation `@cds.query.limit`", + "depth": 4 + }, + { + "source": "/docs/guides/services/served-ootb#precedence", + "title": "Precedence", + "depth": 4 + }, + { + "source": "/docs/guides/services/served-ootb#implicit-sorting", + "title": "Implicit Sorting", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#concurrency-control", + "title": "Concurrency Control", + "depth": 2 + }, + { + "source": "/docs/guides/services/served-ootb#etag", + "title": "Conflict Detection Using ETags", + "depth": 3 + }, + { + "source": "/docs/guides/services/served-ootb#select-for-update", + "title": "Pessimistic Locking", + "depth": 3 + }, + { + "source": "/docs/guides/services/status-flows", + "title": "Status-Transition Flows", + "depth": 1 + }, + { + "source": "/docs/guides/services/status-flows#modeling-status-flows", + "title": "Modeling Status Flows", + "depth": 2 + }, + { + "source": "/docs/guides/services/status-flows#flowstatus-element", + "title": "@flow.status: element", + "depth": 3 + }, + { + "source": "/docs/guides/services/status-flows#from-entry-state", + "title": "@from: entry state", + "depth": 3 + }, + { + "source": "/docs/guides/services/status-flows#to-target-state", + "title": "@to: target state", + "depth": 3 + }, + { + "source": "/docs/guides/services/status-flows#to-flowprevious", + "title": "@to: $flow.previous", + "depth": 3 + }, + { + "source": "/docs/guides/services/status-flows#served-out-of-the-box", + "title": "Served Out-of-the-Box", + "depth": 2 + }, + { + "source": "/docs/guides/services/status-flows#by-generic-handlers", + "title": "By Generic Handlers", + "depth": 3 + }, + { + "source": "/docs/guides/services/status-flows#to-fiori-uis", + "title": "To Fiori UIs", + "depth": 3 + }, + { + "source": "/docs/guides/services/status-flows#adding-custom-handlers", + "title": "Adding Custom Handlers", + "depth": 2 + }, + { + "source": "/docs/guides/services/status-flows#current-limitations", + "title": "Current Limitations", + "depth": 2 + }, + { + "source": "/docs/guides/services/constraints", + "title": "Declarative Constraints", + "depth": 1 + }, + { + "source": "/docs/guides/services/constraints#introduction", + "title": "Introduction", + "depth": 2 + }, + { + "source": "/docs/guides/services/constraints#constraints-annotations", + "title": "Constraints Annotations", + "depth": 3 + }, + { + "source": "/docs/guides/services/constraints#served-out-of-the-box", + "title": "Served Out-of-the-Box", + "depth": 3 + }, + { + "source": "/docs/guides/services/constraints#served-to-fiori-uis", + "title": "Served to Fiori UIs", + "depth": 3 + }, + { + "source": "/docs/guides/services/constraints#input-validation", + "title": "Input Validation", + "depth": 2 + }, + { + "source": "/docs/guides/services/constraints#assert-constraint", + "title": "`@assert:` *(constraint)*", + "depth": 3 + }, + { + "source": "/docs/guides/services/constraints#assertformat", + "title": "`@assert.format`", + "depth": 3 + }, + { + "source": "/docs/guides/services/constraints#assertrange", + "title": "`@assert.range`", + "depth": 3 + }, + { + "source": "/docs/guides/services/constraints#asserttarget", + "title": "`@assert.target`", + "depth": 3 + }, + { + "source": "/docs/guides/services/constraints#mandatory", + "title": "`@mandatory`", + "depth": 3 + }, + { + "source": "/docs/guides/services/constraints#readonly", + "title": "`@readonly`", + "depth": 3 + }, + { + "source": "/docs/guides/services/constraints#error-messages", + "title": "Error Messages", + "depth": 2 + }, + { + "source": "/docs/guides/services/constraints#custom-messages", + "title": "Custom Messages", + "depth": 3 + }, + { + "source": "/docs/guides/services/constraints#localized-messages", + "title": "Localized Messages", + "depth": 3 + }, + { + "source": "/docs/guides/services/constraints#field-control", + "title": "Field Control", + "depth": 2 + }, + { + "source": "/docs/guides/services/constraints#mandatory-1", + "title": "`@mandatory`", + "depth": 3 + }, + { + "source": "/docs/guides/services/constraints#readonly-1", + "title": "`@readonly`", + "depth": 3 + }, + { + "source": "/docs/guides/services/constraints#uihidden", + "title": "`@UI.Hidden`", + "depth": 3 + }, + { + "source": "/docs/guides/services/constraints#invariant-constraints", + "title": "Invariant Constraints", + "depth": 2 + }, + { + "source": "/docs/guides/services/custom-code", + "title": "Custom Event Handlers", + "depth": 1 + }, + { + "source": "/docs/guides/services/custom-code#custom-service-providers", + "title": "Custom Service Providers", + "depth": 2 + }, + { + "source": "/docs/guides/services/custom-code#custom-event-handlers-1", + "title": "Custom Event Handlers", + "depth": 2 + }, + { + "source": "/docs/guides/services/custom-code#hooks-on-before-after", + "title": "Hooks: `on`, `before`, `after`", + "depth": 2 + }, + { + "source": "/docs/guides/services/custom-code#handler-impls", + "title": "Within Event Handlers", + "depth": 2 + }, + { + "source": "/docs/guides/services/custom-actions", + "title": "Custom Actions and Functions", + "depth": 1 + }, + { + "source": "/docs/guides/services/custom-actions#defining-custom-actions", + "title": "Defining Custom Actions", + "depth": 2 + }, + { + "source": "/docs/guides/services/custom-actions#kinds-of-actions", + "title": "Kinds of Actions", + "depth": 2 + }, + { + "source": "/docs/guides/services/custom-actions#implementing-actions", + "title": "Implementing Actions", + "depth": 2 + }, + { + "source": "/docs/guides/services/custom-actions#calling-actions--functions", + "title": "Calling Actions / Functions", + "depth": 2 + }, + { + "source": "/docs/guides/services/media-data", + "title": "Serving Media Data", + "depth": 1 + }, + { + "source": "/docs/guides/services/media-data#annotating-media-elements", + "title": "Annotating Media Elements", + "depth": 2 + }, + { + "source": "/docs/guides/services/media-data#reading-media-resources", + "title": "Reading Media Resources", + "depth": 2 + }, + { + "source": "/docs/guides/services/media-data#creating-a-media-resource", + "title": "Creating a Media Resource", + "depth": 2 + }, + { + "source": "/docs/guides/services/media-data#updating-media-resources", + "title": "Updating Media Resources", + "depth": 2 + }, + { + "source": "/docs/guides/services/media-data#deleting-media-resources", + "title": "Deleting Media Resources", + "depth": 2 + }, + { + "source": "/docs/guides/services/media-data#using-external-resources", + "title": "Using External Resources", + "depth": 2 + }, + { + "source": "/docs/guides/services/media-data#conventions--limitations", + "title": "Conventions & Limitations", + "depth": 2 + }, + { + "source": "/docs/guides/services/media-data#general-conventions", + "title": "General Conventions", + "depth": 4 + }, + { + "source": "/docs/guides/services/media-data#nodejs-runtime-conventions-and-limitations", + "title": "Node.js Runtime Conventions and Limitations", + "depth": 4 + }, + { + "source": "/docs/guides/uis/", + "title": "Serving UIs from CAP Applications", + "depth": 1 + }, + { + "source": "/docs/guides/uis/i18n", + "title": "Localization, i18n", + "depth": 1 + }, + { + "source": "/docs/guides/uis/i18n#externalizing-texts-bundles", + "title": "Externalizing Texts Bundles", + "depth": 2 + }, + { + "source": "/docs/guides/uis/i18n#where-to-place-text-bundles", + "title": "Where to Place Text Bundles?", + "depth": 2 + }, + { + "source": "/docs/guides/uis/i18n#csv-based-text-bundles", + "title": "CSV-Based Text Bundles", + "depth": 2 + }, + { + "source": "/docs/guides/uis/i18n#merging-algorithm", + "title": "Merging Algorithm", + "depth": 2 + }, + { + "source": "/docs/guides/uis/i18n#merging-reuse-bundles", + "title": "Merging Reuse Bundles", + "depth": 2 + }, + { + "source": "/docs/guides/uis/i18n#user-locale", + "title": "Determining User Locales", + "depth": 2 + }, + { + "source": "/docs/guides/uis/i18n#normalized-locales", + "title": "Normalized Locales", + "depth": 2 + }, + { + "source": "/docs/guides/uis/i18n#configuring-normalized-locales", + "title": "Configuring Normalized Locales", + "depth": 4 + }, + { + "source": "/docs/guides/uis/i18n#use-underscores-in-file-names", + "title": "Use Underscores in File Names", + "depth": 4 + }, + { + "source": "/docs/guides/uis/localized-data", + "title": "Localized Data", + "depth": 1 + }, + { + "source": "/docs/guides/uis/localized-data#declaring-localized-data", + "title": "Declaring Localized Data", + "depth": 2 + }, + { + "source": "/docs/guides/uis/localized-data#behind-the-scenes", + "title": "Behind the Scenes", + "depth": 2 + }, + { + "source": "/docs/guides/uis/localized-data#resolving-localized-texts-via-views", + "title": "Resolving localized texts via views", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#resolving-localized-texts-at-runtime", + "title": "Resolving search over localized texts at runtime", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#base-entities-stay-intact", + "title": "Base Entities Stay Intact", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#extending-texts-entities", + "title": "Extending *.texts* Entities", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#user-locale", + "title": "Pseudo var `$user.locale`", + "depth": 2 + }, + { + "source": "/docs/guides/uis/localized-data#determining-userlocale-from-inbound-requests", + "title": "Determining `$user.locale` from Inbound Requests", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#programmatic-access-to-userlocale", + "title": "Programmatic Access to `$user.locale`", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#propagating-of-user-locale", + "title": "Propagating `$user.locale` to Databases", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#reading-localized-data", + "title": "Reading Localized Data", + "depth": 2 + }, + { + "source": "/docs/guides/uis/localized-data#in-agnostic-code", + "title": "In Agnostic Code", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#for-end-users", + "title": "For End Users", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#for-translation-uis", + "title": "For Translation UIs", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#serving-localized-data", + "title": "Serving Localized Data", + "depth": 2 + }, + { + "source": "/docs/guides/uis/localized-data#localized-helper-views", + "title": "`localized.` Helper Views", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#read-operations", + "title": "Read Operations", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#write-operations", + "title": "Write Operations", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#update-operations", + "title": "Update Operations", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#delete-operations", + "title": "Delete Operations", + "depth": 3 + }, + { + "source": "/docs/guides/uis/localized-data#nested-localized-data", + "title": "Nested Localized Data", + "depth": 2 + }, + { + "source": "/docs/guides/uis/localized-data#adding-initial-data", + "title": "Adding Initial Data", + "depth": 2 + }, + { + "source": "/docs/guides/uis/localized-data#add-id_texts-for-sap-fiori-draft-on-sap-hana", + "title": "Add `ID_texts` for SAP Fiori Draft on SAP HANA", + "depth": 4 + }, + { + "source": "/docs/guides/uis/fiori", + "title": "Serving SAP Fiori UIs", + "depth": 1 + }, + { + "source": "/docs/guides/uis/fiori#getting-started", + "title": "Getting Started", + "depth": 2 + }, + { + "source": "/docs/guides/uis/fiori#using-fiori-previews", + "title": "Using Fiori Previews", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#fiori-preview", + "title": "Fiori Preview", + "depth": 6 + }, + { + "source": "/docs/guides/uis/fiori#adding-fiori-apps", + "title": "Adding Fiori Apps", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#sap-fiori-tools", + "title": "SAP Fiori Tools", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#odata-annotations-plugin", + "title": "OData Annotations Plugin", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#fiori-annotations", + "title": "Fiori Annotations", + "depth": 2 + }, + { + "source": "/docs/guides/uis/fiori#where-to-put-them", + "title": "Where to Put Them?", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#prefer-title-and-description", + "title": "Prefer `@title` and `@description`", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#prefer-readonly-mandatory-", + "title": "Prefer `@readonly`, `@mandatory`, ...", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#simple-value-helps", + "title": "Simple Value Helps", + "depth": 2 + }, + { + "source": "/docs/guides/uis/fiori#cdsodatavaluelist", + "title": "`@cds.odata.valuelist`", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#sapcdscommon", + "title": "`@sap/cds/common`", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#fiori-draft-support", + "title": "Fiori Draft Support", + "depth": 2 + }, + { + "source": "/docs/guides/uis/fiori#draft-enabled-entities", + "title": "Draft-Enabled Entities", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#draft-choreography", + "title": "Draft Choreography", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#draft-locks", + "title": "Draft Locks", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#requests-to-drafts", + "title": "Requests to Drafts", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#requests-to-active-data", + "title": "Requests to Active Data", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#draft-agnostic-requests", + "title": "Draft-agnostic Requests", + "depth": 4 + }, + { + "source": "/docs/guides/uis/fiori#programmatic-access", + "title": "Programmatic Access", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#draft-input-validation", + "title": "Draft Input Validation", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#validating-drafts", + "title": "Validating Drafts", + "depth": 6 + }, + { + "source": "/docs/guides/uis/fiori#custom-handlers-for-draft-events", + "title": "Custom Handlers for Draft Events", + "depth": 4 + }, + { + "source": "/docs/guides/uis/fiori#validation-on-active-entities", + "title": "Validation on Active Entities", + "depth": 4 + }, + { + "source": "/docs/guides/uis/fiori#persistent-messages", + "title": "Persistent Messages", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#draft-for-localized-data", + "title": "Draft for Localized Data", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#fiori-tree-views", + "title": "Fiori Tree Views", + "depth": 2 + }, + { + "source": "/docs/guides/uis/fiori#recursive-associations", + "title": "Recursive Associations", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#the-hierarchy-annotation", + "title": "The `@hierarchy` Annotation", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#ui5-manifest-configuration", + "title": "UI5 manifest Configuration", + "depth": 3 + }, + { + "source": "/docs/guides/uis/fiori#cache-control-in-java", + "title": "Cache Control in Java", + "depth": 2 + }, + { + "source": "/docs/guides/uis/fiori#role-based-visibility", + "title": "Role-based Visibility", + "depth": 2 + }, + { + "source": "/docs/guides/uis/vue-react", + "title": "Serving Vue.js or React", + "depth": 1 + }, + { + "source": "/docs/guides/uis/vue-react#example-project", + "title": "Example project", + "depth": 2 + }, + { + "source": "/docs/guides/uis/vue-react#next-up", + "title": "Next Up", + "depth": 2 + }, + { + "source": "/docs/guides/databases/", + "title": "CAP-Level Database Integration", + "depth": 1 + }, + { + "source": "/docs/guides/databases/#best-practices-served-out-of-the-box", + "title": "Best Practices, Served Out of the Box", + "depth": 3 + }, + { + "source": "/docs/guides/databases/#database-independent-guides", + "title": "Database-Independent Guides", + "depth": 3 + }, + { + "source": "/docs/guides/databases/#database-specific-guides", + "title": "Database-Specific Guides", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cap-level-dbs", + "title": "CAP-Level Database Support", + "depth": 1 + }, + { + "source": "/docs/guides/databases/cap-level-dbs#mocked-out-of-the-box", + "title": "Mocked Out of the Box", + "depth": 2 + }, + { + "source": "/docs/guides/databases/cap-level-dbs#todo", + "title": "TODO:", + "depth": 4 + }, + { + "source": "/docs/guides/databases/cap-level-dbs#standard-operators", + "title": "Standard Operators", + "depth": 2 + }, + { + "source": "/docs/guides/databases/cap-level-dbs#standard-sql-operators", + "title": "Standard SQL Operators", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cap-level-dbs#bivalent--and--operators", + "title": "Bivalent `==` and `!=` Operators", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cap-level-dbs#ternary--operator", + "title": "Ternary `?:` Operator", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cap-level-dbs#standard-functions", + "title": "Standard Functions", + "depth": 2 + }, + { + "source": "/docs/guides/databases/cap-level-dbs#portable-functions", + "title": "Portable Functions", + "depth": 6 + }, + { + "source": "/docs/guides/databases/cap-level-dbs#string-functions", + "title": "String Functions", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cap-level-dbs#numeric-functions", + "title": "Numeric Functions", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cap-level-dbs#date--time-functions", + "title": "Date / Time Functions", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cap-level-dbs#aggregate-functions", + "title": "Aggregate Functions", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cap-level-dbs#native-functions", + "title": "Native Functions", + "depth": 2 + }, + { + "source": "/docs/guides/databases/cap-level-dbs#window-functions", + "title": "Window Functions", + "depth": 2 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl", + "title": "CDL Compilation to Database-Specific DDLs", + "depth": 1 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#using-cds-compile-", + "title": "Using `cds compile`, ...", + "depth": 2 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#database-specific-dialects", + "title": "Database-Specific Dialects", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#dialects-by-cds-env-profiles", + "title": "Dialects by `cds env` Profiles", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#using-cds-deploy", + "title": "Using `cds deploy`", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#cdl--ddl-translation", + "title": "CDL ⇒ DDL Translation", + "depth": 2 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#entities--tables--views", + "title": "Entities ⇒ Tables / Views", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#qualified-names--slugified", + "title": "Qualified Names ⇒ Slugified", + "depth": 4 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#types--native-types", + "title": "Types ⇒ Native Types", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#structs--flattened", + "title": "Structs ⇒ Flattened", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#flattened-structs", + "title": "flattened-structs", + "depth": 6 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#associations--joins", + "title": "Associations ⇒ JOINs", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#associations-as-forward-declared-joins", + "title": "Associations as Forward-declared JOINs", + "depth": 6 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#calculated-elements", + "title": "Calculated Elements", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#virtual-elements", + "title": "Virtual Elements", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#default-values", + "title": "Default Values", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#invalid-names", + "title": "Invalid Names", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#reserved-words", + "title": "reserved-words", + "depth": 6 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#keys-constraints", + "title": "Keys, Constraints", + "depth": 2 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#database-constraints", + "title": "Database Constraints", + "depth": 6 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#primary-key-constraints", + "title": "Primary Key Constraints", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#not-null-constraints", + "title": "Not Null Constraints", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#unique-constraints", + "title": "Unique Constraints", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#foreign-key-constraints", + "title": "Foreign Key Constraints", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#on-delete-cascade", + "title": "`ON DELETE CASCADE`", + "depth": 4 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#skipping-with-assertintegrityfalse", + "title": "Skipping with `@assert.integrity:false`", + "depth": 4 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#deferred-enforcement", + "title": "Deferred Enforcement", + "depth": 4 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#customizing-options", + "title": "Customizing Options", + "depth": 2 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#cdspersistenceskip", + "title": "@cds.persistence.skip", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#cdspersistenceexists", + "title": "@cds.persistence.exists", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#cdspersistencetable", + "title": "@cds.persistence.table", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#sqlprepend--append", + "title": "`@sql.prepend / append`", + "depth": 3 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#column-vs-row-tables-on-sap-hana", + "title": "Column vs Row Tables on SAP HANA", + "depth": 4 + }, + { + "source": "/docs/guides/databases/cdl-to-ddl#database-specific-models", + "title": "Database-Specific Models", + "depth": 2 + }, + { + "source": "/docs/guides/databases/initial-data", + "title": "Adding Initial Data", + "depth": 1 + }, + { + "source": "/docs/guides/databases/initial-data#using-cds-add-data", + "title": "Using `cds add data`", + "depth": 2 + }, + { + "source": "/docs/guides/databases/initial-data#editing-csv-files", + "title": "Editing `.csv` Files", + "depth": 2 + }, + { + "source": "/docs/guides/databases/initial-data#initial-vs-test-data", + "title": "Initial vs Test Data", + "depth": 2 + }, + { + "source": "/docs/guides/databases/initial-data#custom-folders", + "title": "Custom Folders", + "depth": 3 + }, + { + "source": "/docs/guides/databases/initial-data#next-to-cds-files", + "title": "Next to `.cds` Files", + "depth": 2 + }, + { + "source": "/docs/guides/databases/initial-data#from-reuse-packages", + "title": "From Reuse Packages", + "depth": 2 + }, + { + "source": "/docs/guides/databases/initial-data#plug-and-play-reuse", + "title": "Plug-and-Play Reuse", + "depth": 2 + }, + { + "source": "/docs/guides/databases/hana", + "title": "Using SAP HANA Cloud for Production", + "depth": 1 + }, + { + "source": "/docs/guides/databases/hana#setup--configuration", + "title": "Setup & Configuration", + "depth": 2 + }, + { + "source": "/docs/guides/databases/hana#running-cds-build", + "title": "Running `cds build`", + "depth": 2 + }, + { + "source": "/docs/guides/databases/hana#generated-hdi-artifacts", + "title": "Generated HDI Artifacts", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#custom-hdi-artifacts", + "title": "Custom HDI Artifacts", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#deploying-to-sap-hana", + "title": "Deploying to SAP HANA", + "depth": 2 + }, + { + "source": "/docs/guides/databases/hana#configure-hana", + "title": "Prepare for Production", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#cds-deploy-hana", + "title": "Using `cds deploy` for Ad-Hoc Deployments", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#configuring-cds-deploy", + "title": "Configuring `cds deploy`", + "depth": 4 + }, + { + "source": "/docs/guides/databases/hana#--to-hanamyservice", + "title": "`--to hana:myservice`", + "depth": 5 + }, + { + "source": "/docs/guides/databases/hana#--vcap-file-someenvfilejson", + "title": "`--vcap-file someEnvFile.json`", + "depth": 5 + }, + { + "source": "/docs/guides/databases/hana#--to-hanamyservice---vcap-file-someenvfilejson", + "title": "`--to hana:myservice --vcap-file someEnvFile.json`", + "depth": 5 + }, + { + "source": "/docs/guides/databases/hana#using-cf-deploy-or-cf-push", + "title": "Using `cf deploy` or `cf push`", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#native-sap-hana-features", + "title": "Native SAP HANA Features", + "depth": 2 + }, + { + "source": "/docs/guides/databases/hana#geospatial-functions", + "title": "Geospatial Functions", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#spatial-grid-generators", + "title": "Spatial Grid Generators", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#functions-without-arguments", + "title": "Functions Without Arguments", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#regex-functions", + "title": "Regex Functions", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#hdi-schema-evolution", + "title": "HDI Schema Evolution", + "depth": 2 + }, + { + "source": "/docs/guides/databases/hana#schema-evolution-and-multitenancyextensibility", + "title": "Schema Evolution and Multitenancy/Extensibility", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#schema-updates-with-sap-hana", + "title": "Schema Updates with SAP HANA", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#deploy-artifact-transitions", + "title": "Deploy Artifact Transitions as Supported by HDI", + "depth": 4 + }, + { + "source": "/docs/guides/databases/hana#enabling-hdbmigrationtable-generation", + "title": "Enabling hdbmigrationtable Generation for Selected Entities During `cds build`", + "depth": 4 + }, + { + "source": "/docs/guides/databases/hana#schema-evolution-native-db-clauses", + "title": "Native Database Clauses", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#advanced-options", + "title": "Advanced Options", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#caveats", + "title": "Caveats", + "depth": 2 + }, + { + "source": "/docs/guides/databases/hana#csv-data-gets-overridden", + "title": "CSV Data Gets Overridden", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#undeploying-artifacts", + "title": "Undeploying Artifacts", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#system-limits", + "title": "System Limits", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana#native-associations", + "title": "Native Associations", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana-native", + "title": "Using Native SAP HANA Artifacts", + "depth": 1 + }, + { + "source": "/docs/guides/databases/hana-native#introduction", + "title": "Introduction", + "depth": 2 + }, + { + "source": "/docs/guides/databases/hana-native#add-native-objects", + "title": "Adding Native SAP HANA Objects", + "depth": 2 + }, + { + "source": "/docs/guides/databases/hana-native#add-existing-sap-hana-objects-from-other-hdi-containers", + "title": "Add Existing SAP HANA Objects from Other HDI Containers", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana-native#create-native-sap-hana-objects", + "title": "Create Native SAP HANA Objects", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana-native#make-the-object-known-to-cds", + "title": "Make the Object Known to CDS", + "depth": 2 + }, + { + "source": "/docs/guides/databases/hana-native#tables-and-views-without-parameters", + "title": "Tables and Views Without Parameters", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana-native#plain-names", + "title": "Plain Names", + "depth": 4 + }, + { + "source": "/docs/guides/databases/hana-native#quoted-table-name-plain-column-names", + "title": "Quoted Table Name, Plain Column Names", + "depth": 4 + }, + { + "source": "/docs/guides/databases/hana-native#quoted-table-name-quoted-column-names", + "title": "Quoted Table Name, Quoted Column Names", + "depth": 4 + }, + { + "source": "/docs/guides/databases/hana-native#views-with-parameters", + "title": "Views with Parameters", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana-native#plain-names-1", + "title": "Plain Names", + "depth": 4 + }, + { + "source": "/docs/guides/databases/hana-native#quoted-names", + "title": "Quoted Names", + "depth": 4 + }, + { + "source": "/docs/guides/databases/hana-native#default-values-of-view-parameters", + "title": "Default Values of View Parameters", + "depth": 4 + }, + { + "source": "/docs/guides/databases/hana-native#calculated-views-and-user-defined-functions", + "title": "Calculated Views and User-Defined Functions", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana-native#associations-and-compositions", + "title": "Associations and Compositions", + "depth": 2 + }, + { + "source": "/docs/guides/databases/hana-native#cdspersistenceskip", + "title": "`@cds.persistence.skip`", + "depth": 5 + }, + { + "source": "/docs/guides/databases/hana-native#cdspersistenceexists", + "title": "`@cds.persistence.exists`", + "depth": 5 + }, + { + "source": "/docs/guides/databases/hana-native#hana-types", + "title": "SAP HANA-Specific Data Types", + "depth": 2 + }, + { + "source": "/docs/guides/databases/hana-native#mapping-uuids-to-sql", + "title": "Mapping UUIDs to SQL", + "depth": 3 + }, + { + "source": "/docs/guides/databases/hana-native#example-index", + "title": "Example Index", + "depth": 3 + }, + { + "source": "/docs/guides/databases/sqlite", + "title": "Using SQLite for Development", + "depth": 1 + }, + { + "source": "/docs/guides/databases/sqlite#setup-for-sqlite", + "title": "Setup for SQLite", + "depth": 2 + }, + { + "source": "/docs/guides/databases/sqlite#using-cds-add-sqlite", + "title": "Using `cds add sqlite`", + "depth": 3 + }, + { + "source": "/docs/guides/databases/sqlite#manual-setup-for-nodejs", + "title": "Manual Setup for Node.js", + "depth": 3 + }, + { + "source": "/docs/guides/databases/sqlite#manual-setup-for-java", + "title": "Manual Setup for Java", + "depth": 3 + }, + { + "source": "/docs/guides/databases/sqlite#using-maven-archetype", + "title": "Using Maven Archetype", + "depth": 3 + }, + { + "source": "/docs/guides/databases/sqlite#using-in-memory-databases", + "title": "Using In-Memory Databases", + "depth": 2 + }, + { + "source": "/docs/guides/databases/sqlite#in-cap-nodejs-projects", + "title": "In CAP Node.js Projects", + "depth": 3 + }, + { + "source": "/docs/guides/databases/sqlite#in-cap-java-projects", + "title": "In CAP Java Projects", + "depth": 3 + }, + { + "source": "/docs/guides/databases/sqlite#using-persistent-databases", + "title": "Using Persistent Databases", + "depth": 2 + }, + { + "source": "/docs/guides/databases/sqlite#using-sqlite-in-production", + "title": "Using SQLite in Production?", + "depth": 2 + }, + { + "source": "/docs/guides/databases/h2", + "title": "Using H2 for Development in CAP Java", + "depth": 1 + }, + { + "source": "/docs/guides/databases/postgres", + "title": "Using PostgreSQL", + "depth": 1 + }, + { + "source": "/docs/guides/databases/postgres#setup--configuration", + "title": "Setup & Configuration", + "depth": 2 + }, + { + "source": "/docs/guides/databases/postgres#auto-wired-configuration-in-nodejs", + "title": "Auto-Wired Configuration in Node.js", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#provisioning-a-db-instance", + "title": "Provisioning a DB Instance", + "depth": 2 + }, + { + "source": "/docs/guides/databases/postgres#cap-java-on-sap-btp", + "title": "CAP Java on SAP BTP", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#using-docker", + "title": "Using Docker", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#service-bindings", + "title": "Service Bindings", + "depth": 2 + }, + { + "source": "/docs/guides/databases/postgres#configure-connection-data-in-cap-java", + "title": "Configure Connection Data in CAP Java", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#service-bindings-for-cds-tooling-in-cap-java", + "title": "Service Bindings for CDS Tooling in CAP Java", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#using-defaults-with-pg-profile", + "title": "Using Defaults with `[pg]` Profile", + "depth": 4 + }, + { + "source": "/docs/guides/databases/postgres#in-your-private-cdsrc-privatejson", + "title": "In Your Private `.cdsrc-private.json`", + "depth": 4 + }, + { + "source": "/docs/guides/databases/postgres#configure-service-bindings-in-nodejs", + "title": "Configure Service Bindings in Node.js", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#using-defaults-with-pg-profile-1", + "title": "Using Defaults with `[pg]` Profile", + "depth": 4 + }, + { + "source": "/docs/guides/databases/postgres#in-your-private-cdsrcjson", + "title": "In Your private `~/.cdsrc.json`", + "depth": 4 + }, + { + "source": "/docs/guides/databases/postgres#in-project-env-files", + "title": "In Project `.env` Files", + "depth": 4 + }, + { + "source": "/docs/guides/databases/postgres#deployment", + "title": "Deployment", + "depth": 2 + }, + { + "source": "/docs/guides/databases/postgres#using-cds-deploy", + "title": "Using `cds deploy`", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#with-a-deployer-app", + "title": "With a Deployer App", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#add-postgresql-deployment-configuration", + "title": "Add PostgreSQL Deployment Configuration", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#deploy", + "title": "Deploy", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#schema-evolution", + "title": "Automatic Schema Evolution", + "depth": 2 + }, + { + "source": "/docs/guides/databases/postgres#limitations", + "title": "Limitations", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#allowed", + "title": "Allowed", + "depth": 4 + }, + { + "source": "/docs/guides/databases/postgres#disallowed", + "title": "Disallowed", + "depth": 4 + }, + { + "source": "/docs/guides/databases/postgres#dry-run-offline", + "title": "Dry-Run Offline", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#generate-scripts", + "title": "Generate Scripts", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#using-liquibase-in-cap-java", + "title": "Using Liquibase in CAP Java", + "depth": 2 + }, + { + "source": "/docs/guides/databases/postgres#①-initial-schema-version", + "title": "① Initial Schema Version", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#schema-evolution-with-liquibase", + "title": "② Schema Evolution", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#migration-from-cds-pg-in-nodejs", + "title": "Migration from cds-pg in Node.js", + "depth": 2 + }, + { + "source": "/docs/guides/databases/postgres#cds-deploy---model-only", + "title": "`cds deploy --model-only`", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#with-deployer-app", + "title": "With Deployer App", + "depth": 3 + }, + { + "source": "/docs/guides/databases/postgres#mtx-support", + "title": "MTX Support", + "depth": 2 + }, + { + "source": "/docs/guides/databases/schema-evolution", + "title": "Schema Evolution", + "depth": 1 + }, + { + "source": "/docs/guides/databases/schema-evolution#drop-create-in-development", + "title": "Drop-Create in Development", + "depth": 2 + }, + { + "source": "/docs/guides/databases/schema-evolution#schema-evolution-by-cap", + "title": "Schema Evolution by CAP", + "depth": 2 + }, + { + "source": "/docs/guides/databases/schema-evolution#disallowed-changes", + "title": "Disallowed Changes", + "depth": 3 + }, + { + "source": "/docs/guides/databases/schema-evolution#automatic-migration", + "title": "Automatic Migration", + "depth": 3 + }, + { + "source": "/docs/guides/databases/schema-evolution#schema-evolution-by-hdi", + "title": "Schema Evolution by HDI", + "depth": 2 + }, + { + "source": "/docs/guides/databases/schema-evolution#liquibase-for-java-projects", + "title": "Liquibase for Java Projects", + "depth": 2 + }, + { + "source": "/docs/guides/databases/performance", + "title": "Performance Considerations for CDS Modeling", + "depth": 1 + }, + { + "source": "/docs/guides/databases/performance#avoid-union", + "title": "Avoid UNION", + "depth": 2 + }, + { + "source": "/docs/guides/databases/performance#polymorphism", + "title": "Polymorphism", + "depth": 3 + }, + { + "source": "/docs/guides/databases/performance#bad", + "title": "**Bad**", + "depth": 4 + }, + { + "source": "/docs/guides/databases/performance#good---normalized", + "title": "**Good - normalized**", + "depth": 4 + }, + { + "source": "/docs/guides/databases/performance#good---de-normalized", + "title": "**Good - de-normalized**", + "depth": 4 + }, + { + "source": "/docs/guides/databases/performance#view-building", + "title": "View Building", + "depth": 3 + }, + { + "source": "/docs/guides/databases/performance#good", + "title": "**Good**", + "depth": 4 + }, + { + "source": "/docs/guides/databases/performance#bad-1", + "title": "**Bad**", + "depth": 4 + }, + { + "source": "/docs/guides/databases/performance#avoid-join", + "title": "Avoid JOIN", + "depth": 2 + }, + { + "source": "/docs/guides/databases/performance#view-building-1", + "title": "View Building", + "depth": 3 + }, + { + "source": "/docs/guides/databases/performance#bad-2", + "title": "**Bad**", + "depth": 4 + }, + { + "source": "/docs/guides/databases/performance#good-1", + "title": "**Good**", + "depth": 4 + }, + { + "source": "/docs/guides/databases/performance#sorting", + "title": "Sorting", + "depth": 3 + }, + { + "source": "/docs/guides/databases/performance#good-2", + "title": "**Good**", + "depth": 4 + }, + { + "source": "/docs/guides/databases/performance#bad-3", + "title": "**Bad**", + "depth": 4 + }, + { + "source": "/docs/guides/databases/performance#filtering", + "title": "Filtering", + "depth": 3 + }, + { + "source": "/docs/guides/databases/performance#good-3", + "title": "**Good**", + "depth": 4 + }, + { + "source": "/docs/guides/databases/performance#bad-4", + "title": "**Bad**", + "depth": 4 + }, + { + "source": "/docs/guides/databases/performance#calculated-fields", + "title": "Calculated Fields", + "depth": 2 + }, + { + "source": "/docs/guides/databases/performance#case-statement", + "title": "Example: Case Statement → Calculation on Write", + "depth": 4 + }, + { + "source": "/docs/guides/databases/performance#compositions-vs-associations", + "title": "Compositions vs Associations", + "depth": 2 + }, + { + "source": "/docs/guides/databases/performance#legacy-systems", + "title": "Legacy Systems", + "depth": 2 + }, + { + "source": "/docs/guides/databases/performance#summary", + "title": "Summary", + "depth": 2 + }, + { + "source": "/docs/guides/databases/vector-embeddings", + "title": "Vector Embeddings", + "depth": 1 + }, + { + "source": "/docs/guides/databases/vector-embeddings#choose-an-embedding-model", + "title": "Choose an Embedding Model", + "depth": 2 + }, + { + "source": "/docs/guides/databases/vector-embeddings#add-embeddings-to-your-cds-model", + "title": "Add Embeddings to Your CDS Model", + "depth": 2 + }, + { + "source": "/docs/guides/databases/vector-embeddings#generate-embeddings", + "title": "Generate Embeddings", + "depth": 2 + }, + { + "source": "/docs/guides/databases/vector-embeddings#generate-embeddings-on-the-database", + "title": "Generate Embeddings on the Database", + "depth": 3 + }, + { + "source": "/docs/guides/databases/vector-embeddings#generate-embeddings-programmatically", + "title": "Generate Embeddings Programmatically", + "depth": 3 + }, + { + "source": "/docs/guides/databases/vector-embeddings#query-for-similarity", + "title": "Query for Similarity", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/", + "title": "Service Protocols in CAP", + "depth": 1 + }, + { + "source": "/docs/guides/protocols/#available-protocols", + "title": "Available Protocols", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/#using-protocols-in-cap", + "title": "Using Protocols in CAP", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/#further-reading", + "title": "Further Reading", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/#conclusion", + "title": "Conclusion", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/odata", + "title": "Serving OData APIs", + "depth": 1 + }, + { + "source": "/docs/guides/protocols/odata#overview", + "title": "Feature Overview", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/odata#odata-patch-collection", + "title": "PATCH Entity Collection with Mass Data (Java)", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/odata#type-mapping", + "title": "Mapping of CDS Types", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/odata#override-type-mapping", + "title": "Overriding Type Mapping", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#annotations", + "title": "OData Annotations", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/odata#terms-and-properties", + "title": "Terms and Properties", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#example", + "title": "Example", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/odata#qualified-annotations", + "title": "Qualified Annotations", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#primitives", + "title": "Primitives", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#null-value", + "title": "Null Value", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/odata#records", + "title": "Records", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#collections", + "title": "Collections", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#references", + "title": "References", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#enumeration-values", + "title": "Enumeration Values", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#expression-annotations", + "title": "Expressions", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#flattening", + "title": "Flattening", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/odata#managed-associations", + "title": "Managed Associations", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/odata#expression-translation", + "title": "Expression Translation", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/odata#annotating-annotations", + "title": "Annotating Annotations", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#dynamic-expressions", + "title": "EDM JSON Expression Syntax", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#sap-annotations", + "title": "`sap:` Annotations", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#differences-to-abap", + "title": "Differences to ABAP", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#vocabularies", + "title": "Annotation Vocabularies", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/odata#oasis-vocabularies", + "title": "OASIS Vocabularies", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#sap-vocabularies", + "title": "SAP Vocabularies", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#additional-vocabularies", + "title": "Additional Vocabularies", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#data-aggregation", + "title": "Data Aggregation", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/odata#example-1", + "title": "Example", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#transformations", + "title": "Transformations", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#concat", + "title": "`concat`", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/odata#skip-top-and-orderby", + "title": "`skip`, `top`, and `orderby`", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/odata#aggregation-methods", + "title": "Aggregation Methods", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#custom-aggregates", + "title": "Custom Aggregates", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#currencies-and-units-of-measure", + "title": "Currencies and Units of Measure", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/odata#other-features", + "title": "Other Features", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#open-types", + "title": "Open Types", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/odata#java-type-mapping", + "title": "Java Type Mapping", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#simple-types", + "title": "Simple Types", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/odata#structured-types", + "title": "Structured Types", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/odata#singletons", + "title": "Singletons", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/odata#requesting-singletons", + "title": "Requesting Singletons", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#updating-singletons", + "title": "Updating Singletons", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#deleting-singletons", + "title": "Deleting Singletons", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#creating-singletons", + "title": "Creating Singletons", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#v2-support", + "title": "V2 Support", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/odata#odata-v2-adapter-node", + "title": "Enabling OData V2 via CDS OData V2 Adapter in Node.js Apps", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#using-odata-v2-in-java-apps", + "title": "Using OData V2 in Java Apps", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#miscellaneous", + "title": "Miscellaneous", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/odata#omitting-elements-from-apis", + "title": "Omitting Elements From APIs", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#absolute-context-url", + "title": "Absolute Context URL", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#parallel-processing-of-atomicity-groups-in-nodejs-apps", + "title": "Parallel Processing of Atomicity Groups in Node.js Apps", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/odata#atomicity-groups", + "title": "Atomicity Groups", + "depth": 6 + }, + { + "source": "/docs/guides/protocols/openapi", + "title": "Publishing to OpenAPI", + "depth": 1 + }, + { + "source": "/docs/guides/protocols/openapi#cli", + "title": "Usage from CLI", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/openapi#swagger-ui", + "title": "Swagger UI", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/openapi#embedded-in-nodejs", + "title": "Embedded in Node.js", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/openapi#embedded-in-java", + "title": "Embedded in Java", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/openapi#online-swagger-editor", + "title": "Online Swagger Editor", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/openapi#annotations", + "title": "Annotations", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/openapi#core-annotations", + "title": "Core Annotations", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/openapi#capabilities", + "title": "Capabilities", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/openapi#validation", + "title": "Validation", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/openapi#authorization", + "title": "Authorization", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/openapi#common", + "title": "Common", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/openapi#openapi", + "title": "OpenAPI", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/openapi#faq", + "title": "Frequently Asked Questions", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/openapi#suppress-get-list-and-by-key-on-an-entity-set", + "title": "Suppress GET (list and by-key) on an entity set?", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/openapi#suppress-get-list-on-an-entity-set", + "title": "Suppress GET (list) on an entity set?", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/openapi#suppress-get-by-key-on-an-entity-set", + "title": "Suppress GET (by-key) on an entity set?", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/asyncapi", + "title": "Publishing to AsyncAPI", + "depth": 1 + }, + { + "source": "/docs/guides/protocols/asyncapi#cli", + "title": "Usage from CLI", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/asyncapi#presets", + "title": "Presets", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/asyncapi#annotations", + "title": "Annotations", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/asyncapi#extensions", + "title": "Extensions", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/asyncapi#behavior-with---merged-flag", + "title": "Behavior with `--merged` flag", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/asyncapi#mapping", + "title": "Type Mapping", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/mcp", + "title": "Model Context Protocol Adapter ", + "depth": 1 + }, + { + "source": "/docs/guides/protocols/mcp#preliminaries", + "title": "Preliminaries", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/mcp#get-sample", + "title": "Get Sample", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#adding-mcp-plugins", + "title": "Adding MCP Plugins", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/mcp#in-cap-nodejs-projects", + "title": "In CAP Node.js Projects", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#in-cap-java-projects", + "title": "In CAP Java Projects", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#serving-mcp", + "title": "Serving MCP", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/mcp#annotate-services-with-mcp", + "title": "Annotate services with `@mcp`", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#tailored-services-for-mcp-use", + "title": "Tailored services for MCP use", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#adding-context-information", + "title": "Adding Context Information", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#test-drive-locally", + "title": "Test-drive Locally", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/mcp#using-claude-code", + "title": "Using Claude Code", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#using-opencode", + "title": "Using OpenCode", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#run-your-cap-server", + "title": "Run your CAP server", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#running-queries", + "title": "Running Queries", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#with-claude-code-cli", + "title": "With Claude Code CLI:", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/mcp#with-opencode-cli", + "title": "With Opencode CLI:", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/mcp#inspect-log-output", + "title": "Inspect Log Output", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#under-the-hood", + "title": "Under the Hood", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/mcp#autowired-mcp-clients", + "title": "Autowired MCP Clients", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#mock-authentication", + "title": "Mock Authentication", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/mcp#opting-out-of-autowiring", + "title": "Opting out of Autowiring", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/mcp#mcp-served-out-of-the-box", + "title": "MCP served out of the box", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#tool-describe", + "title": "Tool: `describe`", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/mcp#tool-query", + "title": "Tool: `query`", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/mcp#tool-call_action", + "title": "Tool: `call_action`", + "depth": 4 + }, + { + "source": "/docs/guides/protocols/mcp#inspect-the-tools", + "title": "Inspect the Tools", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#current-limitations", + "title": "Current Limitations", + "depth": 2 + }, + { + "source": "/docs/guides/protocols/mcp#query-and-actions-only", + "title": "Query and Actions Only", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#prompt-injection-attacks", + "title": "Prompt Injection Attacks", + "depth": 3 + }, + { + "source": "/docs/guides/protocols/mcp#missing-governance-controls", + "title": "Missing Governance Controls", + "depth": 3 + }, + { + "source": "/docs/guides/integration/", + "title": "Services & Platform Integration", + "depth": 1 + }, + { + "source": "/docs/guides/integration/calesi", + "title": "CAP-Level Service Integration", + "depth": 1 + }, + { + "source": "/docs/guides/integration/calesi#preliminaries", + "title": "Preliminaries", + "depth": 2 + }, + { + "source": "/docs/guides/integration/calesi#teaser", + "title": "Teaser", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#overview", + "title": "Overview", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#the-xtravels-sample", + "title": "The XTravels Sample", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#getting-started", + "title": "Getting Started", + "depth": 4 + }, + { + "source": "/docs/guides/integration/calesi#providing-cap-level-apis", + "title": "Providing CAP-level APIs", + "depth": 2 + }, + { + "source": "/docs/guides/integration/calesi#defining-service-apis", + "title": "Defining Service APIs", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#using-denormalized-views", + "title": "Using Denormalized Views", + "depth": 4 + }, + { + "source": "/docs/guides/integration/calesi#exporting-apis", + "title": "Exporting APIs", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#exported-service-definitions", + "title": "Exported Service Definitions", + "depth": 4 + }, + { + "source": "/docs/guides/integration/calesi#packaged-apis", + "title": "Packaged APIs", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#adding-initial-data-and-i18n-bundles", + "title": "Adding Initial Data and I18n Bundles", + "depth": 4 + }, + { + "source": "/docs/guides/integration/calesi#plug--play-config", + "title": "Plug & Play Config", + "depth": 4 + }, + { + "source": "/docs/guides/integration/calesi#publishing-apis", + "title": "Publishing APIs", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#importing-apis", + "title": "Importing APIs", + "depth": 2 + }, + { + "source": "/docs/guides/integration/calesi#packaged-apis-1", + "title": "Packaged APIs", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#odata-apis", + "title": "OData APIs", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#reuse-packages", + "title": "Reuse Packages", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#integrating-models", + "title": "Integrating Models", + "depth": 2 + }, + { + "source": "/docs/guides/integration/calesi#consumption-views", + "title": "Consumption Views", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#associations", + "title": "Associations", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#associations-from-remote", + "title": "Associations from Remote", + "depth": 4 + }, + { + "source": "/docs/guides/integration/calesi#constraints", + "title": "Constraints", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#serving-uis", + "title": "Serving UIs", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#fiori-annotations", + "title": "Fiori Annotations", + "depth": 4 + }, + { + "source": "/docs/guides/integration/calesi#mocked-out-of-the-box", + "title": "Mocked Out of the Box", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#integration-logic-required", + "title": "Integration Logic Required", + "depth": 4 + }, + { + "source": "/docs/guides/integration/calesi#integration-logic", + "title": "Integration Logic", + "depth": 2 + }, + { + "source": "/docs/guides/integration/calesi#connecting-to-remote-services", + "title": "Connecting to Remote Services", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#uniform-agnostic-apis", + "title": "Uniform, Agnostic APIs", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#testing-with-cds-repl", + "title": "Testing with `cds repl`", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#modifying-cqns", + "title": "Modifying CQNs", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#data-federation", + "title": "Data Federation", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#basic-implementation", + "title": "Basic Implementation", + "depth": 4 + }, + { + "source": "/docs/guides/integration/calesi#generic-implementation", + "title": "Generic Implementation", + "depth": 4 + }, + { + "source": "/docs/guides/integration/calesi#delegation", + "title": "Delegation", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#automatic-query-translation", + "title": "Automatic Query Translation", + "depth": 4 + }, + { + "source": "/docs/guides/integration/calesi#navigation", + "title": "Navigation", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#expands", + "title": "Expands", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#outboxed-emits", + "title": "Outboxed Emits", + "depth": 3 + }, + { + "source": "/docs/guides/integration/calesi#learn-more", + "title": "Learn More", + "depth": 2 + }, + { + "source": "/docs/guides/integration/data-federation", + "title": "CAP-level Data Federation", + "depth": 1 + }, + { + "source": "/docs/guides/integration/data-federation#preliminaries", + "title": "Preliminaries", + "depth": 2 + }, + { + "source": "/docs/guides/integration/data-federation#prerequisites", + "title": "Prerequisites", + "depth": 3 + }, + { + "source": "/docs/guides/integration/data-federation#motivation", + "title": "Motivation", + "depth": 3 + }, + { + "source": "/docs/guides/integration/data-federation#the-xtravels-sample", + "title": "The XTravels Sample", + "depth": 3 + }, + { + "source": "/docs/guides/integration/data-federation#federated-consumption-views", + "title": "Federated Consumption Views", + "depth": 2 + }, + { + "source": "/docs/guides/integration/data-federation#service-level-replication", + "title": "Service-level Replication", + "depth": 2 + }, + { + "source": "/docs/guides/integration/data-federation#test-drive-locally", + "title": "Test Drive Locally", + "depth": 2 + }, + { + "source": "/docs/guides/integration/inner-loops", + "title": "Inner-Loop Development", + "depth": 1 + }, + { + "source": "/docs/guides/integration/inner-loops#preliminaries", + "title": "Preliminaries", + "depth": 2 + }, + { + "source": "/docs/guides/integration/inner-loops#what-is-inner-loop", + "title": "What is Inner Loop?", + "depth": 3 + }, + { + "source": "/docs/guides/integration/inner-loops#the-xtravels-sample", + "title": "The XTravels Sample", + "depth": 3 + }, + { + "source": "/docs/guides/integration/inner-loops#activate-generic-data-federation", + "title": "Activate Generic Data Federation", + "depth": 4 + }, + { + "source": "/docs/guides/integration/inner-loops#mocked-out-of-the-box", + "title": "Mocked Out of the Box", + "depth": 2 + }, + { + "source": "/docs/guides/integration/inner-loops#in-process-shared-db--cds-watch", + "title": "In-Process, Shared DB – `cds watch`", + "depth": 3 + }, + { + "source": "/docs/guides/integration/inner-loops#separate-processes--cds-mock", + "title": "Separate Processes – `cds mock`", + "depth": 3 + }, + { + "source": "/docs/guides/integration/inner-loops#cds-mock", + "title": "cds-mock", + "depth": 6 + }, + { + "source": "/docs/guides/integration/inner-loops#providing-mock-data", + "title": "Providing Mock Data", + "depth": 3 + }, + { + "source": "/docs/guides/integration/inner-loops#run-with-real-services", + "title": "Run with Real Services", + "depth": 2 + }, + { + "source": "/docs/guides/integration/inner-loops#test-drive-w-cds-repl", + "title": "Test-drive w/ `cds repl`", + "depth": 2 + }, + { + "source": "/docs/guides/integration/inner-loops#using-npm-workspaces", + "title": "Using `npm` Workspaces", + "depth": 2 + }, + { + "source": "/docs/guides/integration/inner-loops#using-proxy-packages", + "title": "Using Proxy Packages", + "depth": 2 + }, + { + "source": "/docs/guides/integration/reuse-and-compose", + "title": "CAP Service Composition", + "depth": 1 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#introduction-and-overview", + "title": "Introduction and Overview", + "depth": 2 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#usage-scenarios", + "title": "Usage Scenarios", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#examples-from-sample-repositories", + "title": "Examples from [sample repositories](https://github.com/capire)", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#preparation-for-exercises", + "title": "Preparation for Exercises", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#import", + "title": "Importing Reuse Packages", + "depth": 2 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#using-npm-addinstall-from-npm-registries", + "title": "Using `npm add/install` from _npm_ Registries", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#importing-from-other-sources", + "title": "Importing from Other Sources", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#importing-from-maven-dependencies", + "title": "Importing from Maven Dependencies", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#embedding-vs-integration", + "title": "Embedding vs. Integrating Reuse Services", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#reuse-models", + "title": "Reuse & Extend Models", + "depth": 2 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#via-using-from-directives", + "title": "Via `using from` Directives", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#index-cds", + "title": "Using _index.cds_ Entry Points", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#using-different-entry-points", + "title": "Using Different Entry Points", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#extending-imported-definitions", + "title": "Extending Imported Definitions", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#reuse-code", + "title": "Reuse & Extend Code", + "depth": 2 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#in-nodejs", + "title": "In Node.js", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#in-java", + "title": "In Java", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#reuse-uis", + "title": "Reuse & Extend UIs", + "depth": 2 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#service-integration", + "title": "Service Integration", + "depth": 2 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#import-the-remote-services-apis", + "title": "Import the Remote Service's APIs", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#configuring-required-services", + "title": "Configuring Required Services", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#restricted-reuse-options", + "title": "Restricted Reuse Options", + "depth": 4 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#delegating-calls", + "title": "Delegating Calls to Remote Services", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#mocking-required-services", + "title": "Running with Mocked Remote Services", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#testing-locally", + "title": "Testing Remote Integration Locally", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#binding-required-services", + "title": "Binding Required Services", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#bindings-via-cds-env", + "title": "Basic Mechanism Using `cds.env` and Process env Variables", + "depth": 4 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#bindings-via-cds-watch", + "title": "Automatic Bindings by `cds watch`", + "depth": 4 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#bindings-via-process-env", + "title": "Through Process Environment Variables", + "depth": 4 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#bindings-via-vcap_services", + "title": "Through `VCAP_SERVICES`", + "depth": 4 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#bindings-in-cloud-environments", + "title": "In Target Cloud Environments", + "depth": 4 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#providing-reuse-packages", + "title": "Providing Reuse Packages", + "depth": 2 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#considerations-for-maven-based-reuse-packages", + "title": "Considerations for Maven-based reuse packages", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#entry-points", + "title": "Provide Public Entry Points", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#provide-custom-handlers", + "title": "Provide Custom Handlers", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#in-nodejs-1", + "title": "In Node.js", + "depth": 4 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#in-java-1", + "title": "In Java", + "depth": 4 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#add-a-readme", + "title": "Add a Readme", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#publishshare-with-consumers", + "title": "Publish/Share with Consumers", + "depth": 3 + }, + { + "source": "/docs/guides/integration/reuse-and-compose#customizing-saas-usage", + "title": "Customizing SaaS Usage", + "depth": 2 + }, + { + "source": "/docs/guides/events/", + "title": "Events and Messaging", + "depth": 1 + }, + { + "source": "/docs/guides/events/core-concepts", + "title": "Core Eventing in CAP", + "depth": 1 + }, + { + "source": "/docs/guides/events/core-concepts#intrinsic-eventing-in-cap", + "title": "Intrinsic Eventing in CAP", + "depth": 2 + }, + { + "source": "/docs/guides/events/core-concepts#emitters-and-receivers", + "title": "Emitters and Receivers", + "depth": 3 + }, + { + "source": "/docs/guides/events/core-concepts#ubiquitous-events", + "title": "Ubiquitous Events", + "depth": 3 + }, + { + "source": "/docs/guides/events/core-concepts#asynchronous-apis", + "title": "Asynchronous APIs", + "depth": 3 + }, + { + "source": "/docs/guides/events/core-concepts#books-reviews-sample", + "title": "Books Reviews Sample", + "depth": 2 + }, + { + "source": "/docs/guides/events/core-concepts#declaring-events-in-cds", + "title": "Declaring Events in CDS", + "depth": 3 + }, + { + "source": "/docs/guides/events/core-concepts#emitting-events", + "title": "Emitting Events", + "depth": 3 + }, + { + "source": "/docs/guides/events/core-concepts#receiving-events", + "title": "Receiving Events", + "depth": 3 + }, + { + "source": "/docs/guides/events/core-concepts#in-process-eventing", + "title": "In-Process Eventing", + "depth": 2 + }, + { + "source": "/docs/guides/events/core-concepts#start-server", + "title": "1. Run CAP Server", + "depth": 3 + }, + { + "source": "/docs/guides/events/core-concepts#2-add-reviews", + "title": "2. Add Reviews", + "depth": 3 + }, + { + "source": "/docs/guides/events/core-concepts#3-check-ratings", + "title": "3. Check Ratings", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-queues", + "title": "Transactional Event Queues", + "depth": 1 + }, + { + "source": "/docs/guides/events/event-queues#motivation", + "title": "Motivation", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-queues#pubsub-vs-event-queues", + "title": "Pub/Sub vs. Event Queues", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-queues#outbox", + "title": "Outbox", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-queues#programmatic-use", + "title": "Programmatic Use", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-queues#by-configuration", + "title": "By Configuration", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-queues#auto-outboxed-services", + "title": "Auto-Outboxed Services", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-queues#callbacks", + "title": "Callbacks ", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-queues#inbox", + "title": "Inbox", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-queues#scheduled-tasks", + "title": "Scheduled Tasks", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-queues#end-to-end-example", + "title": "End-to-End Example", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-queues#configuration", + "title": "Configuration", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-queues#operations", + "title": "Operations", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-queues#locking", + "title": "Locking", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-queues#authorization", + "title": "Authorization", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-queues#error-handling", + "title": "Error Handling", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-queues#dead-letter-queue", + "title": "Dead Letter Queue", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-queues#observability", + "title": "Observability", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-queues#next-steps", + "title": "Next Steps", + "depth": 2 + }, + { + "source": "/docs/guides/events/messaging", + "title": "CAP-level Messaging", + "depth": 1 + }, + { + "source": "/docs/guides/events/messaging#why-using-messaging", + "title": "Why Using Messaging?", + "depth": 2 + }, + { + "source": "/docs/guides/events/messaging#using-message-channels", + "title": "Using Message Channels", + "depth": 2 + }, + { + "source": "/docs/guides/events/messaging#1-use-file-based-messaging-in-development", + "title": "1. Use `file-based-messaging` in Development", + "depth": 3 + }, + { + "source": "/docs/guides/events/messaging#2-start-the-reviews-service-and-bookstore-separately", + "title": "2. Start the `reviews` Service and `bookstore` Separately", + "depth": 3 + }, + { + "source": "/docs/guides/events/messaging#add-or-update-reviews", + "title": "3. Add or Update Reviews", + "depth": 3 + }, + { + "source": "/docs/guides/events/messaging#4-shut-down-and-restart-receiver--resilience-by-design", + "title": "4. Shut Down and Restart Receiver → Resilience by Design", + "depth": 3 + }, + { + "source": "/docs/guides/events/messaging#have-a-look-into-cds-msg-box", + "title": "Have a Look Into _~/.cds-msg-box_", + "depth": 3 + }, + { + "source": "/docs/guides/events/messaging#using-multiple-channels", + "title": "Using Multiple Channels", + "depth": 2 + }, + { + "source": "/docs/guides/events/messaging#using-separate-channels", + "title": "Using Separate Channels", + "depth": 3 + }, + { + "source": "/docs/guides/events/messaging#using-composite-messaging-implementation", + "title": "Using `composite-messaging` Implementation", + "depth": 3 + }, + { + "source": "/docs/guides/events/messaging#configuring-individual-channels-and-routes", + "title": "Configuring Individual Channels and Routes", + "depth": 3 + }, + { + "source": "/docs/guides/events/messaging#low-level-messaging", + "title": "Low-Level Messaging", + "depth": 2 + }, + { + "source": "/docs/guides/events/messaging#configure-messaging-services", + "title": "Configure Messaging Services", + "depth": 4 + }, + { + "source": "/docs/guides/events/messaging#connect-to-the-messaging-service", + "title": "Connect to the Messaging Service", + "depth": 4 + }, + { + "source": "/docs/guides/events/messaging#emit-events-to-messaging-service", + "title": "Emit Events to Messaging Service", + "depth": 4 + }, + { + "source": "/docs/guides/events/messaging#receive-events-from-messaging-service", + "title": "Receive Events from Messaging Service", + "depth": 4 + }, + { + "source": "/docs/guides/events/messaging#declared-events-and-topic-names", + "title": "Declared Events and `@topic` Names", + "depth": 4 + }, + { + "source": "/docs/guides/events/messaging#conceptual-vs-low-level-messaging", + "title": "Conceptual vs. Low-Level Messaging", + "depth": 4 + }, + { + "source": "/docs/guides/events/messaging#cloudevents", + "title": "CloudEvents Standard", + "depth": 2 + }, + { + "source": "/docs/guides/events/is-aem", + "title": "Messaging via SAP Integration Suite, Advanced Event Mesh", + "depth": 1 + }, + { + "source": "/docs/guides/events/event-mesh", + "title": "Messaging via SAP Event Mesh", + "depth": 1 + }, + { + "source": "/docs/guides/events/event-mesh#prerequisite-create-an-instance-of-sap-event-mesh", + "title": "Prerequisite: Create an Instance of SAP Event Mesh", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-mesh#use-enterprise-messaging", + "title": "Use `enterprise-messaging`", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-mesh#optional-add-namespace-prefixing-rules", + "title": "Optional: Add `namespace` Prefixing Rules", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-mesh#run-tests-in-hybrid-setup", + "title": "Run Tests in `hybrid` Setup", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-mesh#cap-automatically-creates-queues-and-subscriptions", + "title": "CAP Automatically Creates Queues and Subscriptions", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-mesh#optional-configure-queue-names", + "title": "Optional: Configure Queue Names", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-mesh#deploy-to-the-cloud-with-mta", + "title": "Deploy to the Cloud (with MTA)", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-mesh#1-specify-binding-to-sap-event-mesh-instance", + "title": "1. Specify Binding to SAP Event Mesh Instance", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-mesh#2-optional-auto-create-sap-event-mesh-instances", + "title": "2. Optional: Auto-Create SAP Event Mesh Instances", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-hub", + "title": "Messaging via SAP Cloud Application Event Hub", + "depth": 1 + }, + { + "source": "/docs/guides/events/event-hub#prerequisite-set-up-sap-cloud-application-event-hub", + "title": "Prerequisite: Set up SAP Cloud Application Event Hub", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-hub#configuration", + "title": "Configuration", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-hub#use-event-broker-in-nodejs", + "title": "Use `event-broker` in Node.js", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-hub#use-event-hub-in-java", + "title": "Use `event-hub` in Java", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-hub#hybrid-testing", + "title": "Hybrid Testing", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-hub#deploy", + "title": "Prepare for MTA Deployment", + "depth": 2 + }, + { + "source": "/docs/guides/events/event-hub#add-sap-cloud-application-event-hub-instance", + "title": "Add SAP Cloud Application Event Hub Instance", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-hub#add-identity-authentication-service-instance", + "title": "Add Identity Authentication Service Instance", + "depth": 3 + }, + { + "source": "/docs/guides/events/event-hub#bind-the-service-instances", + "title": "Bind the Service Instances", + "depth": 3 + }, + { + "source": "/docs/guides/events/s4", + "title": "Receiving Events from SAP S/4HANA Cloud Systems", + "depth": 1 + }, + { + "source": "/docs/guides/events/s4#find--import-apis", + "title": "Find & Import APIs", + "depth": 2 + }, + { + "source": "/docs/guides/events/s4#find-information-about-events", + "title": "Find Information About Events", + "depth": 2 + }, + { + "source": "/docs/guides/events/s4#add-missing-event-declarations", + "title": "Add Missing Event Declarations", + "depth": 2 + }, + { + "source": "/docs/guides/events/s4#consume-events-agnostically", + "title": "Consume Events Agnostically", + "depth": 2 + }, + { + "source": "/docs/guides/events/s4#configure-cap", + "title": "Configure CAP", + "depth": 2 + }, + { + "source": "/docs/guides/events/s4#configure-sap-s4hana", + "title": "Configure SAP S/4HANA", + "depth": 2 + }, + { + "source": "/docs/guides/events/s4#using-low-level-messaging", + "title": "Using Low-Level Messaging", + "depth": 2 + }, + { + "source": "/docs/guides/security/", + "title": "CAP Security and Data Privacy", + "depth": 1 + }, + { + "source": "/docs/guides/security/#data-protection-vs-data-privacy", + "title": "Data Protection vs. Data Privacy:", + "depth": 4 + }, + { + "source": "/docs/guides/security/overview", + "title": "Overview of Security Concepts and Architecture", + "depth": 1 + }, + { + "source": "/docs/guides/security/overview#key-concepts", + "title": "Key Concepts", + "depth": 2 + }, + { + "source": "/docs/guides/security/overview#key-concept-pluggable", + "title": "Pluggable Building Blocks", + "depth": 3 + }, + { + "source": "/docs/guides/security/overview#key-concept-customizable", + "title": "Customizable", + "depth": 3 + }, + { + "source": "/docs/guides/security/overview#key-concept-platform-services", + "title": "Built on Best of Breed", + "depth": 3 + }, + { + "source": "/docs/guides/security/overview#key-concept-decoupled-coding", + "title": "Decoupled from Business Logic", + "depth": 3 + }, + { + "source": "/docs/guides/security/overview#key-concept-secure-by-default", + "title": "Secure by Default", + "depth": 3 + }, + { + "source": "/docs/guides/security/overview#security-architecture", + "title": "Security Architecture", + "depth": 2 + }, + { + "source": "/docs/guides/security/overview#architecture-overview", + "title": "Architecture Overview", + "depth": 3 + }, + { + "source": "/docs/guides/security/overview#public-zone", + "title": "Public Zone", + "depth": 4 + }, + { + "source": "/docs/guides/security/overview#platform-zone", + "title": "Platform Zone", + "depth": 4 + }, + { + "source": "/docs/guides/security/overview#application-zone", + "title": "Application Zone", + "depth": 4 + }, + { + "source": "/docs/guides/security/overview#platform-environment", + "title": "Platform Requirements", + "depth": 3 + }, + { + "source": "/docs/guides/security/overview#platform-compliance", + "title": "Platform Compliance", + "depth": 2 + }, + { + "source": "/docs/guides/security/overview#local", + "title": "CAP in Local Environment", + "depth": 3 + }, + { + "source": "/docs/guides/security/overview#do", + "title": "DO:", + "depth": 4 + }, + { + "source": "/docs/guides/security/overview#dont", + "title": "DON'T:", + "depth": 4 + }, + { + "source": "/docs/guides/security/overview#cloud", + "title": "CAP in Cloud Environment", + "depth": 3 + }, + { + "source": "/docs/guides/security/overview#btp-services", + "title": "Security Platform Services", + "depth": 3 + }, + { + "source": "/docs/guides/security/overview#identity-service", + "title": "SAP Cloud Identity Services - Identity Authentication", + "depth": 4 + }, + { + "source": "/docs/guides/security/overview#sap-authorization-and-trust-management-service", + "title": "SAP Authorization and Trust Management Service", + "depth": 4 + }, + { + "source": "/docs/guides/security/overview#sap-btp-connectivity", + "title": "SAP BTP Connectivity", + "depth": 4 + }, + { + "source": "/docs/guides/security/overview#sap-malware-scanning-service", + "title": "SAP Malware Scanning Service", + "depth": 4 + }, + { + "source": "/docs/guides/security/overview#sap-credential-store", + "title": "SAP Credential Store", + "depth": 4 + }, + { + "source": "/docs/guides/security/authentication", + "title": "Authentication", + "depth": 1 + }, + { + "source": "/docs/guides/security/authentication#pluggable-authentication", + "title": "Pluggable Authentication", + "depth": 2 + }, + { + "source": "/docs/guides/security/authentication#mock-user-authentication", + "title": "Mock User Authentication", + "depth": 2 + }, + { + "source": "/docs/guides/security/authentication#preconfigured-mock-users", + "title": "Preconfigured Mock Users", + "depth": 3 + }, + { + "source": "/docs/guides/security/authentication#custom-mock-users", + "title": "Customization", + "depth": 3 + }, + { + "source": "/docs/guides/security/authentication#mock-user-testing", + "title": "Automated Testing", + "depth": 3 + }, + { + "source": "/docs/guides/security/authentication#ias-auth", + "title": "IAS Authentication", + "depth": 2 + }, + { + "source": "/docs/guides/security/authentication#ias-ready", + "title": "Get Ready with IAS", + "depth": 3 + }, + { + "source": "/docs/guides/security/authentication#adding-ias", + "title": "Adding IAS", + "depth": 3 + }, + { + "source": "/docs/guides/security/authentication#ias-admin", + "title": "Administrative Console for IAS", + "depth": 4 + }, + { + "source": "/docs/guides/security/authentication#cli-level-testing", + "title": "CLI Level Testing", + "depth": 3 + }, + { + "source": "/docs/guides/security/authentication#ui-level-testing", + "title": "UI Level Testing", + "depth": 3 + }, + { + "source": "/docs/guides/security/authentication#xsuaa-auth", + "title": "XSUAA Authentication", + "depth": 2 + }, + { + "source": "/docs/guides/security/authentication#xsuaa-ready", + "title": "Get Ready with XSUAA", + "depth": 3 + }, + { + "source": "/docs/guides/security/authentication#adding-xsuaa", + "title": "Adding XSUAA", + "depth": 3 + }, + { + "source": "/docs/guides/security/authentication#xsuaa-security-descriptor", + "title": "Security Descriptor", + "depth": 4 + }, + { + "source": "/docs/guides/security/authentication#start-and-check-the-deployment", + "title": "Start and Check the Deployment", + "depth": 4 + }, + { + "source": "/docs/guides/security/authentication#cli-level-testing-1", + "title": "CLI Level Testing", + "depth": 3 + }, + { + "source": "/docs/guides/security/authentication#ui-level-testing-1", + "title": "UI Level Testing", + "depth": 3 + }, + { + "source": "/docs/guides/security/authentication#hybrid-auth", + "title": "Hybrid Authentication", + "depth": 2 + }, + { + "source": "/docs/guides/security/authentication#custom-auth", + "title": "Custom Authentication", + "depth": 2 + }, + { + "source": "/docs/guides/security/authentication#model-auth", + "title": "Automatic Authentication", + "depth": 3 + }, + { + "source": "/docs/guides/security/authentication#partially-auth", + "title": "Overrule Partially", + "depth": 3 + }, + { + "source": "/docs/guides/security/authentication#fully-auth", + "title": "Overrule Fully", + "depth": 3 + }, + { + "source": "/docs/guides/security/authentication#pitfalls", + "title": "Pitfalls", + "depth": 2 + }, + { + "source": "/docs/guides/security/cap-users", + "title": "CAP-level Users & Roles", + "depth": 1 + }, + { + "source": "/docs/guides/security/cap-users#claims", + "title": "CAP User Abstraction", + "depth": 2 + }, + { + "source": "/docs/guides/security/cap-users#user-types", + "title": "User Types", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#roles", + "title": "Roles", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#pseudo-roles", + "title": "Pseudo Roles", + "depth": 4 + }, + { + "source": "/docs/guides/security/cap-users#model-references", + "title": "Model References", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#user-tracing", + "title": "Tracing", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#roles-assignment-ams", + "title": "Role Assignment with AMS", + "depth": 2 + }, + { + "source": "/docs/guides/security/cap-users#adding-ams-support", + "title": "Adding AMS Support", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#adding-ams-support-1", + "title": "Adding AMS Support", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#prepare-cds-model", + "title": "Prepare CDS Model", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#roles-for-ams", + "title": "CAP Roles for AMS", + "depth": 4 + }, + { + "source": "/docs/guides/security/cap-users#attributes-for-ams", + "title": "CAP Attributes for AMS", + "depth": 4 + }, + { + "source": "/docs/guides/security/cap-users#policies", + "title": "Prepare Base Policies", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#local-testing", + "title": "Local Testing", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#ams-deployment", + "title": "Cloud Deployment", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#tracing", + "title": "Tracing", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#xsuaa-roles", + "title": "Role Assignment with XSUAA", + "depth": 2 + }, + { + "source": "/docs/guides/security/cap-users#generate-security-descriptor", + "title": "Generate Security Descriptor", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#publish-security-descriptor", + "title": "Publish Security Descriptor", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#xsuaa-assign", + "title": "Assign Roles in SAP BTP Cockpit", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#developing-with-users", + "title": "Developing with CAP Users", + "depth": 2 + }, + { + "source": "/docs/guides/security/cap-users#reflection", + "title": "Reflection", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#customizing-users", + "title": "Customizing Users", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#switching-users", + "title": "Switching Users", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#switching-to-technical-user", + "title": "Switching to Technical User", + "depth": 4 + }, + { + "source": "/docs/guides/security/cap-users#switching-to-provider-tenant", + "title": "Switching to Technical Provider Tenant", + "depth": 4 + }, + { + "source": "/docs/guides/security/cap-users#switching-to-subscriber-tenant", + "title": "Switching to a Specific Technical Tenant", + "depth": 4 + }, + { + "source": "/docs/guides/security/cap-users#switching-to-privileged-user", + "title": "Switching to Privileged User", + "depth": 4 + }, + { + "source": "/docs/guides/security/cap-users#switching-to-anonymous-user", + "title": "Switching to Anonymous User", + "depth": 4 + }, + { + "source": "/docs/guides/security/cap-users#user-propagation", + "title": "User Propagation", + "depth": 3 + }, + { + "source": "/docs/guides/security/cap-users#between-threads", + "title": "Between Threads", + "depth": 4 + }, + { + "source": "/docs/guides/security/cap-users#user-token", + "title": "Non-CAP Libraries", + "depth": 4 + }, + { + "source": "/docs/guides/security/cap-users#remote-services", + "title": "Remote Services", + "depth": 4 + }, + { + "source": "/docs/guides/security/cap-users#cloud-sdk", + "title": "Cloud SDK", + "depth": 4 + }, + { + "source": "/docs/guides/security/cap-users#pitfalls", + "title": "Pitfalls", + "depth": 2 + }, + { + "source": "/docs/guides/security/remote-authentication", + "title": "Outbound Authentication", + "depth": 1 + }, + { + "source": "/docs/guides/security/remote-authentication#remote-services", + "title": "Remote Service Abstraction", + "depth": 2 + }, + { + "source": "/docs/guides/security/remote-authentication#co-located-services", + "title": "Co-located Services", + "depth": 2 + }, + { + "source": "/docs/guides/security/remote-authentication#prepare", + "title": "1. Prepare the Cloud Foundry Environment", + "depth": 4 + }, + { + "source": "/docs/guides/security/remote-authentication#co-located-consumer", + "title": "2. Prepare and Deploy the Consumer Application", + "depth": 4 + }, + { + "source": "/docs/guides/security/remote-authentication#co-located-provider", + "title": "3. Prepare and Deploy the Provider Application", + "depth": 4 + }, + { + "source": "/docs/guides/security/remote-authentication#4-verify-the-deployment", + "title": "4. Verify the Deployment", + "depth": 4 + }, + { + "source": "/docs/guides/security/remote-authentication#external-services", + "title": "External Services", + "depth": 2 + }, + { + "source": "/docs/guides/security/remote-authentication#ias-app-2-app", + "title": "IAS App-2-App", + "depth": 3 + }, + { + "source": "/docs/guides/security/remote-authentication#1-prepare-and-deploy-the-provider-application", + "title": "1. Prepare and Deploy the Provider Application", + "depth": 4 + }, + { + "source": "/docs/guides/security/remote-authentication#consumer", + "title": "2. Prepare and Deploy the Consumer Application", + "depth": 4 + }, + { + "source": "/docs/guides/security/remote-authentication#connect", + "title": "3. Connect Consumer with Provider", + "depth": 4 + }, + { + "source": "/docs/guides/security/remote-authentication#pitfalls", + "title": "Pitfalls", + "depth": 2 + }, + { + "source": "/docs/guides/security/authorization", + "title": "CAP-level Authorization", + "depth": 1 + }, + { + "source": "/docs/guides/security/authorization#restrictions", + "title": "Declarative Access Control", + "depth": 2 + }, + { + "source": "/docs/guides/security/authorization#static-access-control", + "title": "Static Access Control", + "depth": 2 + }, + { + "source": "/docs/guides/security/authorization#internal-services", + "title": "Internal Services", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#restricting-events", + "title": "@readonly and @insertonly", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#events-and-auto-expose", + "title": "Events to Auto-Exposed Entities", + "depth": 4 + }, + { + "source": "/docs/guides/security/authorization#role-based-access-control", + "title": "Role-Based Access Control", + "depth": 2 + }, + { + "source": "/docs/guides/security/authorization#requires", + "title": "@requires", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#restrict-annotation", + "title": "@restrict", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#supported-combinations-with-cds-resources", + "title": "Supported Combinations with CDS Resources", + "depth": 4 + }, + { + "source": "/docs/guides/security/authorization#combined-restrictions", + "title": "Combined Restrictions", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#propagated-restrictions", + "title": "Propagation of Restrictions", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#restrictions-and-draft-mode", + "title": "Draft Mode", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#autoexposed-restrictions", + "title": "Auto-Exposed and Generated Entities", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#instance-based-auth", + "title": "Instance-Based Access Control", + "depth": 2 + }, + { + "source": "/docs/guides/security/authorization#filter-conditions", + "title": "Filter Conditions", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#user-attrs", + "title": "User Attributes", + "depth": 4 + }, + { + "source": "/docs/guides/security/authorization#unrestricted-xsuaa-attributes", + "title": "Unrestricted XSUAA Attributes", + "depth": 5 + }, + { + "source": "/docs/guides/security/authorization#exists-predicate", + "title": "Exists Predicate", + "depth": 4 + }, + { + "source": "/docs/guides/security/authorization#association-paths", + "title": "Association Paths", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#input-data-auth", + "title": "Checking Input Data", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#reject-403", + "title": "Rejected Entity Selection", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#limitations", + "title": "Limitations", + "depth": 2 + }, + { + "source": "/docs/guides/security/authorization#deep-auth", + "title": "Deep Authorizations", + "depth": 2 + }, + { + "source": "/docs/guides/security/authorization#associations", + "title": "Associations", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#compositions", + "title": "Compositions", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#best-practices", + "title": "Best Practices", + "depth": 2 + }, + { + "source": "/docs/guides/security/authorization#choose-conceptual-roles", + "title": "Choose Conceptual Roles", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#dedicated-services", + "title": "Prefer Single-Purposed, Use-Case Specific Services", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#dedicated-actions", + "title": "Prefer Dedicated Actions for Specific Use-Cases", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#domain-driven-authorization", + "title": "Think About Domain-Driven Authorization", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#limitation-deep-authorization", + "title": "Control Exposure of Associations and Compositions", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#design-authorization-models-from-the-start", + "title": "Design Authorization Models from the Start", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#keep-it-as-simple-as-possible", + "title": "Keep it as Simple as Possible", + "depth": 3 + }, + { + "source": "/docs/guides/security/authorization#separation-of-concerns", + "title": "Separation of Concerns", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-privacy", + "title": "Data Privacy Overview", + "depth": 1 + }, + { + "source": "/docs/guides/security/data-privacy#introduction-to-data-privacy", + "title": "Introduction to Data Privacy", + "depth": 2 + }, + { + "source": "/docs/guides/security/data-privacy#in-a-nutshell", + "title": "In a Nutshell", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-privacy#annotating-personal-data", + "title": "Annotating Personal Data", + "depth": 2 + }, + { + "source": "/docs/guides/security/data-privacy#transparency", + "title": "Automatic Audit Logging", + "depth": 2 + }, + { + "source": "/docs/guides/security/data-privacy#right-of-access", + "title": "Personal Data Management", + "depth": 2 + }, + { + "source": "/docs/guides/security/data-privacy#right-to-be-forgotten", + "title": "Data Retention Management", + "depth": 2 + }, + { + "source": "/docs/guides/security/data-privacy#personal-data-stored-by-cap", + "title": "Personal Data stored by CAP", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-annotations", + "title": "Annotating Personal Data", + "depth": 1 + }, + { + "source": "/docs/guides/security/dpp-annotations#annotated-model", + "title": "Reference App Sample", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-annotations#personaldata", + "title": "@PersonalData...", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-annotations#entitysemantics", + "title": ".EntitySemantics", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-annotations#datasubjectrole", + "title": ".DataSubjectRole", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-annotations#fieldsemantics-datasubjectid", + "title": ".FieldSemantics: DataSubjectID", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-annotations#ispotentiallypersonal", + "title": ".IsPotentiallyPersonal", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-annotations#ispotentiallysensitive", + "title": ".IsPotentiallySensitive", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-annotations#next-steps", + "title": "Next Steps...", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-audit-logging", + "title": "Audit Logging", + "depth": 1 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#annotate-personal-data", + "title": "Annotate Personal Data", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#setup", + "title": "Add the Plugin", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#test-drive-locally", + "title": "Test-drive Locally", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#use-sap-audit-log-service", + "title": "Use SAP Audit Log Service", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#setup-instance-and-deploy-app", + "title": "Setup Instance and Deploy App", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#accessing-audit-logs", + "title": "Accessing Audit Logs", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#generic-audit-logging", + "title": "Generic Audit Logging", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#behind-the-scenes", + "title": "Behind the Scenes...", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#custom-audit-logging", + "title": "Custom Audit Logging", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#service-definition", + "title": "Service Definition", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#sensitive-data-read", + "title": "Sensitive Data Read", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#personal-data-modified", + "title": "Personal Data Modified", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#configuration-modified", + "title": "Configuration Modified", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#security-events", + "title": "Security Events", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#custom-implementation", + "title": "Custom Implementation", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-audit-logging#transactional-outbox", + "title": "Transactional Outbox", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-pdm", + "title": "Personal Data Management", + "depth": 1 + }, + { + "source": "/docs/guides/security/dpp-pdm#annotate-personal-data", + "title": "Annotate Personal Data", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-pdm#provide-a-service-interface-to-sap-personal-data-manager", + "title": "Provide a Service Interface to SAP Personal Data Manager", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-pdm#cap-service-model-for-sap-personal-data-manager", + "title": "CAP Service Model for SAP Personal Data Manager", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-pdm#provide-flat-projections", + "title": "Provide Flat Projections", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-pdm#annotating-search-fields", + "title": "Annotating Search Fields", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-pdm#restrict-access-using-the-requires-annotation", + "title": "Restrict Access Using the `@requires` Annotation", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-pdm#connecting-sap-personal-data-manager", + "title": "Connecting SAP Personal Data Manager", + "depth": 2 + }, + { + "source": "/docs/guides/security/dpp-pdm#activate-access-checks-in-xs-securityjson", + "title": "Activate Access Checks in _xs-security.json_", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-pdm#add-sapxssec-library", + "title": "Add `@sap/xssec` Library", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-pdm#build-and-deploy-your-application", + "title": "Build and Deploy Your Application", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-pdm#subscribe-to-sap-personal-data-manager-service", + "title": "Subscribe to SAP Personal Data Manager Service", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-pdm#create-role-collections", + "title": "Create Role Collections", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-pdm#create-a-service-instance", + "title": "Create a Service Instance", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-pdm#bind-the-service-instance-to-your-application", + "title": "Bind the Service Instance to Your Application.", + "depth": 3 + }, + { + "source": "/docs/guides/security/dpp-pdm#using-the-sap-personal-data-manager-application", + "title": "Using the SAP Personal Data Manager Application", + "depth": 2 + }, + { + "source": "/docs/guides/security/data-protection", + "title": "Product Security Overview", + "depth": 1 + }, + { + "source": "/docs/guides/security/data-protection#secure-communications", + "title": "Secure Communications", + "depth": 2 + }, + { + "source": "/docs/guides/security/data-protection#encrypted-channels", + "title": "Encrypted Communication Channels", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#inbound", + "title": "Inbound Communication (Server)", + "depth": 4 + }, + { + "source": "/docs/guides/security/data-protection#outbound", + "title": "Outbound Communication (Client)", + "depth": 4 + }, + { + "source": "/docs/guides/security/data-protection#internal", + "title": "Internal Communication (Client and Server)", + "depth": 4 + }, + { + "source": "/docs/guides/security/data-protection#filtering", + "title": "Filtering Internet Traffic", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#secure-authentication", + "title": "Secure Authentication", + "depth": 2 + }, + { + "source": "/docs/guides/security/data-protection#authenticate-requests", + "title": "Server Requests", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#authenticate-remote", + "title": "Remote Services", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#sessions", + "title": "Maintaining Sessions", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#secrets", + "title": "Maintaining Secrets", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#secure-authorization", + "title": "Secure Authorization", + "depth": 2 + }, + { + "source": "/docs/guides/security/data-protection#business-authz", + "title": "Business Users", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#cap-endpoints", + "title": "Authorization of CAP Endpoints", + "depth": 4 + }, + { + "source": "/docs/guides/security/data-protection#platform-authz", + "title": "Platform Users", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#secure-multitenancy", + "title": "Secure Multi-Tenancy", + "depth": 2 + }, + { + "source": "/docs/guides/security/data-protection#isolated-persistent-data", + "title": "Isolated Persistent Data", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#isolated-transient-data", + "title": "Isolated Transient Data", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#bad-example", + "title": "**Bad example:**", + "depth": 5 + }, + { + "source": "/docs/guides/security/data-protection#limiting-resource-consumption", + "title": "Limiting Resource Consumption", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#secure-untrusted-input", + "title": "Secure Against Untrusted Input", + "depth": 2 + }, + { + "source": "/docs/guides/security/data-protection#injection-attacks", + "title": "Injection Attacks", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#common-injection-attacks", + "title": "Common Attack Patterns", + "depth": 4 + }, + { + "source": "/docs/guides/security/data-protection#general-injection-attacks", + "title": "General Recommendations Against Injections", + "depth": 4 + }, + { + "source": "/docs/guides/security/data-protection#misues-attacks", + "title": "Service Misuse Attacks", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#dos-attacks", + "title": "Denial-of-Service Attacks", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#http-server-and-cap-protocol-adapter", + "title": "HTTP Server and CAP Protocol Adapter", + "depth": 4 + }, + { + "source": "/docs/guides/security/data-protection#cap-service-runtime", + "title": "CAP Service Runtime", + "depth": 4 + }, + { + "source": "/docs/guides/security/data-protection#database", + "title": "Database", + "depth": 4 + }, + { + "source": "/docs/guides/security/data-protection#supplementary-measures", + "title": "Supplementary Measures", + "depth": 4 + }, + { + "source": "/docs/guides/security/data-protection#additional-attacks", + "title": "Additional Protection Mechanisms", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#secure-by-default", + "title": "Secure by Default and by Design", + "depth": 2 + }, + { + "source": "/docs/guides/security/data-protection#secure-default", + "title": "Secure Default Configuration", + "depth": 3 + }, + { + "source": "/docs/guides/security/data-protection#fail-securely", + "title": "Fail Securely", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/", + "title": "Extensibility", + "depth": 1 + }, + { + "source": "/docs/guides/extensibility/customization", + "title": "Extending SaaS Applications", + "depth": 1 + }, + { + "source": "/docs/guides/extensibility/customization#introduction--overview", + "title": "Introduction & Overview", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/customization#prerequisites", + "title": "Prerequisites", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/customization#prep-as-provider", + "title": "As a SaaS Provider", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/customization#1-enable-extensibility", + "title": "1. Enable Extensibility", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#restrictions", + "title": "2. Restrict Extension Points", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#templates", + "title": "3. Provide Template Projects", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#create-an-extension-project-template", + "title": "Create an Extension Project (Template)", + "depth": 4 + }, + { + "source": "/docs/guides/extensibility/customization#add-sample-content", + "title": "Add Sample Content", + "depth": 4 + }, + { + "source": "/docs/guides/extensibility/customization#add-test-data", + "title": "Add Test Data", + "depth": 4 + }, + { + "source": "/docs/guides/extensibility/customization#add-a-readme", + "title": "Add a Readme", + "depth": 4 + }, + { + "source": "/docs/guides/extensibility/customization#guide", + "title": "4. Provide Extension Guides", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#5-deploy-application", + "title": "5. Deploy Application", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#prep-as-operator", + "title": "As a SaaS Customer", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/customization#1-subscribe-to-saas-app", + "title": "1. Subscribe to SaaS App", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#prepare-an-extension-tenant", + "title": "2. Prepare an Extension Tenant", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#start-ext-project", + "title": "3. Start an Extension Project", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#pull-base", + "title": "4. Pull the Latest Base Model", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#5-install-the-base-model", + "title": "5. Install the Base Model", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#write-extension", + "title": "6. Write the Extension", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#test-locally", + "title": "7. Test-Drive Locally", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#add-local-test-data", + "title": "Add Local Test Data", + "depth": 4 + }, + { + "source": "/docs/guides/extensibility/customization#verify-the-extension", + "title": "Verify the Extension", + "depth": 4 + }, + { + "source": "/docs/guides/extensibility/customization#push-extension", + "title": "8. Push to Test Tenant", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#test-extension", + "title": "Verify the Extension", + "depth": 4 + }, + { + "source": "/docs/guides/extensibility/customization#add-data", + "title": "9. Add Data", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#push-to-prod", + "title": "10. Activate the Extension", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#appendices", + "title": "Appendices", + "depth": 1 + }, + { + "source": "/docs/guides/extensibility/customization#app-router", + "title": "Configuring App Router", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/customization#about-extension-models", + "title": "About Extension Models", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/customization#extending-the-data-model", + "title": "Extending the Data Model", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#extending-the-service-model", + "title": "Extending the Service Model", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#extending-ui-annotations", + "title": "Extending UI Annotations", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#extending-array-values", + "title": "Extending Array Values", + "depth": 4 + }, + { + "source": "/docs/guides/extensibility/customization#semantic-ids", + "title": "Semantic IDs", + "depth": 4 + }, + { + "source": "/docs/guides/extensibility/customization#localizable-texts", + "title": "Localizable Texts", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#cds-login", + "title": "Simplify Your Workflow With `cds login`", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/customization#where-tokens-are-stored", + "title": "Where Tokens Are Stored", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#how-to-login", + "title": "How to Login", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#simplified-workflow", + "title": "Simplified Workflow", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#refreshing-tokens", + "title": "Refreshing Tokens", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#cleaning-up", + "title": "Cleaning Up", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#debugging", + "title": "Debugging", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/customization#add-data-to-extensions", + "title": "Add Data to Extensions", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/feature-toggles", + "title": "Feature Toggles", + "depth": 1 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#introduction-and-overview", + "title": "Introduction and Overview", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#get-cloud-cap-samples-java-for-step-by-step-exercises", + "title": "Get `cloud-cap-samples-java` for step-by-step Exercises", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#get-capsamples-for-step-by-step-exercises", + "title": "Get `cap/samples` for Step-By-Step Exercises", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#enable-feature-toggles", + "title": "Enable Feature Toggles", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#add-sapcds-mtxs-package-dependency", + "title": "Add `@sap/cds-mtxs` Package Dependency", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#switch-on-cdsrequirestoggles", + "title": "Switch on `cds.requires.toggles`", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#adding-features-in-cds", + "title": "Adding Features in CDS", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#feature-ftsisbn", + "title": "Feature *fts/isbn*", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#feature-ftsreviews", + "title": "Feature *fts/reviews*", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#limitations", + "title": "Limitations", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#toggling-features", + "title": "Toggling Features", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#in-development", + "title": "In Development", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#in-production", + "title": "In Production", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#test-drive-locally", + "title": "Test-Drive Locally", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#run-cds-watch", + "title": "Run `cds watch`", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#test-fiori-node", + "title": "See Effects in SAP Fiori UIs", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#model-provider-in-sidecar", + "title": "Model Provider in Sidecar", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#create-sidecar-as-nodejs-project", + "title": "Create Sidecar as Node.js Project", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#add-remote-service-link-to-sidecar", + "title": "Add Remote Service Link to Sidecar", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#test-drive-sidecar-locally", + "title": "Test-Drive Sidecar Locally", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#remote-getcsn-calls-to-sidecar-at-runtime", + "title": "Remote `getCsn()` Calls to Sidecar at Runtime", + "depth": 4 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#test-fiori-java", + "title": "See Effects in SAP Fiori UIs", + "depth": 3 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#feature-vector-providers", + "title": "Feature Vector Providers", + "depth": 2 + }, + { + "source": "/docs/guides/extensibility/feature-toggles#feature-toggled-custom-logic", + "title": "Feature-Toggled Custom Logic", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/", + "title": "Deployment", + "depth": 1 + }, + { + "source": "/docs/guides/deploy/to-cf", + "title": "Deploy to Cloud Foundry", + "depth": 1 + }, + { + "source": "/docs/guides/deploy/to-cf#intro--overview", + "title": "Intro & Overview", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/to-cf#prerequisites", + "title": "Prerequisites", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/to-cf#btp-and-hana", + "title": "1. SAP BTP with SAP HANA Cloud Database Up and Running", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-cf#latest-cds", + "title": "2. Latest Versions of `@sap/cds-dk`", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-cf#mbt", + "title": "3. Cloud MTA Build Tool", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-cf#cf-cli", + "title": "4. Cloud Foundry CLI w/ MTA Plugins", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-cf#prepare-for-production", + "title": "Prepare for Production", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/to-cf#1-sap-hana-database", + "title": "1. SAP HANA Database", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-cf#2-authorizationauthentication", + "title": "2. Authorization/Authentication", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-cf#remote-services", + "title": "3. Remote Service Consumption", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-cf#add-cloud-sdk", + "title": "SAP Cloud SDK", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-cf#native-fetch", + "title": "Native Fetch Client ", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-cf#add-mta-yaml", + "title": "4. MTA-Based Deployment", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-cf#add-ui", + "title": "5. User Interfaces", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-cf#option-a-sap-cloud-portal", + "title": "Option A: SAP Cloud Portal", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-cf#option-b-sap-btp-application-frontend", + "title": "Option B: SAP BTP Application Frontend ", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-cf#add-multitenancy", + "title": "6. Optional: Multitenancy", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-cf#build-and-deploy", + "title": "Build and Deploy", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/to-cf#inspect-apps-in-btp-cockpit", + "title": "Inspect Apps in BTP Cockpit", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-cf#use-mta-extensions-with-cds-up", + "title": "Use MTA Extensions with `cds up`", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-cf#freeze-dependencies", + "title": "Staying Up-to-date", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/to-cf#upgrade-tenants-in-java", + "title": "Upgrade Tenants in Java", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/to-kyma", + "title": "Deploy to Kyma", + "depth": 1 + }, + { + "source": "/docs/guides/deploy/to-kyma#overview", + "title": "Overview", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/to-kyma#prerequisites", + "title": "Prerequisites", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/to-kyma#configure-kubernetes", + "title": "Configure Kubernetes", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#get-access-to-a-container-registry", + "title": "Get Access to a Container Registry", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#set-up-your-cluster-for-a-private-container-registry", + "title": "Set Up Your Cluster for a Private Container Registry", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#deploy-to-kyma-1", + "title": "Deploy to Kyma", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/to-kyma#user-interfaces", + "title": "User Interfaces ", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#add-cap-helm-charts", + "title": "Add CAP Helm Charts", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#build-and-deploy", + "title": "Build and Deploy", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#next-up", + "title": "Next Up...", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/to-kyma#deep-dives", + "title": "Deep Dives", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/to-kyma#configure-image-repository", + "title": "Configure Image Repository", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-kyma#customize-helm-chart", + "title": "Customize Helm Chart", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-kyma#about-cap-helm", + "title": "About CAP Helm Charts", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#configure-helm-chart", + "title": "Configure", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#global-properties", + "title": "Global Properties", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#deployment-properties", + "title": "Deployment Properties", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#sap-btp-services", + "title": "SAP BTP Services", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-kyma#built-in-sap-btp-services", + "title": "Built-in SAP BTP Services", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#arbitrary-btp-services", + "title": "Arbitrary BTP Services", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#configuration-options-for-services", + "title": "Configuration Options for Services", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#configuration-options-for-service-bindings", + "title": "Configuration Options for Service Bindings", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#configuration-options-for-container-images", + "title": "Configuration Options for Container Images", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#html5-applications", + "title": "HTML5 Applications", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#backend-destinations", + "title": "Backend Destinations", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/to-kyma#modify", + "title": "Modify", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-kyma#extend", + "title": "Extend", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-kyma#services-from-cloud-foundry", + "title": "Services from Cloud Foundry", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-kyma#cloud-native-buildpacks", + "title": "Cloud Native Buildpacks", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/to-kyma#cap-operator", + "title": "CAP Operator", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/cicd", + "title": "Deploy using CI/CD Pipelines", + "depth": 1 + }, + { + "source": "/docs/guides/deploy/cicd#github-actions", + "title": "GitHub Actions", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/cicd#deploy-to-staging", + "title": "Deploy to Staging", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/cicd#cloud-foundry", + "title": "Cloud Foundry", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/cicd#kyma", + "title": "Kyma", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/cicd#btp-prerequisites", + "title": "BTP Prerequisites", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/cicd#youre-set", + "title": "You're set!", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/cicd#create-a-github-release", + "title": "Create a GitHub Release", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/cicd#prerequisites", + "title": "Prerequisites", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/cicd#publish-the-release", + "title": "Publish the release", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/cicd#sap-continuous-integration-and-delivery", + "title": "SAP Continuous Integration and Delivery", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/cicd#cicd-pipelines-with-sap-piper", + "title": "CI/CD Pipelines with SAP Piper", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/", + "title": "Deploy Multitenant SaaS Applications", + "depth": 1 + }, + { + "source": "/docs/guides/multitenancy/#introduction--overview", + "title": "Introduction & Overview", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/#enable-multitenancy", + "title": "Enable Multitenancy", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/#test-drive-locally", + "title": "Test-Drive Locally", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/#1-start-mtx-sidecar", + "title": "1. Start MTX Sidecar", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/#2-launch-the-app-server", + "title": "2. Launch the app server", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/#3-subscribe-tenants", + "title": "3. Subscribe Tenants", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/#4-test-via-the-apps-ui", + "title": "4. Test via the app's UI", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/#5-upgrade-your-tenant", + "title": "5. Upgrade Your Tenant", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/#deploy-to-cloud", + "title": "Deploy to Cloud", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/#subscribe-via-btp-cockpit", + "title": "Subscribe via BTP Cockpit", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/#cloud-foundry", + "title": "Cloud Foundry", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/#update-database-schema", + "title": "Update Database Schema", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/#test-drive-in-hybrid-setup", + "title": "Test-Drive in Hybrid Setup", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/#sap-hana-tenant-management-service-v2", + "title": "sap-hana-tenant-management-service-v2", + "depth": 6 + }, + { + "source": "/docs/guides/multitenancy/#sap-hana-tms-v2", + "title": "SAP HANA TMS v2", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/#configure-mtxs-for-tenant-management-service", + "title": "Configure MTXS for Tenant Management Service", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/#handle-sap-hana-tenants", + "title": "Handle SAP HANA Tenants", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/#mandatory-specify-a-unique-prefix-for-the-sap-hana-tenant-name", + "title": "Mandatory: specify a unique prefix for the SAP HANA tenant name", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/#mandatory-for-cap-java-applications", + "title": "Mandatory for CAP Java Applications", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/#assign-many-tenant-containers-to-a-common-sap-hana-tenant", + "title": "Assign Many Tenant Containers to a Common SAP HANA Tenant", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/#delete-sap-hana-tenants", + "title": "Delete SAP HANA tenants", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/#limitations", + "title": "Limitations", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/#saas-dependencies", + "title": "SaaS Dependencies", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/#additional-services", + "title": "Additional Services", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/#adding-custom-handlers", + "title": "Adding Custom Handlers", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/#configuring-the-java-service", + "title": "Configuring the Java Service", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/#option-provisioning-only", + "title": "Option: Provisioning Only", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/#appendix", + "title": "Appendix", + "depth": 1 + }, + { + "source": "/docs/guides/multitenancy/#about-saas-applications", + "title": "About SaaS Applications", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/#about-sidecar-setups", + "title": "About Sidecar Setups", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/mtxs", + "title": "MTX Services Reference", + "depth": 1 + }, + { + "source": "/docs/guides/multitenancy/mtxs#introduction--overview", + "title": "Introduction & Overview", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/mtxs#getting-started", + "title": "Getting Started…", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/mtxs#add-sapcds-mtxs-package-dependency", + "title": "Add `@sap/cds-mtxs` Package Dependency", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#enable-mtx-functionality", + "title": "Enable MTX Functionality", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#test-drive-locally", + "title": "Test-Drive Locally", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#grow-as-you-go", + "title": "Grow As You Go", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/mtxs#enable-mtx-only-if-required", + "title": "Enable MTX Only if Required", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#testing-with-minimal-setup", + "title": "Testing With Minimal Setup", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#sidecars", + "title": "Sidecar Setups", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/mtxs#create-sidecar-as-a-nodejs-subproject", + "title": "Create Sidecar as a Node.js Subproject", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#required-mtx-services", + "title": "Required MTX Services", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#using-shared-database", + "title": "Using Shared Database", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#additional-development-settings", + "title": "Additional `[development]` Settings", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#testing-sidecar-setups", + "title": "Testing Sidecar Setups", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#modelproviderservice-serving-models-from-main-app", + "title": "_ModelProviderService_ serving models from main app", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#note-service-bindings-by-cds-watch", + "title": "Note: Service Bindings by `cds watch`", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#build-sidecar-for-production", + "title": "Build Sidecar for Production", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#test-drive-production-locally", + "title": "Test-Drive Production Locally", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#modelproviderservice-serving-models-from-main-app-1", + "title": "_ModelProviderService_ serving models from main app", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#conf", + "title": "Configuration", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/mtxs#conf-shortcuts", + "title": "Shortcuts `cds.requires.multitenancy / extensibility / toggles`", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#conf-individual", + "title": "Configuring Individual Services", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#allowed-values", + "title": "Allowed Values", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#common-config-options", + "title": "Common Config Options", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#combined-with-convenience-flags", + "title": "Combined with Convenience Flags", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#individual-configurations-only", + "title": "Individual Configurations Only", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#presets", + "title": "Using Configuration Presets", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#profile-based-configuration", + "title": "Profile-based configuration", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#preset-based-configuration", + "title": "Preset-based configuration", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#inspecting-effective-configuration", + "title": "Inspecting Effective Configuration", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#customization", + "title": "Customization", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/mtxs#customizing-service-definitions", + "title": "Customizing Service Definitions", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#adding-custom-lifecycle-event-handlers", + "title": "Adding Custom Lifecycle Event Handlers", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#example-handler-for-saasprovisioningservice", + "title": "Example handler for SaasProvisioningService", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#consumption", + "title": "Consumption", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/mtxs#via-programmatic-apis", + "title": "Via Programmatic APIs", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#via-rest-apis", + "title": "Via REST APIs", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#modelproviderservice", + "title": "ModelProviderService", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/mtxs#model-provider-config", + "title": "Configuration", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#model-provider-presets", + "title": "Supported Presets", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#getcsn-tenant-toggles--csn", + "title": "`getCsn` _(tenant, toggles) → CSN_", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#example-get-csn", + "title": "Example Usage", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#getedmx-tenant-toggles-service-locale--edmx", + "title": "`getEdmx` _(tenant, toggles, service, locale) → EDMX_", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#example-get-edmx", + "title": "Example Usage", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#getresources---tar", + "title": "`getResources` _() → TAR_", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#getextensions-tenant--csn", + "title": "`getExtensions` _(tenant) → CSN_", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#isextended-tenant--truefalse", + "title": "`isExtended` _(tenant) → true|false_", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#extensibilityservice", + "title": "ExtensibilityService", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/mtxs#extensibility-config", + "title": "Configuration", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#get-extensions", + "title": "GET `Extensions/` _→ []_", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#request-format", + "title": "Request Format", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#response-format", + "title": "Response Format", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#example-request", + "title": "Example Request", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#get-extension", + "title": "Get a specific extension", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#get-all-extensions", + "title": "Get all extensions", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#put-extensions", + "title": "PUT `Extensions/` (\\[csn\\]) _→ \\[\\]_", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#http-request-options", + "title": "HTTP Request Options", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#request-format-1", + "title": "Request Format", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#response-format-1", + "title": "Response Format", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#example-request-1", + "title": "Example Request", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#delete-extensions", + "title": "DELETE `Extensions/`", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#http-request-options-1", + "title": "HTTP Request Options", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#request-format-2", + "title": "Request Format", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#example-usage", + "title": "Example Usage", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#extension-restrictions", + "title": "Extension Restrictions", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#restrict-service-extensions", + "title": "Restrict Service Extensions", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#restrict-entities-and-fields", + "title": "Restrict Entities and Fields", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#restrict--enable-annotations", + "title": "Restrict / Enable Annotations", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#restrict-unbound-entities", + "title": "Restrict Unbound Entities", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#deploymentservice", + "title": "DeploymentService", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/mtxs#deployment-config", + "title": "Configuration", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#deployment-presets", + "title": "Supported Presets", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#subscribe-tenant", + "title": "`subscribe` _(tenant)_", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#upgrade-tenant", + "title": "`upgrade` _(tenant)_", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#drop-creating-databases-for-sqlite", + "title": "Drop-Creating Databases for SQLite", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#schema-evolution-for-sap-hana", + "title": "Schema Evolution for SAP HANA", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#unsubscribe-tenant", + "title": "`unsubscribe` _(tenant)_", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#saasprovisioningservice", + "title": "SaasProvisioningService", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/mtxs#saas-provisioning-config", + "title": "Configuration", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#http-request-options-2", + "title": "HTTP Request Options", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#passing-tenant-specific-deployment-parameters", + "title": "Passing tenant-specific deployment parameters", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#example-usage-1", + "title": "Example Usage", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#subscription", + "title": "Subscription", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#upgrade", + "title": "Upgrade", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#get-tenant", + "title": "GET `tenant/`", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#example-tenant-metadata", + "title": "Example Usage", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#example-get-tenant-metadata", + "title": "Get Metadata for a Specific Tenant", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#get-metadata-for-all-tenants", + "title": "Get Metadata for All Tenants", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#put-tenant", + "title": "PUT `tenant/` (...)", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#example-post-tenant", + "title": "Example Usage", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#delete-tenant", + "title": "DELETE `tenant/`", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#get-dependencies", + "title": "GET `dependencies` _→ []_", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#upgrade-tenants--jobs", + "title": "`upgrade` _[tenants] → Jobs_", + "depth": 3 + }, + { + "source": "/docs/guides/multitenancy/mtxs#example-upgrade", + "title": "Example Usage", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#asynchronously-upgrade-a-list-of-tenants", + "title": "Asynchronously Upgrade a List of Tenants", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#asynchronously-upgrade-all-tenants", + "title": "Asynchronously Upgrade All Tenants", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#about-technical-tenant-t0", + "title": "About technical Tenant `t0`", + "depth": 2 + }, + { + "source": "/docs/guides/multitenancy/mtxs#what-t0-stores", + "title": "What `t0` stores", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#lifecycle-of-t0", + "title": "Lifecycle of `t0`", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#schema-evolution", + "title": "Schema evolution", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#special-constraints-for-t0", + "title": "Special constraints for `t0`", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#configuring-a-different-tenant-name-for-t0", + "title": "Configuring a different Tenant Name for `t0`", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#default-database_id-and-lazyt0", + "title": "Default `database_id` and `lazyT0`", + "depth": 4 + }, + { + "source": "/docs/guides/multitenancy/mtxs#lazyt0-configuration", + "title": "`lazyT0` configuration", + "depth": 5 + }, + { + "source": "/docs/guides/multitenancy/mtxs#old-mtx-reference", + "title": "Old MTX Reference", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/microservices", + "title": "Microservices with CAP", + "depth": 1 + }, + { + "source": "/docs/guides/deploy/microservices#create-a-solution-monorepo", + "title": "Create a Solution Monorepo", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/microservices#using-a-shared-database", + "title": "Using a Shared Database", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/microservices#add-a-project-for-shared-database", + "title": "Add a Project For Shared Database", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#all-in-one-deployment", + "title": "All-in-one Deployment", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/microservices#deployment-descriptor", + "title": "Deployment Descriptor", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#database", + "title": "Database", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#applications", + "title": "Applications", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#authentication", + "title": "Authentication", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#messaging", + "title": "Messaging", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#destinations", + "title": "Destinations", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#app-router", + "title": "App Router", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#static-content", + "title": "Static Content", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/microservices#configuration", + "title": "Configuration", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/microservices#deploy", + "title": "Deploy", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#deployment-as-separate-mta", + "title": "Deployment as Separate MTA", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/microservices#database-1", + "title": "Database", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#binding-to-shared-database", + "title": "Binding to shared database", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/microservices#subsequent-updates", + "title": "Subsequent updates", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/microservices#late-cut-microservices", + "title": "Late-Cut Microservices", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/microservices#flexibility-in-deployments", + "title": "Flexibility in Deployments", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#a-late-cut", + "title": "A Late Cut", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#best-practices", + "title": "Best Practices", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#appendix", + "title": "Appendix", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/microservices#monolith-or-microservice", + "title": "Monolith or Microservice", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#application-instances", + "title": "Application Instances", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#modules", + "title": "Modules", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#multiple-applications", + "title": "Multiple Applications", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#resource-separation", + "title": "Resource Separation", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/microservices#independent-scaling", + "title": "Independent Scaling", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/microservices#fault-tolerance", + "title": "Fault Tolerance", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/microservices#multiple-deployment-units", + "title": "Multiple Deployment Units", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#multiple-databases", + "title": "Multiple Databases", + "depth": 3 + }, + { + "source": "/docs/guides/deploy/microservices#data-federation", + "title": "Data Federation", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/health-checks", + "title": "Health Checks", + "depth": 1 + }, + { + "source": "/docs/guides/deploy/build", + "title": "Customizing `cds build`", + "depth": 1 + }, + { + "source": "/docs/guides/deploy/build#automatic-build-tasks", + "title": "Automatic Build Tasks", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/build#extending-cds-build", + "title": "Extending `cds build`", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/build#custom-build-tasks", + "title": "Custom Build Tasks", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/build#build-task-types", + "title": "Build Task Types", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/build#build-task-properties", + "title": "Build Task Properties", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/build#build-target-folder", + "title": "Build Target Folder", + "depth": 2 + }, + { + "source": "/docs/guides/deploy/build#nodejs", + "title": "Node.js", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/build#build-ws", + "title": "npm Workspace Support ", + "depth": 4 + }, + { + "source": "/docs/guides/deploy/build#java", + "title": "Java", + "depth": 4 + }, + { + "source": "/docs/cds/", + "title": "Core Data Services (CDS)", + "depth": 1 + }, + { + "source": "/docs/cds/cdl", + "title": "Conceptual Definition Language (CDL)", + "depth": 1 + }, + { + "source": "/docs/cds/cdl#language-preliminaries", + "title": "Language Preliminaries", + "depth": 2 + }, + { + "source": "/docs/cds/cdl#keywords--identifiers", + "title": "Keywords & Identifiers", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#built-in-types", + "title": "Built-in Types", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#vector-embeddings", + "title": "Vector Embeddings", + "depth": 6 + }, + { + "source": "/docs/cds/cdl#literals", + "title": "Literals", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#date--time-literals", + "title": "Date & Time Literals", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#multiline-literals", + "title": "Multiline String Literals", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#model-imports", + "title": "Model Imports", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#using", + "title": "The `using` Directive", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#model-resolution", + "title": "Model Resolution", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#namespaces", + "title": "Namespaces", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#the-namespace-directive", + "title": "The `namespace` Directive", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#context", + "title": "The `context` Directive", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#scoped-names", + "title": "Scoped Definitions", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#fully-qualified-names", + "title": "Fully Qualified Names", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#comments", + "title": "Comments", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#doc-comments", + "title": "Doc Comments", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#entities--type-definitions", + "title": "Entities & Type Definitions", + "depth": 2 + }, + { + "source": "/docs/cds/cdl#entity-definitions", + "title": "Entity Definitions", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#type-definitions", + "title": "Type Definitions", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#structured-types", + "title": "Structured Types", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#arrayed-types", + "title": "Arrayed Types", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#null-values", + "title": "Null Values", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#virtual-elements", + "title": "Virtual Elements", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#calculated-elements", + "title": "Calculated Elements", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#on-read", + "title": "On-read", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#on-write", + "title": "On-write", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#association-like-calculated-elements", + "title": "Association-like calculated elements", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#default-values", + "title": "Default Values", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#type-references", + "title": "Type References", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#constraints", + "title": "Constraints", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#enums", + "title": "Enums", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#views--projections", + "title": "Views & Projections", + "depth": 2 + }, + { + "source": "/docs/cds/cdl#as-select-from", + "title": "The `as select from` Variant", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#as-projection-on", + "title": "The `as projection on` Variant", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#views-with-inferred-signatures", + "title": "Views with Inferred Signatures", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#virtual-elements-in-views", + "title": "Virtual elements in views", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#views-with-parameters", + "title": "Views with Parameters", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#runtimeviews", + "title": "Runtime Views", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#associations", + "title": "Associations", + "depth": 2 + }, + { + "source": "/docs/cds/cdl#unmanaged-associations", + "title": "Unmanaged Associations", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#managed-to-one-associations", + "title": "Managed (To-One) Associations", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#managed-associations", + "title": "managed-associations", + "depth": 6 + }, + { + "source": "/docs/cds/cdl#to-many-associations", + "title": "To-many Associations", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#many-to-many-associations", + "title": "Many-to-many Associations", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#compositions", + "title": "Compositions", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#managed-compositions", + "title": "Managed Compositions of Aspects", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#with-inline-targets", + "title": "With Inline Targets", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#with-named-targets", + "title": "With Named Targets", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#default-target-cardinality", + "title": "Default Target Cardinality", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#for-many-to-many-relationships", + "title": "For Many-to-many Relationships", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#publish-associations", + "title": "Publish Associations in Projections", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#publish-associations-with-filter", + "title": "Publish Associations with Infix Filter", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#annotations", + "title": "Annotations", + "depth": 2 + }, + { + "source": "/docs/cds/cdl#annotation-syntax", + "title": "Annotation Syntax", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#using-annotate-directives", + "title": "Using `annotate` Directives", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#annotation-targets", + "title": "Annotation Targets", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#annotation-values", + "title": "Annotation Values", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#records-as-syntax-shortcuts", + "title": "Records as Syntax Shortcuts", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#annotation-propagation", + "title": "Annotation Propagation", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#expressions-as-annotation-values", + "title": "Expressions as Annotation Values", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#name-resolution", + "title": "Name resolution", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#csn-representation", + "title": "CSN Representation", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#propagation", + "title": "Propagation", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#cds-annotations", + "title": "CDS Annotations", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#odata-annotations", + "title": "OData Annotations", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#extend-array-annotations", + "title": "Extend Array Annotations", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#aspects", + "title": "Aspects", + "depth": 2 + }, + { + "source": "/docs/cds/cdl#the-extend-directive", + "title": "The `extend` Directive", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#the-annotate-directive", + "title": "The `annotate` Directive", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#named-aspects", + "title": "Named Aspects", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#includes", + "title": "Includes -- `:` as Shortcut Syntax", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#extend-view", + "title": "Extending Views and Projections", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#services", + "title": "Services", + "depth": 2 + }, + { + "source": "/docs/cds/cdl#service-definitions", + "title": "Service Definitions", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#exposed-entities", + "title": "Exposed Entities", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#auto--redirected-associations", + "title": "(Auto-) Redirected Associations", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#resolving-ambiguities", + "title": "Resolving Ambiguities", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#using-redirected-to-with-projected-associations", + "title": "Using `redirected to` with Projected Associations", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#using-cdsredirectiontarget-annotations", + "title": "Using `@cds.redirection.target` Annotations", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#auto-exposed-entities", + "title": "Auto-Exposed Entities", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#actions", + "title": "Custom Actions and Functions", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#bound-actions", + "title": "Bound Actions and Functions", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#actions-returning-media", + "title": "Returning Media Data Streams", + "depth": 4 + }, + { + "source": "/docs/cds/cdl#events", + "title": "Custom-Defined Events", + "depth": 3 + }, + { + "source": "/docs/cds/cdl#extend-service", + "title": "Extending Services", + "depth": 3 + }, + { + "source": "/docs/cds/csn", + "title": "Core Schema Notation (CSN)", + "depth": 1 + }, + { + "source": "/docs/cds/csn#anatomy", + "title": "Anatomy", + "depth": 2 + }, + { + "source": "/docs/cds/csn#properties", + "title": "Properties", + "depth": 4 + }, + { + "source": "/docs/cds/csn#literals", + "title": "Literals", + "depth": 2 + }, + { + "source": "/docs/cds/csn#remarks", + "title": "Remarks", + "depth": 4 + }, + { + "source": "/docs/cds/csn#definitions", + "title": "Definitions", + "depth": 2 + }, + { + "source": "/docs/cds/csn#example", + "title": "Example", + "depth": 4 + }, + { + "source": "/docs/cds/csn#def-properties", + "title": "Properties", + "depth": 4 + }, + { + "source": "/docs/cds/csn#type-definitions", + "title": "Type Definitions", + "depth": 2 + }, + { + "source": "/docs/cds/csn#example-1", + "title": "Example", + "depth": 4 + }, + { + "source": "/docs/cds/csn#properties-1", + "title": "Properties", + "depth": 4 + }, + { + "source": "/docs/cds/csn#scalar-types", + "title": "Scalar Types", + "depth": 3 + }, + { + "source": "/docs/cds/csn#structured-types", + "title": "Structured Types", + "depth": 3 + }, + { + "source": "/docs/cds/csn#arrayed-types", + "title": "Arrayed Types", + "depth": 3 + }, + { + "source": "/docs/cds/csn#enumeration-types", + "title": "Enumeration Types", + "depth": 3 + }, + { + "source": "/docs/cds/csn#entity-definitions", + "title": "Entity Definitions", + "depth": 2 + }, + { + "source": "/docs/cds/csn#example-2", + "title": "Example", + "depth": 4 + }, + { + "source": "/docs/cds/csn#properties-2", + "title": "Properties", + "depth": 4 + }, + { + "source": "/docs/cds/csn#view-definitions", + "title": "View Definitions", + "depth": 3 + }, + { + "source": "/docs/cds/csn#example-3", + "title": "Example", + "depth": 4 + }, + { + "source": "/docs/cds/csn#properties-3", + "title": "Properties", + "depth": 4 + }, + { + "source": "/docs/cds/csn#views-with-declared-signatures", + "title": "Views with Declared Signatures", + "depth": 3 + }, + { + "source": "/docs/cds/csn#views-with-parameters", + "title": "Views with Parameters", + "depth": 3 + }, + { + "source": "/docs/cds/csn#projections", + "title": "Projections", + "depth": 3 + }, + { + "source": "/docs/cds/csn#properties-4", + "title": "Properties", + "depth": 4 + }, + { + "source": "/docs/cds/csn#associations", + "title": "Associations", + "depth": 2 + }, + { + "source": "/docs/cds/csn#basic-to-one-associations", + "title": "Basic to-one Associations", + "depth": 3 + }, + { + "source": "/docs/cds/csn#assoc-card", + "title": "With Specified `cardinality`", + "depth": 3 + }, + { + "source": "/docs/cds/csn#assoc-on", + "title": "With Specified `on` Condition", + "depth": 3 + }, + { + "source": "/docs/cds/csn#assoc-keys", + "title": "With Specified `keys`", + "depth": 3 + }, + { + "source": "/docs/cds/csn#annotations", + "title": "Annotations", + "depth": 2 + }, + { + "source": "/docs/cds/csn#example-4", + "title": "Example", + "depth": 4 + }, + { + "source": "/docs/cds/csn#aspects", + "title": "Aspects", + "depth": 2 + }, + { + "source": "/docs/cds/csn#extend-with-named-aspect", + "title": "Extend with \\", + "depth": 3 + }, + { + "source": "/docs/cds/csn#extend-with-anonymous-aspect", + "title": "Extend with \\", + "depth": 3 + }, + { + "source": "/docs/cds/csn#annotate-with-anonymous-aspect", + "title": "annotate with \\", + "depth": 3 + }, + { + "source": "/docs/cds/csn#services", + "title": "Services", + "depth": 2 + }, + { + "source": "/docs/cds/csn#actions--functions", + "title": "Actions / Functions", + "depth": 3 + }, + { + "source": "/docs/cds/csn#properties-5", + "title": "Properties", + "depth": 4 + }, + { + "source": "/docs/cds/csn#imports", + "title": "Imports", + "depth": 2 + }, + { + "source": "/docs/cds/csn#example-5", + "title": "Example", + "depth": 4 + }, + { + "source": "/docs/cds/csn#i18n", + "title": "i18n", + "depth": 2 + }, + { + "source": "/docs/cds/cql", + "title": "Query Language (CQL)", + "depth": 1 + }, + { + "source": "/docs/cds/cql#postfix-projections", + "title": "Postfix Projections", + "depth": 2 + }, + { + "source": "/docs/cds/cql#nested-expands", + "title": "Nested Expands ", + "depth": 3 + }, + { + "source": "/docs/cds/cql#alias", + "title": "Alias", + "depth": 4 + }, + { + "source": "/docs/cds/cql#expressions", + "title": "Expressions", + "depth": 4 + }, + { + "source": "/docs/cds/cql#nested-inlines", + "title": "Nested Inlines ", + "depth": 3 + }, + { + "source": "/docs/cds/cql#smart--selector", + "title": "Smart `*` Selector", + "depth": 2 + }, + { + "source": "/docs/cds/cql#example", + "title": "Example:", + "depth": 4 + }, + { + "source": "/docs/cds/cql#excluding-clause", + "title": "Excluding Clause", + "depth": 3 + }, + { + "source": "/docs/cds/cql#in-nested-expands", + "title": "In Nested Expands ", + "depth": 3 + }, + { + "source": "/docs/cds/cql#in-nested-inlines", + "title": "In Nested Inlines ", + "depth": 3 + }, + { + "source": "/docs/cds/cql#path-expressions", + "title": "Path Expressions", + "depth": 2 + }, + { + "source": "/docs/cds/cql#path-expressions-in-from-clauses", + "title": "Path Expressions in `from` Clauses", + "depth": 3 + }, + { + "source": "/docs/cds/cql#path-expressions-in-all-other-clauses", + "title": "Path Expressions in All Other Clauses", + "depth": 3 + }, + { + "source": "/docs/cds/cql#with-infix-filters", + "title": "With Infix Filters", + "depth": 3 + }, + { + "source": "/docs/cds/cql#exists-predicate", + "title": "Exists Predicate", + "depth": 3 + }, + { + "source": "/docs/cds/cql#casts-in-cdl", + "title": "Casts in CDL", + "depth": 2 + }, + { + "source": "/docs/cds/cql#use-enums", + "title": "Use enums", + "depth": 2 + }, + { + "source": "/docs/cds/cql#association-definitions", + "title": "Association Definitions", + "depth": 2 + }, + { + "source": "/docs/cds/cql#query-local-mixins", + "title": "Query-Local Mixins", + "depth": 3 + }, + { + "source": "/docs/cds/cql#select-list-associations", + "title": "In the select list", + "depth": 3 + }, + { + "source": "/docs/cds/cqn", + "title": "Query Notation (CQN)", + "depth": 1 + }, + { + "source": "/docs/cds/cqn#introduction", + "title": "Introduction", + "depth": 2 + }, + { + "source": "/docs/cds/cqn#select", + "title": "SELECT", + "depth": 2 + }, + { + "source": "/docs/cds/cqn#from", + "title": "`.from`", + "depth": 3 + }, + { + "source": "/docs/cds/cqn#source", + "title": "source", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#columns", + "title": "`.columns`", + "depth": 3 + }, + { + "source": "/docs/cds/cqn#column", + "title": "column", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#as", + "title": "as", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#cast", + "title": "cast", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#infix", + "title": "infix", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#expand", + "title": "expand", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#where", + "title": "`.where`", + "depth": 3 + }, + { + "source": "/docs/cds/cqn#having", + "title": "`.having`", + "depth": 3 + }, + { + "source": "/docs/cds/cqn#search", + "title": "`.search`", + "depth": 3 + }, + { + "source": "/docs/cds/cqn#orderby", + "title": "`.orderBy`", + "depth": 3 + }, + { + "source": "/docs/cds/cqn#order", + "title": "order", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#insert", + "title": "INSERT", + "depth": 2 + }, + { + "source": "/docs/cds/cqn#upsert", + "title": "UPSERT", + "depth": 2 + }, + { + "source": "/docs/cds/cqn#entries", + "title": "`.entries`", + "depth": 3 + }, + { + "source": "/docs/cds/cqn#values", + "title": "`.values`", + "depth": 3 + }, + { + "source": "/docs/cds/cqn#rows", + "title": "`.rows`", + "depth": 3 + }, + { + "source": "/docs/cds/cqn#update", + "title": "UPDATE", + "depth": 2 + }, + { + "source": "/docs/cds/cqn#data", + "title": "`.data`", + "depth": 3 + }, + { + "source": "/docs/cds/cqn#with", + "title": "`.with`", + "depth": 3 + }, + { + "source": "/docs/cds/cqn#changes", + "title": "changes", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#delete", + "title": "DELETE", + "depth": 2 + }, + { + "source": "/docs/cds/cqn#expressions", + "title": "Expressions", + "depth": 2 + }, + { + "source": "/docs/cds/cqn#expr", + "title": "expr", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#ref", + "title": "ref", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#val", + "title": "val", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#xpr", + "title": "xpr", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#list", + "title": "list", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#func", + "title": "func", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#param", + "title": "param", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#xo", + "title": "xo", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#name", + "title": "name", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#scalar", + "title": "scalar", + "depth": 6 + }, + { + "source": "/docs/cds/cqn#full-cqndts-file", + "title": "Full `cqn.d.ts` File", + "depth": 2 + }, + { + "source": "/docs/cds/cxl", + "title": "CDS Expression Language (CXL)", + "depth": 1 + }, + { + "source": "/docs/cds/cxl#preliminaries", + "title": "Preliminaries", + "depth": 2 + }, + { + "source": "/docs/cds/cxl#live-code", + "title": "Live Code", + "depth": 3 + }, + { + "source": "/docs/cds/cxl#trying-it-with-cds-repl", + "title": "Trying it with `cds repl`", + "depth": 3 + }, + { + "source": "/docs/cds/cxl#expressions-expr", + "title": "Expressions (`expr`)", + "depth": 2 + }, + { + "source": "/docs/cds/cxl#expr", + "title": "expr", + "depth": 6 + }, + { + "source": "/docs/cds/cxl#in-calculated-elements", + "title": "In Calculated Elements", + "depth": 3 + }, + { + "source": "/docs/cds/cxl#path-expressions-ref", + "title": "Path Expressions (`ref`)", + "depth": 2 + }, + { + "source": "/docs/cds/cxl#ref", + "title": "ref", + "depth": 6 + }, + { + "source": "/docs/cds/cxl#simple-element-reference", + "title": "Simple Element Reference", + "depth": 3 + }, + { + "source": "/docs/cds/cxl#path-navigation", + "title": "Path Navigation", + "depth": 3 + }, + { + "source": "/docs/cds/cxl#in-exists-predicates", + "title": "In `exists` Predicates", + "depth": 3 + }, + { + "source": "/docs/cds/cxl#infix-filters", + "title": "Infix Filters", + "depth": 2 + }, + { + "source": "/docs/cds/cxl#exists-infix-filter", + "title": "Applied to `exists` Predicate", + "depth": 3 + }, + { + "source": "/docs/cds/cxl#applied-to-from-clause", + "title": "Applied to `from` Clause", + "depth": 3 + }, + { + "source": "/docs/cds/cxl#in-calculated-elements-1", + "title": "In Calculated Elements", + "depth": 3 + }, + { + "source": "/docs/cds/cxl#between-path-segments", + "title": "Between Path Segments", + "depth": 3 + }, + { + "source": "/docs/cds/cxl#operators-xpr", + "title": "Operators (`xpr`)", + "depth": 2 + }, + { + "source": "/docs/cds/cxl#xpr", + "title": "xpr", + "depth": 6 + }, + { + "source": "/docs/cds/cxl#functions-func", + "title": "Functions (`func`)", + "depth": 2 + }, + { + "source": "/docs/cds/cxl#func", + "title": "func", + "depth": 6 + }, + { + "source": "/docs/cds/cxl#literals-val", + "title": "Literals (`val`)", + "depth": 2 + }, + { + "source": "/docs/cds/cxl#val", + "title": "val", + "depth": 6 + }, + { + "source": "/docs/cds/cxn", + "title": "Expression Notation (CXN)", + "depth": 1 + }, + { + "source": "/docs/cds/cxn#literal-values", + "title": "Literal Values", + "depth": 2 + }, + { + "source": "/docs/cds/cxn#references", + "title": "References", + "depth": 2 + }, + { + "source": "/docs/cds/cxn#function-calls", + "title": "Function Calls", + "depth": 2 + }, + { + "source": "/docs/cds/cxn#lists", + "title": "Lists", + "depth": 2 + }, + { + "source": "/docs/cds/cxn#operators", + "title": "Operator Expressions", + "depth": 2 + }, + { + "source": "/docs/cds/cxn#binding-parameters", + "title": "Binding Parameters", + "depth": 2 + }, + { + "source": "/docs/cds/cxn#sub-queries", + "title": "Sub Queries", + "depth": 2 + }, + { + "source": "/docs/cds/types", + "title": "Core / Built-in Types", + "depth": 1 + }, + { + "source": "/docs/cds/types#vector-embeddings", + "title": "Vector Embeddings", + "depth": 6 + }, + { + "source": "/docs/cds/common", + "title": "Common Types and Aspects", + "depth": 1 + }, + { + "source": "/docs/cds/common#why-use-sapcdscommon", + "title": "Why Use _@sap/cds/common_?", + "depth": 2 + }, + { + "source": "/docs/cds/common#outcome--optimized-best-practice", + "title": "Outcome = Optimized Best Practice", + "depth": 3 + }, + { + "source": "/docs/cds/common#common-reuse-aspects", + "title": "Common Reuse Aspects", + "depth": 2 + }, + { + "source": "/docs/cds/common#aspect-cuid", + "title": "Aspect `cuid`", + "depth": 3 + }, + { + "source": "/docs/cds/common#aspect-managed", + "title": "Aspect `managed`", + "depth": 3 + }, + { + "source": "/docs/cds/common#aspect-temporal", + "title": "Aspect `temporal`", + "depth": 3 + }, + { + "source": "/docs/cds/common#code-types", + "title": "Common Reuse Types", + "depth": 2 + }, + { + "source": "/docs/cds/common#type-country", + "title": "Type `Country`", + "depth": 3 + }, + { + "source": "/docs/cds/common#type-currency", + "title": "Type `Currency`", + "depth": 3 + }, + { + "source": "/docs/cds/common#type-language", + "title": "Type `Language`", + "depth": 3 + }, + { + "source": "/docs/cds/common#type-timezone", + "title": "Type `Timezone`", + "depth": 3 + }, + { + "source": "/docs/cds/common#code-lists", + "title": "Common Code Lists", + "depth": 2 + }, + { + "source": "/docs/cds/common#namespace-sapcommon", + "title": "Namespace: `sap.common`", + "depth": 4 + }, + { + "source": "/docs/cds/common#aspect-codelist", + "title": "Aspect `CodeList`", + "depth": 3 + }, + { + "source": "/docs/cds/common#entity-countries", + "title": "Entity `Countries`", + "depth": 3 + }, + { + "source": "/docs/cds/common#entity-currencies", + "title": "Entity `Currencies`", + "depth": 3 + }, + { + "source": "/docs/cds/common#entity-languages", + "title": "Entity `Languages`", + "depth": 3 + }, + { + "source": "/docs/cds/common#entity-timezones", + "title": "Entity `Timezones`", + "depth": 3 + }, + { + "source": "/docs/cds/common#sql-persistence", + "title": "SQL Persistence", + "depth": 3 + }, + { + "source": "/docs/cds/common#minimalistic-design", + "title": "Minimalistic Design", + "depth": 3 + }, + { + "source": "/docs/cds/common#aspects-for-localized-data", + "title": "Aspects for Localized Data", + "depth": 2 + }, + { + "source": "/docs/cds/common#namespace-sapcommon-1", + "title": "Namespace: `sap.common`", + "depth": 4 + }, + { + "source": "/docs/cds/common#texts-aspects", + "title": "Aspect `TextsAspect`", + "depth": 3 + }, + { + "source": "/docs/cds/common#locale-type", + "title": "Type `Locale`", + "depth": 3 + }, + { + "source": "/docs/cds/common#sql-persistence-1", + "title": "SQL Persistence", + "depth": 3 + }, + { + "source": "/docs/cds/common#providing-initial-data", + "title": "Providing Initial Data", + "depth": 2 + }, + { + "source": "/docs/cds/common#add-translated-texts", + "title": "Add Translated Texts", + "depth": 3 + }, + { + "source": "/docs/cds/common#using-tools-like-excel", + "title": "Using Tools like Excel", + "depth": 3 + }, + { + "source": "/docs/cds/common#prebuilt-data", + "title": "Using Prebuilt Content Package", + "depth": 3 + }, + { + "source": "/docs/cds/common#adapting-to-your-needs", + "title": "Adapting to Your Needs", + "depth": 2 + }, + { + "source": "/docs/cds/common#adding-detailed-fields-as-of-iso-3166-1", + "title": "Adding Detailed Fields as of [ISO 3166-1]", + "depth": 3 + }, + { + "source": "/docs/cds/common#protecting-certain-entries", + "title": "Protecting Certain Entries", + "depth": 3 + }, + { + "source": "/docs/cds/common#programmatic-solution", + "title": "Programmatic Solution", + "depth": 4 + }, + { + "source": "/docs/cds/common#using-different-foreign-keys", + "title": "Using Different Foreign Keys", + "depth": 3 + }, + { + "source": "/docs/cds/common#mapping-to-sap-s4hana-or-abap-table-signatures", + "title": "Mapping to SAP S/4HANA or ABAP Table Signatures", + "depth": 3 + }, + { + "source": "/docs/cds/common#adding-own-code-lists", + "title": "Adding Own Code Lists", + "depth": 2 + }, + { + "source": "/docs/cds/common#defining-a-new-code-list-entity", + "title": "Defining a New Code List Entity", + "depth": 3 + }, + { + "source": "/docs/cds/common#defining-a-new-reuse-type", + "title": "Defining a New Reuse Type", + "depth": 3 + }, + { + "source": "/docs/cds/common#using-the-new-reuse-type-and-code-list", + "title": "Using the New Reuse Type and Code List", + "depth": 3 + }, + { + "source": "/docs/cds/common#code-lists-with-validity", + "title": "Code Lists with Validity", + "depth": 2 + }, + { + "source": "/docs/cds/common#accommodating-changes", + "title": "Accommodating Changes", + "depth": 3 + }, + { + "source": "/docs/cds/common#exclude-outdated-entries-from-pick-lists-optional", + "title": "Exclude Outdated Entries from Pick Lists (Optional)", + "depth": 3 + }, + { + "source": "/docs/cds/common#1-extend-the-common-code-list-entity", + "title": "1. Extend the Common Code List Entity", + "depth": 4 + }, + { + "source": "/docs/cds/common#2-fill-validity-boundaries-in-code-lists", + "title": "2. Fill Validity Boundaries in Code Lists:", + "depth": 4 + }, + { + "source": "/docs/cds/common#3-model-pick-list-entity", + "title": "3. Model Pick List Entity", + "depth": 4 + }, + { + "source": "/docs/cds/common#4-include-pick-list-with-validity-on-the-ui", + "title": "4. Include Pick List with Validity on the UI", + "depth": 4 + }, + { + "source": "/docs/cds/annotations", + "title": "Common Annotations", + "depth": 1 + }, + { + "source": "/docs/cds/annotations#general-purpose", + "title": "General Purpose", + "depth": 2 + }, + { + "source": "/docs/cds/annotations#access-control", + "title": "Access Control", + "depth": 2 + }, + { + "source": "/docs/cds/annotations#input-validation", + "title": "Input Validation", + "depth": 2 + }, + { + "source": "/docs/cds/annotations#services--apis", + "title": "Services / APIs", + "depth": 2 + }, + { + "source": "/docs/cds/annotations#persistence", + "title": "Persistence", + "depth": 2 + }, + { + "source": "/docs/cds/annotations#odata", + "title": "OData", + "depth": 2 + }, + { + "source": "/docs/cds/compiler/messages", + "title": "Compiler Messages", + "depth": 1 + }, + { + "source": "/docs/cds/aspects", + "title": "Aspect-Oriented Modeling", + "depth": 1 + }, + { + "source": "/docs/cds/aspects#similar-to-aspect-oriented-programming", + "title": "Similar to Aspect-Oriented Programming", + "depth": 2 + }, + { + "source": "/docs/cds/aspects#separation-of-concerns", + "title": "Separation of Concerns", + "depth": 2 + }, + { + "source": "/docs/cds/aspects#all-in-one-models", + "title": "All-in-one Models", + "depth": 3 + }, + { + "source": "/docs/cds/aspects#keep-your-core-clean", + "title": "Keep Your Core Clean", + "depth": 3 + }, + { + "source": "/docs/cds/aspects#factor-out-separate-concerns", + "title": "Factor Out Separate Concerns", + "depth": 3 + }, + { + "source": "/docs/cds/aspects#common-reuse-aspects", + "title": "Common Reuse Aspects", + "depth": 2 + }, + { + "source": "/docs/cds/aspects#max-base-class-approach", + "title": "_Max Base Class_ Approach", + "depth": 3 + }, + { + "source": "/docs/cds/aspects#separate-reuse-aspects", + "title": "Separate Reuse Aspects", + "depth": 3 + }, + { + "source": "/docs/cds/aspects#adaptation-of-reused-definitions", + "title": "Adaptation of Reused Definitions", + "depth": 2 + }, + { + "source": "/docs/cds/aspects#adding--adapting-fields", + "title": "Adding / Adapting Fields", + "depth": 3 + }, + { + "source": "/docs/cds/aspects#adding-relationships", + "title": "Adding Relationships", + "depth": 3 + }, + { + "source": "/docs/cds/aspects#adding-reuse-aspects", + "title": "Adding Reuse Aspects", + "depth": 3 + }, + { + "source": "/docs/cds/aspects#customization-verticalization", + "title": "Customization, Verticalization", + "depth": 2 + }, + { + "source": "/docs/cds/aspects#adding-custom-fields", + "title": "Adding Custom Fields", + "depth": 3 + }, + { + "source": "/docs/cds/aspects#overriding-annotations", + "title": "Overriding Annotations", + "depth": 3 + }, + { + "source": "/docs/cds/aspects#verticalization", + "title": "Verticalization", + "depth": 3 + }, + { + "source": "/docs/cds/aspects#inheritance-hierarchies", + "title": "Inheritance Hierarchies", + "depth": 2 + }, + { + "source": "/docs/cds/aspects#table-per-leaf-class-strategy", + "title": "Table Per Leaf Class Strategy", + "depth": 3 + }, + { + "source": "/docs/cds/aspects#table-per-class-strategy", + "title": "Table Per Class Strategy", + "depth": 3 + }, + { + "source": "/docs/cds/aspects#single-table-strategy", + "title": "Single Table Strategy", + "depth": 3 + }, + { + "source": "/docs/cds/models", + "title": "On The Nature of Models", + "depth": 1 + }, + { + "source": "/docs/cds/models#metaphysics-of-languages", + "title": "Metaphysics of Languages", + "depth": 2 + }, + { + "source": "/docs/cds/models#languages", + "title": "Languages", + "depth": 3 + }, + { + "source": "/docs/cds/models#representations", + "title": "Representations", + "depth": 3 + }, + { + "source": "/docs/cds/models#reflections", + "title": "Reflections", + "depth": 3 + }, + { + "source": "/docs/cds/models#what-is-a-cds-model", + "title": "What is a CDS Model?", + "depth": 2 + }, + { + "source": "/docs/cds/models#in-plain-coding-at-runtime", + "title": "In Plain Coding at Runtime", + "depth": 3 + }, + { + "source": "/docs/cds/models#parsed-at-runtime", + "title": "Parsed at Runtime", + "depth": 3 + }, + { + "source": "/docs/cds/models#from-cds-source-files", + "title": "From _.cds_ Source Files", + "depth": 3 + }, + { + "source": "/docs/cds/models#from-json-files", + "title": "From _.json_ Files", + "depth": 3 + }, + { + "source": "/docs/cds/models#from-other-frontends", + "title": "From Other Frontends", + "depth": 3 + }, + { + "source": "/docs/cds/models#processing-models", + "title": "Processing Models", + "depth": 2 + }, + { + "source": "/docs/node.js/", + "title": "CAP Service SDK for Node.js", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-facade", + "title": "The *cds* Façade Object", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-facade#refs-to-submodules", + "title": "Refs to Submodules", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-facade#import-good", + "title": "**Good:**", + "depth": 5 + }, + { + "source": "/docs/node.js/cds-facade#import-bad", + "title": "**Bad:**", + "depth": 5 + }, + { + "source": "/docs/node.js/cds-facade#builtin-types--classes", + "title": "Builtin Types & Classes", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-facade#cds-builtin-types", + "title": "cds. builtin .types", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-linked-classes", + "title": "cds. linked .classes", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#core-classes", + "title": "Core Classes", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-facade#cds-service", + "title": "cds. Service", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-eventcontext", + "title": "cds. EventContext", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-event", + "title": "cds. Event", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-request", + "title": "cds. Request", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-user", + "title": "cds. User", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#properties", + "title": "Properties", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-facade#cds-version", + "title": "cds. version", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-home", + "title": "cds. home", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-root", + "title": "cds. root", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-cli", + "title": "cds. cli", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-entities", + "title": "cds. entities", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-env", + "title": "cds. env", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-requires", + "title": "cds. requires", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-services", + "title": "cds. services", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-context", + "title": "cds. context", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-model", + "title": "cds. model", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-app", + "title": "cds. app", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-db", + "title": "cds. db", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#methods", + "title": "Methods", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-facade#cds-error", + "title": "cds. error()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#cds-exit", + "title": "cds. exit()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-facade#lifecycle-events", + "title": "Lifecycle Events", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-compile", + "title": "Parsing and Compiling Models", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-compile#cds-compile-", + "title": "cds. compile (...)", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-compile#compiling-cds-files-async", + "title": "Compiling `.cds` files (async)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#single-in-memory-sources", + "title": "Single in-memory sources", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#multiple-in-memory-sources", + "title": "Multiple in-memory sources", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#additional-options", + "title": "Additional Options", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#cds-compile-to-", + "title": "cds. compile .to ...", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-compile#json", + "title": ".json()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#yaml", + "title": ".yaml()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#edm", + "title": ".edm()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#edmx", + "title": ".edmx()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#hdbtable", + "title": ".hdbtable()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#hana", + "title": ".hana() ", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#sql", + "title": ".sql()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#cdl", + "title": ".cdl()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#asyncapi", + "title": ".asyncapi()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#cds-load", + "title": "cds. load (files)", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-compile#cds-parse", + "title": "cds. parse()", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-compile#parse-cdl", + "title": "cds. parse. cdl()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#parse-cql", + "title": "cds. parse. cql()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#parse-cxl", + "title": "cds. parse. expr()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#cds-parse-xpr", + "title": "cds. parse. xpr()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#cds-parse-ref", + "title": "cds. parse. ref()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#cds-minify", + "title": "cds. minify()", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-compile#cds-resolve", + "title": "cds. resolve()", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-compile#lifecycle-events", + "title": "Lifecycle Events", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-compile#compileforruntime", + "title": "compile.for.runtime", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#compiletodbx", + "title": "compile.to.dbx", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-compile#compiletoedmx", + "title": "compile.to.edmx", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect", + "title": "Reflecting CDS Models", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-reflect#cds-linked", + "title": "cds. linked ([csn](../cds/csn))", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-reflect#linked-csn", + "title": "LinkedCSN", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-reflect#-is_linked", + "title": ". is_linked", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-definitions", + "title": ". definitions", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-services", + "title": ". services", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-entities", + "title": ". entities", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#each", + "title": "each()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#all", + "title": "all()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#find", + "title": "find()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#foreach", + "title": "foreach()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#iterable", + "title": "LinkedDefinitions", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-reflect#any", + "title": "LinkedDefinition", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-reflect#-is_linked-1", + "title": ". is_linked", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-name", + "title": ". name", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-kind", + "title": ". kind", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#instanceof", + "title": "*instanceof*", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-reflect#cds-service", + "title": "cds. service", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-reflect#-is_service", + "title": ". is_service", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-entities-1", + "title": ". entities", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-events", + "title": ". events", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-actions", + "title": ". actions", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#cds-entity", + "title": "cds. entity", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-reflect#-is_entity", + "title": ". is_entity", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-keys", + "title": ". keys", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-associations", + "title": ". associations", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-compositions", + "title": ". compositions", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-actions-1", + "title": ". actions", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-texts", + "title": ". texts", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-drafts", + "title": ". drafts", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#cds-struct", + "title": "cds. struct", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-reflect#-is_struct", + "title": ". is_struct", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-elements", + "title": ". elements", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#cds-association", + "title": "cds. Association", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-reflect#-_target", + "title": ". _target", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-isassociation", + "title": ". isAssociation", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-iscomposition", + "title": ". isComposition", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-is2one--2many", + "title": ". is2one / 2many", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-keys-1", + "title": ". keys", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#-foreignkeys", + "title": ". foreignKeys", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-reflect#cds-linked-classes", + "title": "cds. linked .classes", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-reflect#mixin", + "title": "mixin()", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-reflect#cds-builtin-types", + "title": "cds. builtin. types", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-server", + "title": "Bootstrapping Servers", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-server#cli-command-cds-serve", + "title": "CLI Command `cds serve`", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-server#built-in-serverjs", + "title": " Built-in `server.js`", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-server#cds-server", + "title": "cds. server()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-server#cds-app", + "title": "cds. app", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-server#custom-serverjs", + "title": " Custom `server.js`", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-server#plug-in-to-lifecycle-events", + "title": "Plug-in to Lifecycle Events", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-server#override-cdsserver", + "title": "Override `cds.server()`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-server#lifecycle-events", + "title": "Lifecycle Events", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-server#bootstrap", + "title": "bootstrap", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-server#loaded", + "title": "loaded", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-server#connect", + "title": "connect", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-server#serving", + "title": "serving", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-server#served", + "title": "served", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-server#listening", + "title": "listening", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-server#shutdown", + "title": "shutdown", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-server#configuration", + "title": "Configuration", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-server#cors-middleware", + "title": "CORS Middleware", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-server#toggle-generic-index-page", + "title": "Toggle Generic Index Page", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-server#maximum-request-body-size", + "title": "Maximum Request Body Size", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-server#see-also", + "title": "See Also...", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-serve", + "title": "Serving Provided Services", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-serve#cds-serve-", + "title": "cds. serve (...)", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-serve#common-usages", + "title": "Common Usages:", + "depth": 5 + }, + { + "source": "/docs/node.js/cds-serve#arguments", + "title": "Arguments:", + "depth": 5 + }, + { + "source": "/docs/node.js/cds-serve#caching", + "title": "Caching:", + "depth": 5 + }, + { + "source": "/docs/node.js/cds-serve#common-usages-and-defaults", + "title": "Common Usages and Defaults", + "depth": 5 + }, + { + "source": "/docs/node.js/cds-serve#from", + "title": ".from (model) ", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#to", + "title": ".to (protocol) ", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#at", + "title": ".at (path) ", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#in", + "title": ".in ([express app](https://expressjs.com/api.html#app)) ", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#with", + "title": ".with (impl) ", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#cds-middlewares", + "title": "cds. middlewares", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-serve#-context", + "title": ". context()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#-trace", + "title": ". trace()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#-auth", + "title": ". auth()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#-ctx_model", + "title": ". ctx_model()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#addmw-pos", + "title": ".add(mw, pos?)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#custom-middlewares", + "title": "Custom Middlewares", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#customization-of-cdscontextuser", + "title": "Customization of `cds.context.user`", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-serve#enabling-feature-flags", + "title": "Enabling Feature Flags", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-serve#current-limitations", + "title": "Current Limitations", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#cds-protocols", + "title": "cds. protocols", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-serve#protocol", + "title": "@protocol", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#path", + "title": "@path", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#patch-vs-put-vs-replace", + "title": "PATCH vs. PUT vs. Replace", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#custom-protocol-adapter", + "title": "Custom Protocol Adapter", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-serve#current-limitations-1", + "title": "Current Limitations", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-connect", + "title": "Connecting to Required Services", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-connect#connecting-to-required-services", + "title": "Connecting to Required Services", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-connect#cds-connectto-", + "title": "cds. connect.to ()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-connect#cds-connect-caching", + "title": "cds. services", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-connect#configuring-required-services", + "title": "Configuring Required Services", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-connect#cds-env-requires", + "title": "cds-env-requires", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-connect#cdsrequiressrvimpl", + "title": "cds.requires.\\`.impl`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-connect#cdsrequiressrvkind", + "title": "cds.requires.\\`.kind`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-connect#cdsrequiressrvmodel", + "title": "cds.requires.\\`.model`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-connect#cdsrequiressrvservice", + "title": "cds.requires.\\`.service`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-connect#service-bindings", + "title": "Service Bindings", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-connect#cdsrequiressrvcredentials", + "title": "cds.requires.\\.credentials", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-connect#bindings-via-cds-env", + "title": "Basic Mechanism", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-connect#bindings-in-cloud-platforms", + "title": "In Cloud Foundry", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-connect#vcap-services", + "title": "Through `VCAP_SERVICES` env var", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-connect#in-kubernetes-kyma", + "title": "In Kubernetes / Kyma", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-connect#env-service-bindings", + "title": "Through environment variables", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-connect#file-system-service-bindings", + "title": "Through the file system", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-connect#provide-service-bindings", + "title": "Provide Service Bindings (`VCAP_SERVICES`)", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-connect#through-cdsrc-privatejson-file-for-hybrid-testing", + "title": "Through _.cdsrc-private.json_ File for Hybrid Testing", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-connect#bindings-via-process-env", + "title": "Through `process.env` Variables", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-connect#in-env-files-for-local-testing", + "title": "In _.env_ Files for Local Testing", + "depth": 4 + }, + { + "source": "/docs/node.js/core-services", + "title": "Core Services", + "depth": 1 + }, + { + "source": "/docs/node.js/core-services#provided-services", + "title": "Provided Services", + "depth": 2 + }, + { + "source": "/docs/node.js/core-services#cds-modeling-provided-services", + "title": "CDS-Modeling *Provided* Services", + "depth": 4 + }, + { + "source": "/docs/node.js/core-services#serving-provided-services---cdsserve", + "title": "Serving Provided Services → `cds.serve`", + "depth": 4 + }, + { + "source": "/docs/node.js/core-services#required-services", + "title": "Required Services", + "depth": 2 + }, + { + "source": "/docs/node.js/core-services#configuring-required-services", + "title": "Configuring *Required* Services", + "depth": 4 + }, + { + "source": "/docs/node.js/core-services#connecting-to-required-services--cdsconnect", + "title": "Connecting to Required Services → `cds.connect`", + "depth": 4 + }, + { + "source": "/docs/node.js/core-services#implementing-services", + "title": "Implementing Services", + "depth": 2 + }, + { + "source": "/docs/node.js/core-services#in-sibling-js-files-next-to-cds-sources", + "title": "In sibling `.js` files, next to `.cds` sources", + "depth": 4 + }, + { + "source": "/docs/node.js/core-services#specified-by-impl-annotation-or-impl-configuration", + "title": "Specified by `@impl` Annotation, or `impl` Configuration", + "depth": 4 + }, + { + "source": "/docs/node.js/core-services#how-to-provide-custom-service-implementations", + "title": "How to provide custom service implementations?", + "depth": 4 + }, + { + "source": "/docs/node.js/core-services#consuming-services", + "title": "Consuming Services", + "depth": 2 + }, + { + "source": "/docs/node.js/core-services#cdsservice", + "title": "`cds.Service`", + "depth": 2 + }, + { + "source": "/docs/node.js/core-services#service---", + "title": "Service ( ... )", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#-name", + "title": ". name", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#-model", + "title": ". model", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#-options", + "title": ". options", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#-actions", + "title": ". actions", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#-events", + "title": ". events", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#-types", + "title": ". types", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#-entities", + "title": ". entities", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-entities", + "title": "srv-entities", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#similarity-and-difference-to-cdsentities", + "title": "Similarity _and_ difference to `cds.entities`", + "depth": 4 + }, + { + "source": "/docs/node.js/core-services#srv-init", + "title": "srv. init()", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-init-1", + "title": "srv-init", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#srv-prepend", + "title": "srv. prepend()", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-prepend-1", + "title": "srv-prepend", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#srv-on-before-after", + "title": "srv. on, before, after()", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-on-before-after-1", + "title": "srv-on-before-after", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#srv-before-request", + "title": "srv. before (request)", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-before-request-1", + "title": "srv-before-request", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#srv-after-request", + "title": "srv. after (request)", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-after-request-1", + "title": "srv-after-request", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#srv-on-request", + "title": "srv. on (request)", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-on-request-1", + "title": "srv-on-request", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#interceptor-stack-with-next", + "title": "Interceptor stack with `next()`", + "depth": 4 + }, + { + "source": "/docs/node.js/core-services#srv-on-event", + "title": "srv. on (event)", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-on-event-1", + "title": "srv-on-event", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#srv-on-error", + "title": "srv. on (error)", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-on-error-1", + "title": "srv-on-error", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#srv-send-request", + "title": "srv. send (request)", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-send-request-1", + "title": "srv-send-request", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#srv-emit-event", + "title": "srv. emit (event)", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-emit-event-1", + "title": "srv-emit-event", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#srv-run-query", + "title": "srv. run (query)", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-run-query-1", + "title": "srv-run-query", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#srv-run--fn-", + "title": "srv. run ( fn )", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-run-fn", + "title": "srv-run-fn", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#srv-dispatch-event", + "title": "srv. dispatch (event)", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-dispatch-event-1", + "title": "srv-dispatch-event", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#srv-handle-event", + "title": "srv. handle (event)", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-handle-event-1", + "title": "srv-handle-event", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#srv-foreach-entity", + "title": "srv. foreach (entity)", + "depth": 3 + }, + { + "source": "/docs/node.js/core-services#srv-foreach-entity-1", + "title": "srv-foreach-entity", + "depth": 6 + }, + { + "source": "/docs/node.js/core-services#rest-style-api", + "title": "REST-style API", + "depth": 2 + }, + { + "source": "/docs/node.js/core-services#crud-style-api", + "title": "CRUD-style API", + "depth": 2 + }, + { + "source": "/docs/node.js/app-services", + "title": "Application Services", + "depth": 1 + }, + { + "source": "/docs/node.js/app-services#class-cdsapplicationservice", + "title": "Class `cds.ApplicationService`", + "depth": 2 + }, + { + "source": "/docs/node.js/app-services#generic-handlers-in-srvinit", + "title": "Generic Handlers in `srv.init()`", + "depth": 3 + }, + { + "source": "/docs/node.js/app-services#static-handle_authorization", + "title": "_static_ handle_authorization()", + "depth": 3 + }, + { + "source": "/docs/node.js/app-services#static-handle_etags", + "title": "_static_ handle_etags()", + "depth": 3 + }, + { + "source": "/docs/node.js/app-services#static-handle_validations", + "title": "_static_ handle_validations()", + "depth": 3 + }, + { + "source": "/docs/node.js/app-services#static-handle_temporal_data", + "title": "_static_ handle_temporal_data()", + "depth": 3 + }, + { + "source": "/docs/node.js/app-services#static-handle_localized_data", + "title": "_static_ handle_localized_data()", + "depth": 3 + }, + { + "source": "/docs/node.js/app-services#static-handle_managed_data", + "title": "_static_ handle_managed_data()", + "depth": 3 + }, + { + "source": "/docs/node.js/app-services#static-handle_paging", + "title": "_static_ handle_paging()", + "depth": 3 + }, + { + "source": "/docs/node.js/app-services#static-handle_fiori", + "title": "_static_ handle_fiori()", + "depth": 3 + }, + { + "source": "/docs/node.js/app-services#static-handle_crud", + "title": "_static_ handle_crud()", + "depth": 3 + }, + { + "source": "/docs/node.js/app-services#overriding-generic-handlers", + "title": "Overriding Generic Handlers", + "depth": 2 + }, + { + "source": "/docs/node.js/app-services#adding-generic-handlers", + "title": "Adding Generic Handlers", + "depth": 2 + }, + { + "source": "/docs/node.js/app-services#results-of-generic-crud-handlers", + "title": "Results of Generic CRUD Handlers", + "depth": 2 + }, + { + "source": "/docs/node.js/remote-services", + "title": "Remote Services ", + "depth": 1 + }, + { + "source": "/docs/node.js/remote-services#cds-remote-service", + "title": "cds.**RemoteService** class ", + "depth": 2 + }, + { + "source": "/docs/node.js/remote-services#class-cdsremoteservice---extends-cdsservice", + "title": "class cds.**RemoteService** extends cds.Service ", + "depth": 3 + }, + { + "source": "/docs/node.js/remote-services#remoteservice-configuration", + "title": "cds.RemoteService — Configuration", + "depth": 2 + }, + { + "source": "/docs/node.js/remote-services#csrf-token-handling", + "title": "CSRF-Token Handling", + "depth": 3 + }, + { + "source": "/docs/node.js/remote-services#basic-configuration", + "title": "Basic Configuration", + "depth": 4 + }, + { + "source": "/docs/node.js/remote-services#advanced-configuration", + "title": "Advanced Configuration", + "depth": 4 + }, + { + "source": "/docs/node.js/remote-services#timeout-handling", + "title": "Timeout Handling", + "depth": 3 + }, + { + "source": "/docs/node.js/remote-services#configuration-option", + "title": "Configuration Option", + "depth": 4 + }, + { + "source": "/docs/node.js/remote-services#more-to-come", + "title": " More to Come ", + "depth": 2 + }, + { + "source": "/docs/node.js/messaging", + "title": "Messaging", + "depth": 1 + }, + { + "source": "/docs/node.js/messaging#overview", + "title": "Overview", + "depth": 2 + }, + { + "source": "/docs/node.js/messaging#summary-table", + "title": "Summary Table", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#cdsmessagingservice----class", + "title": "cds.**MessagingService** class ", + "depth": 2 + }, + { + "source": "/docs/node.js/messaging#class-cdsmessagingservice----extends-cdsservice", + "title": "class cds.**MessagingService** extends cds.Service ", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#declaring-events", + "title": "Declaring Events", + "depth": 2 + }, + { + "source": "/docs/node.js/messaging#custom-topics-with-declared-events", + "title": "Custom Topics with Declared Events", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#cloudevents-protocol", + "title": "CloudEvents Protocol", + "depth": 2 + }, + { + "source": "/docs/node.js/messaging#topic-prefixes", + "title": "Topic Prefixes", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#topic-manipulations", + "title": "Topic Manipulations", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#sap-event-mesh", + "title": "SAP Event Mesh", + "depth": 4 + }, + { + "source": "/docs/node.js/messaging#emitting-events", + "title": "Emitting Events", + "depth": 2 + }, + { + "source": "/docs/node.js/messaging#receiving-events", + "title": "Receiving Events", + "depth": 2 + }, + { + "source": "/docs/node.js/messaging#inbox", + "title": "Inbox ", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#message-brokers", + "title": "Message Brokers", + "depth": 2 + }, + { + "source": "/docs/node.js/messaging#configuring-message-brokers", + "title": "Configuring Message Brokers", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#event-mesh-shared", + "title": "SAP Event Mesh (Shared)", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#sap-event-mesh-1", + "title": "SAP Event Mesh", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#integration-suite-event-mesh", + "title": "Event Mesh in SAP Integration Suite ", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#event-mesh", + "title": "`event-mesh`", + "depth": 4 + }, + { + "source": "/docs/node.js/messaging#event-mesh-shared-1", + "title": "`event-mesh-shared`", + "depth": 4 + }, + { + "source": "/docs/node.js/messaging#advanced-event-mesh", + "title": "SAP Integration Suite, Advanced Event Mesh ", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#event-broker", + "title": "SAP Cloud Application Event Hub", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#redis-pubsub", + "title": "Redis PubSub ", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#file-based", + "title": "File Based", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#local-messaging", + "title": "Local Messaging", + "depth": 3 + }, + { + "source": "/docs/node.js/messaging#composite-messaging", + "title": "Composite-Messaging", + "depth": 3 + }, + { + "source": "/docs/node.js/databases", + "title": "Database Services", + "depth": 1 + }, + { + "source": "/docs/node.js/databases#cds-db-service", + "title": "cds.**DatabaseService** class ", + "depth": 2 + }, + { + "source": "/docs/node.js/databases#class-cdsdatabaseservice----extends-cdsservice", + "title": "class cds.**DatabaseService** extends cds.Service ", + "depth": 3 + }, + { + "source": "/docs/node.js/databases#db-begin", + "title": "srv.begin () → this ", + "depth": 3 + }, + { + "source": "/docs/node.js/databases#databaseservice-consumption", + "title": "cds.DatabaseService — Consumption", + "depth": 2 + }, + { + "source": "/docs/node.js/databases#databaseservice-consumption-1", + "title": "databaseservice-consumption", + "depth": 6 + }, + { + "source": "/docs/node.js/databases#insertresult-beta", + "title": "`InsertResult` (Beta)", + "depth": 3 + }, + { + "source": "/docs/node.js/databases#databaseservice-configuration", + "title": "cds.DatabaseService — Configuration", + "depth": 2 + }, + { + "source": "/docs/node.js/databases#databaseservice-configuration-1", + "title": "databaseservice-configuration", + "depth": 6 + }, + { + "source": "/docs/node.js/databases#pool", + "title": "Pool", + "depth": 3 + }, + { + "source": "/docs/node.js/databases#databaseservice-upsert", + "title": "cds.DatabaseService — UPSERT", + "depth": 2 + }, + { + "source": "/docs/node.js/databases#databaseservice-upsert-1", + "title": "databaseservice-upsert", + "depth": 6 + }, + { + "source": "/docs/node.js/databases#more-to-come", + "title": " More to Come ", + "depth": 2 + }, + { + "source": "/docs/node.js/events", + "title": "Events and Requests", + "depth": 1 + }, + { + "source": "/docs/node.js/events#cds-context", + "title": "cds. context", + "depth": 2 + }, + { + "source": "/docs/node.js/events#cds-event-context", + "title": "`cds.EventContext`", + "depth": 2 + }, + { + "source": "/docs/node.js/events#-http", + "title": ". http", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-id", + "title": ". id", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-locale", + "title": ". locale", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-tenant", + "title": ". tenant", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-timestamp", + "title": ". timestamp", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-user", + "title": ". user", + "depth": 3 + }, + { + "source": "/docs/node.js/events#cds-event", + "title": "`cds.Event`", + "depth": 2 + }, + { + "source": "/docs/node.js/events#-event", + "title": ". event", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-data", + "title": ". data", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-headers", + "title": ". headers", + "depth": 3 + }, + { + "source": "/docs/node.js/events#eve-before-commit", + "title": "eve. before 'commit'", + "depth": 3 + }, + { + "source": "/docs/node.js/events#eve-on-succeeded", + "title": "eve. on 'succeeded'", + "depth": 3 + }, + { + "source": "/docs/node.js/events#eve-on-failed", + "title": "eve. on 'failed'", + "depth": 3 + }, + { + "source": "/docs/node.js/events#eve-on-done", + "title": "eve. on 'done'", + "depth": 3 + }, + { + "source": "/docs/node.js/events#cds-request", + "title": "`cds.Request`", + "depth": 2 + }, + { + "source": "/docs/node.js/events#-req", + "title": ". req", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-res", + "title": ". res", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-method", + "title": ". method", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-target", + "title": ". target", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-path", + "title": ". path", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-entity", + "title": ". entity", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-params", + "title": ". params", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-query", + "title": ". query", + "depth": 3 + }, + { + "source": "/docs/node.js/events#-subject", + "title": ". subject", + "depth": 3 + }, + { + "source": "/docs/node.js/events#req-reply-results", + "title": "req. reply (results)", + "depth": 3 + }, + { + "source": "/docs/node.js/events#req-reject", + "title": "req. reject ({ ... })", + "depth": 3 + }, + { + "source": "/docs/node.js/events#req-reject---", + "title": "req. reject ( ... )", + "depth": 3 + }, + { + "source": "/docs/node.js/events#req-error", + "title": "req. error()", + "depth": 3 + }, + { + "source": "/docs/node.js/events#req-warn", + "title": "req. warn()", + "depth": 3 + }, + { + "source": "/docs/node.js/events#req-info", + "title": "req. info()", + "depth": 3 + }, + { + "source": "/docs/node.js/events#req-notify", + "title": "req. notify()", + "depth": 3 + }, + { + "source": "/docs/node.js/events#error-responses", + "title": "Error Responses", + "depth": 2 + }, + { + "source": "/docs/node.js/events#translations-for-validation-errors", + "title": "Translations for Validation Errors", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql", + "title": "Querying in JavaScript", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-ql#constructing-queries", + "title": "Constructing Queries", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql#api-facades", + "title": "API Facades", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-ql#using-reflected-definitions", + "title": "Using Reflected Definitions", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-ql#not-locked-in-to-sql", + "title": " Not Locked in to SQL", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-ql#executing-queries", + "title": "Executing Queries", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql#first-class-objects", + "title": "First-Class Objects", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql#leveraging-late-materialization", + "title": " Leveraging Late Materialization", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-ql#avoiding-sql-injection", + "title": "Avoiding SQL Injection", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql#using-cds-repl", + "title": "Using `cds repl`", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql#cdsql", + "title": "cds.ql()", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql#cdsqlclone", + "title": "cds.ql.clone()", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql#cds-ql-clone", + "title": "cds-ql-clone", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#class-cds-ql-query", + "title": "cds.ql. Query", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql#class-cds-ql-query-1", + "title": "class-cds-ql-query", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#kind", + "title": ".kind", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#then", + "title": "then()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#bind-srv", + "title": "bind (srv)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#select", + "title": "SELECT", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql#one", + "title": ".one", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#select-one", + "title": "select-one", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#elements", + "title": ".elements", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#select-elements", + "title": "select-elements", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#distinct", + "title": ".distinct", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#select-distinct", + "title": "select-distinct", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#columns", + "title": "columns()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#select-columns", + "title": "select-columns", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#select-from", + "title": "from()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#select-from-1", + "title": "select-from", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#alias", + "title": "alias()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#where", + "title": "where()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#select-where", + "title": "select-where", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#having", + "title": "having()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#select-having", + "title": "select-having", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#groupby", + "title": "groupBy()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#select-group-by", + "title": "select-group-by", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#orderby", + "title": "orderBy()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#select-order-by", + "title": "select-order-by", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#limit", + "title": "limit()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#select-limit", + "title": "select-limit", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#forupdate", + "title": "forUpdate()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#select-for-update", + "title": "select-for-update", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#forsharelock", + "title": "forShareLock()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#hints", + "title": "hints()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#pipeline", + "title": "pipeline()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#stream", + "title": "stream()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#foreach", + "title": "foreach()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#insert", + "title": "INSERT", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql#insert-1", + "title": "insert", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#into", + "title": "into()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#insert-into", + "title": "insert-into", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#insert-entries", + "title": "entries()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#insert-entries-1", + "title": "insert-entries", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#values", + "title": "values()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#rows", + "title": "rows()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#from", + "title": "from()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#upsert", + "title": "UPSERT", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql#upsert-1", + "title": "upsert", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#into-1", + "title": "into()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#entries", + "title": "entries()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#upsert-entries", + "title": "upsert-entries", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#update", + "title": "UPDATE", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql#update-1", + "title": "update", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#entity", + "title": "entity()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#set", + "title": "set()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#with", + "title": "with()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#where-1", + "title": "where()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#delete", + "title": "DELETE", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql#delete-1", + "title": "delete", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#delete-from", + "title": "from()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#where-2", + "title": "where()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#expressions", + "title": "Expressions", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-ql#expr", + "title": "expr()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#expr-1", + "title": "expr", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#ref", + "title": "ref()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#ref-1", + "title": "ref", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#val", + "title": "val()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#val-1", + "title": "val", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#xpr", + "title": "xpr()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#xpr-1", + "title": "xpr", + "depth": 6 + }, + { + "source": "/docs/node.js/cds-ql#list", + "title": "list()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#func", + "title": "func()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#predicate", + "title": "predicate()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#columns-1", + "title": "columns()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#nested", + "title": "nested()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#expand", + "title": "expand()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#inline", + "title": "inline()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#where-3", + "title": "where()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#orderby-1", + "title": "orderBy()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-ql#orders", + "title": "orders()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log", + "title": "Minimalistic Logging Facade", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-log#cds-log", + "title": "cds.log (id?, options?) ", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-log#arguments", + "title": "*Arguments*", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-log#logger-id", + "title": "*Logger `id` — cached & shared loggers*", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log#logger-label", + "title": "*Logger `label` — used to prefix log output*", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log#logger-api", + "title": "_Logger usage → much like `console`_", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log#recommendations", + "title": "*Recommendations*", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log#cds-log-format", + "title": "cds.log.format", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-log#setting-formats-for-new-loggers", + "title": "_Setting Formats for New Loggers_", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log#setting-formats-for-existing-loggers", + "title": "_Setting Formats for Existing Loggers_", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log#log-levels", + "title": "cds.log.levels", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-log#configuring-log-levels", + "title": "*Configuring Log Levels*", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log#programmatically-set-log-levels", + "title": "*Programmatically Set Log Levels*", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log#log-levels-as-used-by-the-cap-nodejs-runtime", + "title": "*Log Levels as Used by the CAP Node.js Runtime*", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log#cdsloglogger", + "title": "cds.log.Logger", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-log#arguments-1", + "title": "*Arguments*", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-log#winston", + "title": "*Using `winston` Loggers*", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log#custom-loggers", + "title": "_Custom Loggers_", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log#debug-env-variable", + "title": "`DEBUG` env variable", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-log#matching-multiple-values-of-debug", + "title": "*Matching multiple values of `DEBUG`*", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log#configuration", + "title": "Configuration", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-log#cds-log-modules", + "title": "Common IDs", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-log#logging-in-development", + "title": "Logging in Development", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-log#logging-in-production", + "title": "Logging in Production", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-log#header-masking", + "title": "Header Masking", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log#custom-fields", + "title": "Custom Fields", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-log#node-observability-correlation", + "title": "Request Correlation", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-i18n", + "title": "Localization / i18n", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-i18n#introduction", + "title": "Introduction", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-i18n#localized-fiori-uis", + "title": "Localized (Fiori) UIs", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#localized-messages", + "title": "Localized Messages", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#direct-usage", + "title": "Direct Usage", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#cdsi18n", + "title": "`cds.i18n`", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-i18n#file", + "title": "`.file`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#folders", + "title": "`.folders`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#messages", + "title": "`.messages`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#labels", + "title": "`.labels`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#bundle4", + "title": "`bundle4()`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#i18nbundle", + "title": "`I18nBundle`", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-i18n#constructor", + "title": "`constructor`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#defaults", + "title": "`.defaults`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#fallback", + "title": "`.fallback`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#files", + "title": "`.files`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#at-key-", + "title": "`at (key, ...)`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#for-key-", + "title": "`for (key, ...)`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#using-default-locales", + "title": "Using Default Locales", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-i18n#using-message-templates", + "title": "Using Message Templates", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-i18n#looking-up-labels-for-csn-definitions", + "title": "Looking up labels for CSN definitions", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-i18n#key4-csn", + "title": "`key4 (csn)`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#texts4-locale", + "title": "`texts4 (locale)`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#translations4-locales", + "title": "`translations4 (locales)`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#i18nfiles", + "title": "`I18nFiles`", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-i18n#constructor-1", + "title": "`constructor`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#-file--basename", + "title": "– `file` / `basename`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#-model", + "title": "– `model`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#-roots", + "title": "– `roots`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#-leafs", + "title": "– `leafs`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#-folders", + "title": "– `folders`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#locales", + "title": "`locales()`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#fetching-i18n-folders", + "title": "Fetching i18n Folders...", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-i18n#from-models-neighborhood", + "title": "From Models' Neighborhood", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#1-starting-from-the-current-models-sources", + "title": "1. Starting from the current model's `$sources`", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-i18n#2-get-distinct-source-directories", + "title": "2. Get distinct source directories", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-i18n#3-check-for-existing--matching-i18nfolders", + "title": "3. Check for existing & matching `i18n.folders`", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-i18n#4-result-i18n-folders-used-by-bundle", + "title": "4. Result: i18n folders used by bundle", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-i18n#from-static-project-folders", + "title": "From Static Project Folders", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#from-absolute-folders", + "title": "From Absolute Folders", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-i18n#config", + "title": "Configuration Options", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-i18n#messages-texts", + "title": "Messages Texts", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-env", + "title": "Project-Specific Configurations", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-env#cli", + "title": "CLI `cds env` Command", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-env#cds-env", + "title": "The `cds.env` Module", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-env#sources-for-cdsenv", + "title": "Sources for `cds.env`", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-env#programmatic-settings", + "title": "Programmatic Settings", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-env#defaults", + "title": "Global Defaults", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-env#built-in-to-sapcds", + "title": "Built-In to `@sap/cds`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-env#user-specific-defaults-in-cdsrcjson", + "title": "User-Specific Defaults in _~/.cdsrc.json_", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-env#project-settings", + "title": "Project Configuration", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-env#in-packagejson", + "title": "In _./package.json_", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-env#in-cdsrcjson", + "title": "In _./.cdsrc.json_", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-env#private-project-settings", + "title": "Private Project Settings", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-env#in-cdsrc-privatejson", + "title": "In _./.cdsrc-private.json_", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-env#process-env", + "title": "Process Environment", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-env#on-the-command-line", + "title": "On the Command Line", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-env#in-default-envjson", + "title": "In _./default-env.json_", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-env#in-env", + "title": "In `./.env`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-env#env-cds-config", + "title": "`CDS_CONFIG` env variable", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-env#services", + "title": "Required Services", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-env#in-cdsrequiresservice-settings", + "title": "In `cds.requires.` Settings", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-env#prototype-chained-along-kind-references", + "title": "Prototype-Chained Along `.kind` References", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-env#profiles", + "title": "Configuration Profiles", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-env#app-specific-settings", + "title": "App-Specific Settings", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-utils", + "title": "Common Utility Functions", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-utils#module-cdsutils", + "title": "Module `cds.utils`", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-utils#uuid", + "title": "uuid()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#decodeuri-uri", + "title": "decodeURI (*uri*)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#decodeuricomponent-uri", + "title": "decodeURIComponent (*uri*)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#local-filename", + "title": "local (*filename*)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#exists-file", + "title": "exists (*file*)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#isdir-file", + "title": "isdir (*file*)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#isfile-file", + "title": "isfile (*file*)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#async-read-file", + "title": "async read (*file*)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#async-write-data-to-file", + "title": "async write (*data*) .to (...*file*)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#async-copy-src-to-dst", + "title": "async copy (*src*) .to (...*dst*)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#async-mkdirp-path", + "title": "async mkdirp (...*path*)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#async-rmdir-path", + "title": "async rmdir (...*path*)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#async-rimraf-path", + "title": "async rimraf (...*path*)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#async-rm-path", + "title": "async rm (...*path*)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#colors", + "title": "colors", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-utils#shortcuts-to-nodejs-modules", + "title": "Shortcuts to Node.js Modules", + "depth": 2 + }, + { + "source": "/docs/node.js/event-queues", + "title": "Event Queues in Node.js", + "depth": 1 + }, + { + "source": "/docs/node.js/event-queues#programmatic-api", + "title": "Programmatic API", + "depth": 2 + }, + { + "source": "/docs/node.js/event-queues#queueing-a-service", + "title": "Queueing a Service", + "depth": 3 + }, + { + "source": "/docs/node.js/event-queues#cdsqueuedsrv", + "title": "`cds.queued(srv)`", + "depth": 4 + }, + { + "source": "/docs/node.js/event-queues#cdsunqueuedsrv", + "title": "`cds.unqueued(srv)`", + "depth": 4 + }, + { + "source": "/docs/node.js/event-queues#queueing-through-configuration", + "title": "Queueing through Configuration", + "depth": 4 + }, + { + "source": "/docs/node.js/event-queues#scheduling", + "title": "Scheduling", + "depth": 3 + }, + { + "source": "/docs/node.js/event-queues#callbacks", + "title": "Callbacks ", + "depth": 3 + }, + { + "source": "/docs/node.js/event-queues#manual-processing", + "title": "Manual Processing", + "depth": 3 + }, + { + "source": "/docs/node.js/event-queues#configuration", + "title": "Configuration", + "depth": 2 + }, + { + "source": "/docs/node.js/event-queues#disabling-the-queue", + "title": "Disabling the Queue", + "depth": 3 + }, + { + "source": "/docs/node.js/event-queues#troubleshooting", + "title": "Troubleshooting", + "depth": 2 + }, + { + "source": "/docs/node.js/event-queues#inspecting-cdsoutboxmessages", + "title": "Inspecting `cds.outbox.Messages`", + "depth": 3 + }, + { + "source": "/docs/node.js/event-queues#deleting-entries", + "title": "Deleting Entries", + "depth": 3 + }, + { + "source": "/docs/node.js/event-queues#messages-table-not-found", + "title": "Messages Table Not Found", + "depth": 3 + }, + { + "source": "/docs/node.js/fiori", + "title": "Fiori Support", + "depth": 1 + }, + { + "source": "/docs/node.js/fiori#draft-support", + "title": "Draft Entities", + "depth": 2 + }, + { + "source": "/docs/node.js/fiori#draft-specific-events", + "title": "Draft-specific Events", + "depth": 2 + }, + { + "source": "/docs/node.js/fiori#new", + "title": "`NEW`", + "depth": 3 + }, + { + "source": "/docs/node.js/fiori#edit", + "title": "`EDIT`", + "depth": 3 + }, + { + "source": "/docs/node.js/fiori#patch", + "title": "`PATCH`", + "depth": 3 + }, + { + "source": "/docs/node.js/fiori#save", + "title": "`SAVE`", + "depth": 3 + }, + { + "source": "/docs/node.js/fiori#discard", + "title": "`DISCARD`", + "depth": 3 + }, + { + "source": "/docs/node.js/fiori#custom-actions", + "title": "Custom Actions", + "depth": 3 + }, + { + "source": "/docs/node.js/fiori#draft-locks", + "title": "Draft Locks", + "depth": 2 + }, + { + "source": "/docs/node.js/fiori#draft-timeouts", + "title": "Draft Timeouts", + "depth": 2 + }, + { + "source": "/docs/node.js/fiori#programmatic-apis", + "title": "Programmatic APIs ", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-tx", + "title": "Transaction Management", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-tx#automatic-transactions", + "title": "Automatic Transactions", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-tx#nested-transactions", + "title": "Nested Transactions", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-tx#manual-transactions", + "title": "Manual Transactions", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-tx#background-jobs", + "title": "Background Jobs", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-tx#event-contexts", + "title": "cds. context", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-tx#accessing-context", + "title": "Accessing Context", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-tx#setting-contexts", + "title": "Setting Contexts", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-tx#continuation-local-variable", + "title": "Continuation-local Variable", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-tx#context-propagation", + "title": "Context Propagation", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-tx#srv-tx", + "title": "cds/srv. tx()", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-tx#srvtx----context-fn--txsrv", + "title": "srv.tx (context?, fn?) → tx\\ ", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-tx#srv-tx-ctx", + "title": "srv.tx ({ tenant?, user?, ... }) → tx\\ ", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-tx#srv-tx-fn", + "title": "srv.tx ((tx)=>{...}) → tx\\ ", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-tx#srv-tx-context", + "title": "srv.tx (ctx) → tx\\ ", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-tx#tx-context", + "title": "_↳_ tx.context → [cds.EventContext](events#cds-event-context) ", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-tx#commit", + "title": "_↳_ tx.commit (res?) ⇢ res ", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-tx#rollback", + "title": " _↳_ tx.rollback (err?) ⇢ err ", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-tx#cds-spawn", + "title": "cds.spawn()", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-tx#deprecated-apis", + "title": "DEPRECATED APIs", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-tx#srv-tx-req", + "title": "srv.tx (req) → tx\\ ", + "depth": 4 + }, + { + "source": "/docs/node.js/authentication", + "title": "Authentication", + "depth": 1 + }, + { + "source": "/docs/node.js/authentication#cds-user", + "title": "cds. User", + "depth": 2 + }, + { + "source": "/docs/node.js/authentication#user-is", + "title": ".is (\\) ", + "depth": 3 + }, + { + "source": "/docs/node.js/authentication#user-id", + "title": ". id", + "depth": 3 + }, + { + "source": "/docs/node.js/authentication#user-attr", + "title": ". attr", + "depth": 3 + }, + { + "source": "/docs/node.js/authentication#user-auth-info", + "title": ". authInfo?", + "depth": 3 + }, + { + "source": "/docs/node.js/authentication#privileged-user", + "title": "cds.**User.Privileged**", + "depth": 2 + }, + { + "source": "/docs/node.js/authentication#anonymous-user", + "title": "cds.**User.Anonymous**", + "depth": 2 + }, + { + "source": "/docs/node.js/authentication#default-user", + "title": "cds.**User.default**", + "depth": 2 + }, + { + "source": "/docs/node.js/authentication#enforcement", + "title": "Authorization Enforcement", + "depth": 2 + }, + { + "source": "/docs/node.js/authentication#strategies", + "title": "Authentication Strategies", + "depth": 2 + }, + { + "source": "/docs/node.js/authentication#dummy", + "title": "Dummy Authentication", + "depth": 3 + }, + { + "source": "/docs/node.js/authentication#mocked", + "title": "Mocked Authentication", + "depth": 3 + }, + { + "source": "/docs/node.js/authentication#mock-users", + "title": "Pre-defined Mock Users", + "depth": 4 + }, + { + "source": "/docs/node.js/authentication#jwt", + "title": "JWT-based Authentication", + "depth": 3 + }, + { + "source": "/docs/node.js/authentication#xsuaa", + "title": "XSUAA-based Authentication", + "depth": 3 + }, + { + "source": "/docs/node.js/authentication#ias", + "title": "IAS-based Authentication", + "depth": 3 + }, + { + "source": "/docs/node.js/authentication#token-validation", + "title": "Token Validation", + "depth": 4 + }, + { + "source": "/docs/node.js/authentication#xsuaa-fallback", + "title": "XSUAA Fallback", + "depth": 4 + }, + { + "source": "/docs/node.js/authentication#custom", + "title": "Custom Authentication", + "depth": 3 + }, + { + "source": "/docs/node.js/authentication#authentication-in-production", + "title": "Authentication in Production", + "depth": 2 + }, + { + "source": "/docs/node.js/authentication#enforced-by-default", + "title": "Enforced by Default", + "depth": 3 + }, + { + "source": "/docs/node.js/authentication#cached-by-default", + "title": "Cached by Default", + "depth": 3 + }, + { + "source": "/docs/node.js/authentication#hybrid-setup", + "title": "Authentication in Hybrid Setup", + "depth": 2 + }, + { + "source": "/docs/node.js/authentication#with-xsuaa", + "title": "with XSUAA", + "depth": 3 + }, + { + "source": "/docs/node.js/authentication#configure-the-application", + "title": "Configure the Application", + "depth": 4 + }, + { + "source": "/docs/node.js/authentication#auth-in-cockpit", + "title": "Set Up the Roles for the Application", + "depth": 4 + }, + { + "source": "/docs/node.js/authentication#running-app-router", + "title": "Running App Router", + "depth": 4 + }, + { + "source": "/docs/node.js/authentication#with-ias", + "title": "with IAS", + "depth": 3 + }, + { + "source": "/docs/node.js/authentication#configure-the-application-1", + "title": "Configure the Application", + "depth": 4 + }, + { + "source": "/docs/node.js/authentication#deploy-the-application", + "title": "Deploy the Application", + "depth": 4 + }, + { + "source": "/docs/node.js/authentication#assign-policies-in-the-administrative-console", + "title": "Assign Policies in the Administrative Console", + "depth": 4 + }, + { + "source": "/docs/node.js/authentication#start-hybrid-testing", + "title": "Start Hybrid Testing", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-plugins", + "title": "CDS Plugin Packages", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-plugins#add-a-cds-pluginjs", + "title": "Add a `cds-plugin.js`", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-plugins#auto-configuration", + "title": "Auto-Configuration", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-plugins#cds-plugins", + "title": "cds. plugins", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-plugins#configuration-schema", + "title": "Configuration Schema ", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-plugins#declaration-in-plugin", + "title": "Declaration in Plugin", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-plugins#usage-in-a-cap-project", + "title": "Usage In a CAP Project", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-test", + "title": "Testing with `cds.test`", + "depth": 1 + }, + { + "source": "/docs/node.js/cds-test#getting-started", + "title": "Getting Started", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-test#project-setup", + "title": "Project Setup", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#writing-tests", + "title": "Writing Tests", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#testing-services", + "title": "Testing Services", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#running-tests", + "title": "Running Tests", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#testing-with-cloud-services", + "title": "Testing with Cloud Services", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-test#dos-and-donts", + "title": "Dos and Don'ts", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#class-cdstesttest", + "title": "Class `cds.test.Test`", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-test#cdstest", + "title": "cds.test()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#defaults", + "title": ".defaults", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#expect", + "title": ".expect", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#http-bound", + "title": "GET / PUT / POST ...", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#authentication", + "title": "Authentication", + "depth": 4 + }, + { + "source": "/docs/node.js/cds-test#http-methods", + "title": "test. get/put/post/...()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#test-data-reset", + "title": "test .data .reset()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#test-log", + "title": "test. log()", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#test-run-", + "title": "test. run (...)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#test-in-folder-", + "title": "test. in (folder, ...)", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#cds_test_env_check", + "title": "`CDS_TEST_ENV_CHECK`", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#deprecated-apis", + "title": "Deprecated APIs", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-test#expect-in-jest", + "title": ".expect in Jest", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#axios", + "title": ".axios", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#chai", + "title": ".chai", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#assert", + "title": ".assert", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#should", + "title": ".should", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#best-practices", + "title": "Best Practices", + "depth": 2 + }, + { + "source": "/docs/node.js/cds-test#minimal-assumptions", + "title": "Minimal Assumptions", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#dont-test-snapshots", + "title": "Don't Test Snapshots", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#dont-obscure-errors", + "title": "Don't Obscure Errors", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#runner-agnostic-tests", + "title": "Runner-Agnostic Tests", + "depth": 3 + }, + { + "source": "/docs/node.js/cds-test#using-cdstest-in-repl", + "title": "Using `cds.test` in REPL", + "depth": 2 + }, + { + "source": "/docs/node.js/streaming", + "title": "Streaming", + "depth": 1 + }, + { + "source": "/docs/node.js/streaming#overview", + "title": "Overview", + "depth": 2 + }, + { + "source": "/docs/node.js/streaming#why", + "title": "Why", + "depth": 2 + }, + { + "source": "/docs/node.js/streaming#how", + "title": "How", + "depth": 2 + }, + { + "source": "/docs/node.js/streaming#object-mode", + "title": "Object Mode", + "depth": 3 + }, + { + "source": "/docs/node.js/streaming#pipeline", + "title": "Pipeline", + "depth": 3 + }, + { + "source": "/docs/node.js/streaming#performance", + "title": "Performance", + "depth": 3 + }, + { + "source": "/docs/node.js/streaming#errors", + "title": "Errors", + "depth": 3 + }, + { + "source": "/docs/node.js/typescript", + "title": "Using TypeScript", + "depth": 1 + }, + { + "source": "/docs/node.js/typescript#enable-typescript-support", + "title": "Enable TypeScript Support", + "depth": 2 + }, + { + "source": "/docs/node.js/typescript#writing-typescript-files", + "title": "Writing TypeScript Files", + "depth": 2 + }, + { + "source": "/docs/node.js/typescript#samples", + "title": "Samples", + "depth": 2 + }, + { + "source": "/docs/node.js/typescript#developing-typescript-projects", + "title": "Developing TypeScript Projects", + "depth": 2 + }, + { + "source": "/docs/node.js/typescript#cds-watch", + "title": "Using `cds watch` ", + "depth": 3 + }, + { + "source": "/docs/node.js/typescript#cds-tsx", + "title": "Using `cds-tsx` ", + "depth": 3 + }, + { + "source": "/docs/node.js/typescript#cds-ts", + "title": "Using `cds-ts`", + "depth": 3 + }, + { + "source": "/docs/node.js/typescript#testing-with-ts-jest", + "title": "Testing with `ts-jest`", + "depth": 2 + }, + { + "source": "/docs/node.js/typescript#building-typescript-projects", + "title": "Building TypeScript Projects", + "depth": 2 + }, + { + "source": "/docs/node.js/typescript#running-built-projects-locally", + "title": "Running Built Projects Locally", + "depth": 2 + }, + { + "source": "/docs/node.js/typescript#typescript-apis-in-sapcds", + "title": "TypeScript APIs in `@sap/cds` ", + "depth": 2 + }, + { + "source": "/docs/node.js/typescript#type-imports", + "title": "Type Imports", + "depth": 3 + }, + { + "source": "/docs/node.js/typescript#good", + "title": "**Good:**", + "depth": 5 + }, + { + "source": "/docs/node.js/typescript#bad", + "title": "**Bad:**", + "depth": 5 + }, + { + "source": "/docs/node.js/typescript#community", + "title": "Community", + "depth": 3 + }, + { + "source": "/docs/node.js/typescript#help-us-improve-the-types", + "title": "Help us improve the types", + "depth": 4 + }, + { + "source": "/docs/node.js/typescript#generating-model-types-automatically", + "title": "Generating Model Types Automatically", + "depth": 2 + }, + { + "source": "/docs/node.js/best-practices", + "title": "Best Practices", + "depth": 1 + }, + { + "source": "/docs/node.js/best-practices#dependencies", + "title": "Managing Dependencies", + "depth": 2 + }, + { + "source": "/docs/node.js/best-practices#use-caret", + "title": "Always Use the _Latest Minor_ Releases → for Example, `^7.2.0`", + "depth": 3 + }, + { + "source": "/docs/node.js/best-practices#publish", + "title": "Keep Open Ranges When *Publishing* for Reuse", + "depth": 3 + }, + { + "source": "/docs/node.js/best-practices#bad", + "title": "Bad", + "depth": 4 + }, + { + "source": "/docs/node.js/best-practices#good", + "title": "Good", + "depth": 4 + }, + { + "source": "/docs/node.js/best-practices#deploy", + "title": "Lock Dependencies Before *Deploying*", + "depth": 3 + }, + { + "source": "/docs/node.js/best-practices#oss", + "title": "Minimize Usage of Open Source Packages", + "depth": 3 + }, + { + "source": "/docs/node.js/best-practices#upgrade", + "title": "Upgrade to _Latest Majors_ as Soon as Possible", + "depth": 3 + }, + { + "source": "/docs/node.js/best-practices#additional-advice", + "title": "Additional Advice", + "depth": 3 + }, + { + "source": "/docs/node.js/best-practices#securing-your-application", + "title": "Securing Your Application", + "depth": 2 + }, + { + "source": "/docs/node.js/best-practices#content-security-policy-csp", + "title": "Content Security Policy (CSP)", + "depth": 3 + }, + { + "source": "/docs/node.js/best-practices#cross-site-request-forgery-csrf-token", + "title": "Cross-Site Request Forgery (CSRF) Token", + "depth": 3 + }, + { + "source": "/docs/node.js/best-practices#using-app-router", + "title": "Using App Router", + "depth": 4 + }, + { + "source": "/docs/node.js/best-practices#manual-implementation", + "title": "Manual Implementation", + "depth": 4 + }, + { + "source": "/docs/node.js/best-practices#cross-origin-resource-sharing-cors", + "title": "Cross-Origin Resource Sharing (CORS)", + "depth": 3 + }, + { + "source": "/docs/node.js/best-practices#custom-cors-implementation", + "title": "Custom CORS Implementation", + "depth": 4 + }, + { + "source": "/docs/node.js/best-practices#configuring-cors-in-app-router", + "title": "Configuring CORS in App Router", + "depth": 4 + }, + { + "source": "/docs/node.js/best-practices#availability-checks", + "title": "Availability Checks", + "depth": 2 + }, + { + "source": "/docs/node.js/best-practices#anonymous-ping", + "title": "Anonymous Ping", + "depth": 3 + }, + { + "source": "/docs/node.js/best-practices#error-handling", + "title": "Error Handling", + "depth": 2 + }, + { + "source": "/docs/node.js/best-practices#error-types", + "title": "Error Types", + "depth": 3 + }, + { + "source": "/docs/node.js/best-practices#guidelines", + "title": "Guidelines", + "depth": 3 + }, + { + "source": "/docs/node.js/best-practices#let-it-crash", + "title": "Let It Crash", + "depth": 4 + }, + { + "source": "/docs/node.js/best-practices#dont-hide-origins-of-errors", + "title": "Don't Hide Origins of Errors", + "depth": 4 + }, + { + "source": "/docs/node.js/best-practices#further-readings", + "title": "Further Readings", + "depth": 3 + }, + { + "source": "/docs/node.js/best-practices#timestamps", + "title": "Timestamps", + "depth": 2 + }, + { + "source": "/docs/node.js/best-practices#decimals-int64", + "title": "Decimals and Int64 as Strings", + "depth": 2 + }, + { + "source": "/docs/node.js/best-practices#do-arithmetic-in-the-database", + "title": "Do arithmetic in the database", + "depth": 4 + }, + { + "source": "/docs/node.js/best-practices#arithmetic-in-javascript", + "title": "Arithmetic in Javascript", + "depth": 4 + }, + { + "source": "/docs/node.js/best-practices#custom-streaming-beta", + "title": "Custom Streaming ", + "depth": 2 + }, + { + "source": "/docs/node.js/best-practices#custom-count", + "title": "Custom $count", + "depth": 2 + }, + { + "source": "/docs/java/", + "title": "CAP Service SDK for Java", + "depth": 1 + }, + { + "source": "/docs/java/getting-started", + "title": "Getting Started", + "depth": 1 + }, + { + "source": "/docs/java/getting-started#introduction", + "title": "Introduction", + "depth": 2 + }, + { + "source": "/docs/java/getting-started#local", + "title": "Setting Up Local Development", + "depth": 2 + }, + { + "source": "/docs/java/getting-started#new-project", + "title": "Starting a New Project", + "depth": 2 + }, + { + "source": "/docs/java/getting-started#run-the-cap-java-maven-archetype", + "title": "Run the Maven Archetype", + "depth": 3 + }, + { + "source": "/docs/java/getting-started#add-a-sample-cds-model", + "title": "Add a Sample CDS Model", + "depth": 3 + }, + { + "source": "/docs/java/getting-started#add-cloudfoundry-target-platform", + "title": "Add CloudFoundry target platform", + "depth": 3 + }, + { + "source": "/docs/java/getting-started#project-layout", + "title": "Project Layout", + "depth": 3 + }, + { + "source": "/docs/java/getting-started#add-an-integration-test-module-optional", + "title": "Add an Integration Test Module (Optional)", + "depth": 3 + }, + { + "source": "/docs/java/getting-started#build-and-run", + "title": "Build and Run", + "depth": 3 + }, + { + "source": "/docs/java/getting-started#supported-ides", + "title": "Supported IDEs", + "depth": 3 + }, + { + "source": "/docs/java/getting-started#source-path-configuration-and-cds-build", + "title": "Source Path Configuration and CDS build", + "depth": 4 + }, + { + "source": "/docs/java/getting-started#run-and-test-the-application", + "title": "Run and Test the Application", + "depth": 4 + }, + { + "source": "/docs/java/getting-started#sample", + "title": "Sample Application", + "depth": 2 + }, + { + "source": "/docs/java/versions", + "title": "Versions & Dependencies", + "depth": 1 + }, + { + "source": "/docs/java/versions#versions", + "title": "Versions", + "depth": 2 + }, + { + "source": "/docs/java/versions#active-version", + "title": "Active Version", + "depth": 3 + }, + { + "source": "/docs/java/versions#maintenance-version", + "title": "Maintenance Version", + "depth": 3 + }, + { + "source": "/docs/java/versions#dependencies", + "title": "Maintain Dependencies", + "depth": 2 + }, + { + "source": "/docs/java/versions#minimum-versions", + "title": "Minimum Versions", + "depth": 3 + }, + { + "source": "/docs/java/versions#dependencies-version-5", + "title": "Active Version 5.x", + "depth": 4 + }, + { + "source": "/docs/java/versions#dependencies-version-4", + "title": "Maintenance Version 4.9.x", + "depth": 4 + }, + { + "source": "/docs/java/versions#consistent-versions", + "title": "Consistent Versions", + "depth": 3 + }, + { + "source": "/docs/java/versions#update-versions", + "title": "Update Versions", + "depth": 3 + }, + { + "source": "/docs/java/reflection-api", + "title": "Working with CDS Models", + "depth": 1 + }, + { + "source": "/docs/java/reflection-api#the-cds-model", + "title": "The CDS Model", + "depth": 2 + }, + { + "source": "/docs/java/reflection-api#examples", + "title": "Examples", + "depth": 2 + }, + { + "source": "/docs/java/reflection-api#get-and-inspect-an-element-of-an-entity", + "title": "Get and Inspect an Element of an Entity", + "depth": 3 + }, + { + "source": "/docs/java/reflection-api#get-and-inspect-all-elements-of-an-entity", + "title": "Get and Inspect All Elements of an Entity", + "depth": 3 + }, + { + "source": "/docs/java/reflection-api#get-and-inspect-an-association-element-of-an-entity", + "title": "Get and Inspect an Association Element of an Entity", + "depth": 3 + }, + { + "source": "/docs/java/reflection-api#find-an-annotation-by-name-and-get-its-value", + "title": "Find an Annotation by Name and Get Its Value", + "depth": 3 + }, + { + "source": "/docs/java/reflection-api#filter-a-stream-of-entities-by-namespace", + "title": "Filter a Stream of Entities by Namespace", + "depth": 3 + }, + { + "source": "/docs/java/reflection-api#get-all-elements-with-given-annotation", + "title": "Get All Elements with Given Annotation", + "depth": 3 + }, + { + "source": "/docs/java/reflection-api#feature-toggles", + "title": "Feature Toggles", + "depth": 2 + }, + { + "source": "/docs/java/reflection-api#feature-toggles-and-active-feature-set", + "title": "Feature Toggles and Active Feature Set", + "depth": 3 + }, + { + "source": "/docs/java/reflection-api#features-in-cds-models", + "title": "Features in CDS Models", + "depth": 3 + }, + { + "source": "/docs/java/reflection-api#the-model-provider-service", + "title": "The Model Provider Service", + "depth": 3 + }, + { + "source": "/docs/java/reflection-api#toggling-sap-fiori-ui-elements", + "title": "Toggling SAP Fiori UI Elements", + "depth": 3 + }, + { + "source": "/docs/java/reflection-api#features-on-the-database", + "title": "Features on the Database", + "depth": 3 + }, + { + "source": "/docs/java/reflection-api#feature-toggles-info-provider", + "title": "Feature Toggles Info Provider", + "depth": 3 + }, + { + "source": "/docs/java/reflection-api#from-mock-user-configuration", + "title": "From Mock User Configuration", + "depth": 4 + }, + { + "source": "/docs/java/reflection-api#custom-implementation", + "title": "Custom Implementation", + "depth": 4 + }, + { + "source": "/docs/java/reflection-api#defining-feature-toggles-for-internal-service-calls", + "title": "Defining Feature Toggles for Internal Service Calls", + "depth": 4 + }, + { + "source": "/docs/java/reflection-api#using-feature-toggles-in-custom-code", + "title": "Using Feature Toggles in Custom Code", + "depth": 3 + }, + { + "source": "/docs/java/cds-data", + "title": "Working with CDS Data", + "depth": 1 + }, + { + "source": "/docs/java/cds-data#predefined-types", + "title": "Predefined Types", + "depth": 2 + }, + { + "source": "/docs/java/cds-data#sap-hana-specific-data-types", + "title": "SAP HANA-Specific Data Types", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#numeric-type-determination", + "title": "Numeric Type Determination", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#arithmetic-expressions", + "title": "Arithmetic Expressions", + "depth": 4 + }, + { + "source": "/docs/java/cds-data#numeric-standard-functions", + "title": "Numeric Standard Functions", + "depth": 4 + }, + { + "source": "/docs/java/cds-data#structured-data", + "title": "Structured Data", + "depth": 2 + }, + { + "source": "/docs/java/cds-data#relationships-to-other-entities", + "title": "Relationships to other entities", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#entities-and-structured-types", + "title": "Entities and Structured Types", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#nested-structures-and-associations", + "title": "Nested Structures and Associations", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#cds-data", + "title": "CDS Data", + "depth": 2 + }, + { + "source": "/docs/java/cds-data#path-access", + "title": "Path Access", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#serialization", + "title": "Serialization", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#cds-map", + "title": "Map Data", + "depth": 2 + }, + { + "source": "/docs/java/cds-data#vector-embeddings", + "title": "Vector Embeddings", + "depth": 2 + }, + { + "source": "/docs/java/cds-data#data-in-cds-query-language-cql", + "title": "Data in CDS Query Language (CQL)", + "depth": 2 + }, + { + "source": "/docs/java/cds-data#deep-inserts-through-compositions-and-cascading-associations", + "title": "Deep Inserts through Compositions and Cascading Associations", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#setting-managed-associations-to-existing-target-entities", + "title": "Setting Managed Associations to Existing Target Entities", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#inserts-through-compositions-via-paths", + "title": "Inserts through Compositions via Paths", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#select-managed-associations", + "title": "Select Managed Associations", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#select-with-paths-in-matching", + "title": "Select with Paths in Matching", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#typed-access", + "title": "Typed Access", + "depth": 2 + }, + { + "source": "/docs/java/cds-data#struct", + "title": "Struct", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#generated-accessor-interfaces", + "title": "Generated Accessor Interfaces", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#renaming-elements-in-java", + "title": "Renaming Elements in Java", + "depth": 4 + }, + { + "source": "/docs/java/cds-data#renaming-types-in-java", + "title": "Renaming Types in Java", + "depth": 4 + }, + { + "source": "/docs/java/cds-data#excluding-elements", + "title": "Excluding Elements", + "depth": 4 + }, + { + "source": "/docs/java/cds-data#entity-inheritance-in-java", + "title": "Entity Inheritance in Java", + "depth": 4 + }, + { + "source": "/docs/java/cds-data#creating-a-data-container-for-an-interface", + "title": "Creating a Data Container for an Interface", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#read-only-access", + "title": "Read-Only Access", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#typed-streaming-of-data", + "title": "Typed Streaming of Data", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#typed-access-to-query-results", + "title": "Typed Access to Query Results", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#cds-data-processor", + "title": "Data Processor", + "depth": 2 + }, + { + "source": "/docs/java/cds-data#element-filters", + "title": "Element Filters", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#data-validators", + "title": "Data Validators", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#data-converters", + "title": "Data Converters", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#data-generators", + "title": "Data Generators", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#diff-processor", + "title": "Diff Processor", + "depth": 2 + }, + { + "source": "/docs/java/cds-data#implementing-a-diffvisitor", + "title": "Implementing a DiffVisitor", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#filtering-for-diffvisitor", + "title": "Filtering for DiffVisitor", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#deep-traversal", + "title": "Deep Traversal", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#mediatypeprocessing", + "title": "Media Type Processing", + "depth": 2 + }, + { + "source": "/docs/java/cds-data#no-custom-processing", + "title": "No Custom Processing", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#media-upload", + "title": "Media Upload", + "depth": 4 + }, + { + "source": "/docs/java/cds-data#media-download", + "title": "Media Download", + "depth": 4 + }, + { + "source": "/docs/java/cds-data#custom-processing", + "title": "Custom Processing", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#media-upload-1", + "title": "Media Upload", + "depth": 4 + }, + { + "source": "/docs/java/cds-data#media-download-1", + "title": "Media Download", + "depth": 4 + }, + { + "source": "/docs/java/cds-data#pre--or-post-processing-using-a-stream-proxy", + "title": "Pre- or Post-Processing Using a Stream Proxy", + "depth": 3 + }, + { + "source": "/docs/java/cds-data#media-upload-2", + "title": "Media Upload", + "depth": 4 + }, + { + "source": "/docs/java/cds-data#media-download-2", + "title": "Media Download", + "depth": 4 + }, + { + "source": "/docs/java/cds-data#reminder", + "title": "Reminder", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/", + "title": "Working with CDS CQL", + "depth": 1 + }, + { + "source": "/docs/java/working-with-cql/query-api", + "title": "Building CQL Statements", + "depth": 1 + }, + { + "source": "/docs/java/working-with-cql/query-api#introduction", + "title": "Introduction", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-api#concepts", + "title": "Concepts", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-api#the-cql-statement-builders", + "title": "The CQL Statement Builders", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#lambda-expressions", + "title": "Lambda Expressions", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#path-expressions", + "title": "Path Expressions", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#target-entity-sets", + "title": "Target Entity Sets", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#target-entity-filters", + "title": "Filters", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#concepts-where-clause", + "title": "Using `where`", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#using-byid", + "title": "Using `byID`", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#using-matching", + "title": "Using `matching`", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#using-byparams", + "title": "Using `byParams`", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#filtering-map-data", + "title": "Filtering Map Data ", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#parameters", + "title": "Parameters", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#constant-and-non-constant-literal-values", + "title": "Constant and Non-Constant Literal Values", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#select", + "title": "Select", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-api#source", + "title": "Source", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#from-entity-set", + "title": "`FROM` Entity Set", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#from-reference", + "title": "`FROM` Reference", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#from-select", + "title": "`FROM` Subquery", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#projections", + "title": "Projections", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#expand", + "title": "Deep Read with `expand`", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#expand-optimization", + "title": "Optimized Expand Execution", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#inline", + "title": "Flattened Results with `inline`", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#managed-associations-on-the-select-list", + "title": "Managed Associations on the Select List", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#selecting-map-data", + "title": "Selecting Map Data", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#selecting-sub-elements-of-map-data", + "title": "Selecting Sub-Elements of Map Data ", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#filtering", + "title": "Filtering and Searching", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#search-in-sub-elements-of-map-data", + "title": "Search in Sub-Elements of Map Data ", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#where-clause", + "title": "Using `where` Clause", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#aggregating", + "title": "Aggregating Data", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#aggregation-functions", + "title": "Aggregation Functions", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#grouping", + "title": "Grouping", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#group-by", + "title": "Group By", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#having", + "title": "Having", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#aggregating-associations", + "title": "Aggregating over Associations ", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#min", + "title": "min", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#max", + "title": "max", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#sum", + "title": "sum", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#count", + "title": "count", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#ordering-and-pagination", + "title": "Ordering and Pagination", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#order-by", + "title": "Order By", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#pagination", + "title": "Pagination", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#sorting-by-map-data", + "title": "Sorting by Map Data ", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#write-lock", + "title": "Pessimistic Locking", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#wait-strategies", + "title": "Wait Strategies", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#restrictions", + "title": "Restrictions", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#insert", + "title": "Insert", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-api#single-insert", + "title": "Single Insert", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#bulk-insert", + "title": "Bulk Insert", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#deep-insert", + "title": "Deep Insert", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#upsert", + "title": "Upsert", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-api#single-upsert", + "title": "Single Upsert", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#bulk-upsert", + "title": "Bulk Upsert", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#deep-upsert", + "title": "Deep Upsert", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#update", + "title": "Update", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-api#update-individual-entities", + "title": "Updating Individual Entities", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#update-expressions", + "title": "Update with Expressions", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#deep-update", + "title": "Deep Update", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#deep-update-full-set", + "title": "Full Set Representation", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#deep-update-delta", + "title": "Delta Representation", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#bulk-update", + "title": "Bulk Update: Update Multiple Entity Records with Individual Data", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#update-multiple-entity-records-with-the-same-data", + "title": "Update Multiple Entity Records with the same Data", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#searched-update", + "title": "Searched Update", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#batch-update", + "title": "Parameterized Batch Update", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#delete", + "title": "Delete", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-api#using-matching-1", + "title": "Using `matching`", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#using-byparams-1", + "title": "Using `byParams`", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#expressions", + "title": "Expressions", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-api#entity-refs", + "title": "Entity References", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#values", + "title": "Values", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#element-references", + "title": "Element References", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#literal-values", + "title": "Literal Values", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#list-values", + "title": "List Values", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#expr-param", + "title": "Parameters", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#scalar-functions", + "title": "Scalar Functions", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#generic-scalar-function", + "title": "Generic Scalar Function", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#tolower", + "title": "`toLower`", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#toupper", + "title": "`toUpper`", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#substring", + "title": "`substring`", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#concat", + "title": "`concat`", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#datetime-functions", + "title": "Date/Time functions", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#extraction-functions", + "title": "Extraction Functions", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#difference-computation-functions", + "title": "Difference Computation Functions", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#vector-functions", + "title": "Vector Functions", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#computing-vector-embeddings-in-sap-hana", + "title": "Computing Vector Embeddings in SAP HANA ", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#computing-vector-similarity-and-distance", + "title": "Computing Vector Similarity and Distance", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#case-when-then-expressions", + "title": "Case-When-Then Expressions", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#concat-expression", + "title": "Concat Expression", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#string-expressions", + "title": "String Expressions", + "depth": 6 + }, + { + "source": "/docs/java/working-with-cql/query-api#arithmetic-expressions", + "title": "Arithmetic Expressions", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#plus", + "title": "`plus`", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#minus", + "title": "`minus`", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#times", + "title": "`times`", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#dividedby", + "title": "`dividedBy`", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#predicates", + "title": "Predicates", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#comparison-operators", + "title": "`Comparison Operators`", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#in-predicate", + "title": "`IN` Predicate", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#in-subquery-predicate", + "title": "`IN` Subquery Predicate", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#etag-predicate", + "title": "`ETag Predicate`", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#logical-operators", + "title": "`Logical Operators`", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#predicate-functions", + "title": "`Predicate Functions`", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#containment-test", + "title": "Containment Test", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#matches-pattern", + "title": "Regular Expressions (`matchesPattern`)", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#filter-by-associated-data", + "title": "Filter by Associated Data", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#any-match", + "title": "Using `anyMatch/allMatch`", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#exists-subquery", + "title": "Using an `EXISTS` Subquery", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-api#parsing-cqn", + "title": "Parsing CQN", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-api#cql-helper-interface", + "title": "CQL Expression Trees", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-api#composing-predicates", + "title": "Composing Predicates", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#connecting-streams-of-predicates", + "title": "Connecting Streams of Predicates", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-api#working-with-select-list-items", + "title": "Working with Select List Items", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#using-functions-and-arithmetic-expressions", + "title": "Using Functions and Arithmetic Expressions", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#copying-modifying-cql-statements", + "title": "Copying & Modifying CDS QL Statements", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-api#modify-where", + "title": "Replacing Predicates", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#modify-ref", + "title": "Replacing References", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#modify-select", + "title": "Modify the Select List", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-api#modify-order-by", + "title": "Modify the Order-By Clause", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution", + "title": "Executing CQL Statements", + "depth": 1 + }, + { + "source": "/docs/java/working-with-cql/query-execution#queries", + "title": "Query Execution", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-execution#parameterized-execution", + "title": "Parameterized Execution", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution#named-parameters", + "title": "Named Parameters", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-execution#indexed-parameters", + "title": "Indexed Parameters", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-execution#batch-execution", + "title": "Batch Execution", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-execution#querying-views", + "title": "Querying Parameterized Views on SAP HANA", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-execution#hana-hints", + "title": "Query Hints", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution#data-manipulation", + "title": "Data Manipulation", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution#update", + "title": "Update", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-execution#structured-documents", + "title": "Structured Documents", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution#cascading-over-associations", + "title": "Cascading over Associations", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-execution#deep-insert-upsert", + "title": "Deep Insert / Upsert", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-execution#cascading-delete", + "title": "Cascading Delete", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-execution#views", + "title": "Views and Projections", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-execution#updatable-views", + "title": "Write through Views", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution#delete-via-view", + "title": "Delete through Views", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution#runtimeviews", + "title": "Runtime Views", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution#rtview-cte", + "title": "Read in `cte` mode", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-execution#rtview-resolve", + "title": "Read in `resolve` mode", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-execution#draft-views", + "title": "Draft Queries on Views", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution#views-on-remote-services", + "title": "Views on Remote Services", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution#concurrency-control", + "title": "Concurrency Control", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-execution#optimistic", + "title": "Optimistic Locking", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution#optimistic-concurrency-control-in-odata", + "title": "Optimistic Concurrency Control in OData", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-execution#etag-predicate", + "title": "The ETag Predicate", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-execution#providing-new-etag-values-with-update-data", + "title": "Providing new ETag Values with Update Data", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-execution#runtime-managed-versions", + "title": "Runtime-Managed Versions ", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-execution#expected-version-from-data", + "title": "Expected Version from Data", + "depth": 5 + }, + { + "source": "/docs/java/working-with-cql/query-execution#pessimistic-locking", + "title": "Pessimistic Locking", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution#using-io-streams-in-queries", + "title": "Using I/O Streams in Queries", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-execution#using-native-sql", + "title": "Using Native SQL", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-execution#result", + "title": "Query Result Processing", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-execution#null-values", + "title": "Null Values", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution#typed-result-processing", + "title": "Typed Result Processing", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution#entity-refs", + "title": "Entity References", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-execution#introspecting-the-row-type", + "title": "Introspecting the Row Type", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-introspection", + "title": "Introspecting CQL Statements", + "depth": 1 + }, + { + "source": "/docs/java/working-with-cql/query-introspection#introduction", + "title": "Introduction", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-introspection#cqnanalyzer-vs-cqnvisitor", + "title": "CqnAnalyzer vs. CqnVisitor", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-introspection#when-to-use-what", + "title": "When to Use What", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-introspection#cqnanalyzer", + "title": "CqnAnalyzer", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-introspection#usage", + "title": "Usage", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-introspection#resolving-cds-entities", + "title": "Resolving CDS Entities", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-introspection#extracting-filter-values", + "title": "Extracting Filter Values", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-introspection#using-the-iterator", + "title": "Using the Iterator", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-introspection#cqnvisitor", + "title": "CqnVisitor", + "depth": 2 + }, + { + "source": "/docs/java/working-with-cql/query-introspection#fields-of-application", + "title": "Fields of Application", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-introspection#usage-1", + "title": "Usage", + "depth": 3 + }, + { + "source": "/docs/java/working-with-cql/query-introspection#data", + "title": "Data", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-introspection#filter", + "title": "Filter", + "depth": 4 + }, + { + "source": "/docs/java/working-with-cql/query-introspection#visitor", + "title": "Visitor", + "depth": 4 + }, + { + "source": "/docs/java/services", + "title": "Services", + "depth": 1 + }, + { + "source": "/docs/java/services#an-event-based-api", + "title": "An Event-Based API", + "depth": 2 + }, + { + "source": "/docs/java/services#using-services", + "title": "Using Services", + "depth": 3 + }, + { + "source": "/docs/java/services#cqn-based-services", + "title": "CQN-based Services", + "depth": 2 + }, + { + "source": "/docs/java/services#application-lifecycle-service", + "title": "Application Lifecycle Service", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/", + "title": "CQN Services", + "depth": 1 + }, + { + "source": "/docs/java/cqn-services/#application-services", + "title": "Application Services", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/#draftservices", + "title": "Draft Services", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/#persistenceservice", + "title": "Persistence Services", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/#remote-services", + "title": "Remote Services", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/persistence-services", + "title": "Persistence Services", + "depth": 1 + }, + { + "source": "/docs/java/cqn-services/persistence-services#database-support", + "title": "Database Support", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/persistence-services#sap-hana-cloud", + "title": "SAP HANA Cloud", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/persistence-services#postgresql", + "title": "PostgreSQL", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/persistence-services#h2-database", + "title": "H2 Database", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/persistence-services#sqlite", + "title": "SQLite", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/persistence-services#datasources", + "title": "Datasources", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/persistence-services#datasource-configuration", + "title": "Datasource Configuration", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/persistence-services#sap-hana", + "title": "SAP HANA", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/persistence-services#service-bindings", + "title": "Service Bindings", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/persistence-services#configure-the-ddl-generation", + "title": "Configure the DDL generation", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/persistence-services#sql-optimization-mode", + "title": "SQL Optimization Mode", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/persistence-services#postgresql-1", + "title": "PostgreSQL", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/persistence-services#initial-database-schema", + "title": "Initial Database Schema", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/persistence-services#postgres-connection", + "title": "Configure the Connection Data Explicitly", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/persistence-services#h2", + "title": "H2", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/persistence-services#sqlite-1", + "title": "SQLite", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/persistence-services#initial-database-schema-1", + "title": "Initial Database Schema", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/persistence-services#file-based-storage", + "title": "File-Based Storage", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/persistence-services#in-memory-storage", + "title": "In-Memory Storage", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/persistence-services#persistence-services-1", + "title": "Persistence Services", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/persistence-services#default-persistence-service", + "title": "The Default Persistence Service", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/persistence-services#additional-persistence-services", + "title": "Additional Persistence Services", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/persistence-services#example-multitenant-application-with-tenant-independent-datasource", + "title": "Example: Multitenant Application with Tenant-independent Datasource", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/persistence-services#local-development-and-testing-with-mtx", + "title": "Local Development and Testing with MTX", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/persistence-services#local-development-and-testing-without-mtx", + "title": "Local Development and Testing without MTX", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/persistence-services#native-sql", + "title": "Native SQL", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/persistence-services#jdbctemplate", + "title": "Native SQL with JDBC Templates", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/persistence-services#staticmodel", + "title": "Using CQL with a Static CDS Model", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/persistence-services#static-model-in-the-query-builder", + "title": "Static Model in the Query Builder", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/persistence-services#model-interfaces", + "title": "Model Interfaces", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/persistence-services#accessor-interfaces", + "title": " Accessor Interfaces", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/persistence-services#javadoc-comments", + "title": "Javadoc comments", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/persistence-services#usage", + "title": "Usage", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/application-services", + "title": "Application Services", + "depth": 1 + }, + { + "source": "/docs/java/cqn-services/application-services#crudevents", + "title": "Handling CRUD Events", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/application-services#odata-requests", + "title": "OData Requests", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/application-services#deeply-structured-documents", + "title": "Deeply Structured Documents", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/application-services#result-handling", + "title": "Result Handling", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/application-services#read-result", + "title": "READ Result", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/application-services#update-and-delete-results", + "title": "UPDATE and DELETE Results", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/application-services#insert-and-upsert-results", + "title": "INSERT and UPSERT Results", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/application-services#result-builder", + "title": "Result Builder", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/application-services#actions", + "title": "Actions and Functions", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/application-services#implement-event-handler", + "title": "Implement Event Handler", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/application-services#trigger-action-or-function", + "title": "Trigger Action or Function", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/application-services#best-practices-and-faqs", + "title": "Best Practices and FAQs", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/application-services#serve-configuration", + "title": "Serve Configuration", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/application-services#configure-base-path", + "title": "Configure Base Path", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/application-services#configure-path-and-protocol", + "title": "Configure Path and Protocol", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/application-services#configure-endpoints", + "title": "Configure Endpoints", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/remote-services", + "title": "Remote Services", + "depth": 1 + }, + { + "source": "/docs/java/cqn-services/remote-services#remote-odata-services", + "title": "Remote OData Services", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/remote-services#configuring-cds-service-name", + "title": "Configuring CDS Service Name", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/remote-services#service-binding-based-scenarios", + "title": "Using Service Bindings", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/remote-services#binding-to-a-reuse-service", + "title": "Binding to a Reuse Service", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/remote-services#binding-to-a-service-with-shared-identity", + "title": "Binding to a Service with Shared Identity", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/remote-services#configuring-the-authentication-strategy", + "title": "Configuring the Authentication Strategy", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/remote-services#destination-based-scenarios", + "title": "Using Destinations", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/remote-services#consuming-apis-from-other-ias-applications", + "title": "Consuming APIs from Other IAS-Applications", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/remote-services#configuring-the-authentication-strategy-1", + "title": "Configuring the Authentication Strategy", + "depth": 5 + }, + { + "source": "/docs/java/cqn-services/remote-services#retrieve-destinations", + "title": "Retrieve Destinations", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/remote-services#configuring-the-url", + "title": "Configuring the URL", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/remote-services#consuming-remote-services", + "title": "Consuming Remote Services", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/remote-services#consuming-media-elements", + "title": "Consuming Media Elements", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/remote-services#reading-media-elements", + "title": "Reading Media Elements", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/remote-services#writing-media-elements", + "title": "Writing Media Elements", + "depth": 4 + }, + { + "source": "/docs/java/cqn-services/remote-services#cloud-sdk-integration", + "title": "Cloud SDK Integration", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/remote-services#cloud-sdk-dependencies", + "title": "Maven Dependencies", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/remote-services#destination-strategies", + "title": "Configuring Destination Strategies", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/remote-services#programmatic-destination-registration", + "title": "Programmatic Destination Registration", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/remote-services#native-consumption", + "title": "Native Service Consumption", + "depth": 2 + }, + { + "source": "/docs/java/cqn-services/remote-services#native-bindings", + "title": "Using Service Bindings", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/remote-services#native-destinations", + "title": "Using Destinations", + "depth": 3 + }, + { + "source": "/docs/java/cqn-services/remote-services#programmatic-destinations", + "title": "Programmatic Destinations", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/", + "title": "Event Handlers", + "depth": 1 + }, + { + "source": "/docs/java/event-handlers/#introduction-to-event-handlers", + "title": "Introduction to Event Handlers", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/#phases", + "title": "Event Phases", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/#before", + "title": "Before", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/#on", + "title": "On", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/#after", + "title": "After", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/#eventcontext", + "title": "Event Contexts", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/#eventcompletion", + "title": "Completing the Event Processing", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/#proceed-on", + "title": "Explicitly Proceeding the On Handler Execution", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/#customeventcontext", + "title": "Defining Custom EventContext Interfaces", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/#handlerclasses", + "title": "Event Handler Classes", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/#handlerannotations", + "title": "Event Handler Annotations", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/#handlersignature", + "title": "Event Handler Method Signatures", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/#contextarguments", + "title": "Event Context Arguments", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/#pojoarguments", + "title": "Entity Data Arguments", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/#entity-reference-arguments", + "title": "Entity Reference Arguments", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/#servicearguments", + "title": "Service Arguments", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/#return-values", + "title": "Return Values", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/#ordering-of-event-handler-methods", + "title": "Ordering of Event Handler Methods", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/indicating-errors", + "title": "Indicating Errors", + "depth": 1 + }, + { + "source": "/docs/java/event-handlers/indicating-errors#overview", + "title": "Overview", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/indicating-errors#exceptions", + "title": "Exceptions", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/indicating-errors#messages", + "title": "Messages", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/indicating-errors#throwing-a-serviceexception-from-messages", + "title": "Throwing a ServiceException from Error Messages", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/indicating-errors#formatting-and-localization", + "title": "Formatting and Localization", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/indicating-errors#ootb-translated-messages", + "title": "Translations for Validation Error Messages", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/indicating-errors#provide-custom-error-messages", + "title": "Provide custom error messages", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/indicating-errors#target", + "title": "Target", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/indicating-errors#crud-events", + "title": "CRUD Events", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/indicating-errors#bound-actions-and-functions", + "title": "Bound Actions and Functions", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/indicating-errors#errorhandler", + "title": "Error Handler", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/request-contexts", + "title": "Request Contexts", + "depth": 1 + }, + { + "source": "/docs/java/event-handlers/request-contexts#overview", + "title": "Overview", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/request-contexts#reading-requestcontext", + "title": "Accessing Request Contexts", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/request-contexts#defining-requestcontext", + "title": "Defining New Request Contexts", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/request-contexts#modifying-requestcontext", + "title": "Modifying Request Contexts", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/request-contexts#request-context-inheritance", + "title": "Request Context Inheritance", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/request-contexts#global-providers", + "title": "Registering Global Providers", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/request-contexts#threading-requestcontext", + "title": "Passing Request Contexts to Threads", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/changeset-contexts", + "title": "ChangeSet Contexts", + "depth": 1 + }, + { + "source": "/docs/java/event-handlers/changeset-contexts#overview", + "title": "Overview", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/changeset-contexts#defining-changeset-contexts", + "title": "Defining ChangeSet Contexts", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/changeset-contexts#reacting-on-changesets", + "title": "Reacting on ChangeSets", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/changeset-contexts#cancelling-changesets", + "title": "Cancelling ChangeSets", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/changeset-contexts#database-transactions-in-spring-boot", + "title": "Database Transactions in Spring Boot", + "depth": 2 + }, + { + "source": "/docs/java/event-handlers/changeset-contexts#setting-session-context-variables", + "title": "Setting Session Context Variables", + "depth": 3 + }, + { + "source": "/docs/java/event-handlers/changeset-contexts#avoid-transactions", + "title": "Avoiding Transactions for Select", + "depth": 2 + }, + { + "source": "/docs/java/event-queues", + "title": "Event Queues in Java", + "depth": 1 + }, + { + "source": "/docs/java/event-queues#programmatic-api", + "title": "Programmatic API", + "depth": 2 + }, + { + "source": "/docs/java/event-queues#queueing-a-service", + "title": "Queueing a Service", + "depth": 3 + }, + { + "source": "/docs/java/event-queues#scheduling", + "title": "Scheduling", + "depth": 3 + }, + { + "source": "/docs/java/event-queues#schedule-options", + "title": "`Schedule` Options", + "depth": 4 + }, + { + "source": "/docs/java/event-queues#technical-outbox-api", + "title": "Technical Outbox API", + "depth": 3 + }, + { + "source": "/docs/java/event-queues#custom-serialization", + "title": "Custom Serialization", + "depth": 4 + }, + { + "source": "/docs/java/event-queues#error-handling", + "title": "Error Handling", + "depth": 3 + }, + { + "source": "/docs/java/event-queues#configuration", + "title": "Configuration", + "depth": 2 + }, + { + "source": "/docs/java/event-queues#default-outbox-services", + "title": "Default Outbox Services", + "depth": 3 + }, + { + "source": "/docs/java/event-queues#status-lock-timeout", + "title": "Status Lock Timeout", + "depth": 4 + }, + { + "source": "/docs/java/event-queues#collector-strategies", + "title": "Collector Strategies", + "depth": 3 + }, + { + "source": "/docs/java/event-queues#hot-tenant-task", + "title": "Hot-Tenant Task", + "depth": 4 + }, + { + "source": "/docs/java/event-queues#all-tenants-task", + "title": "All-Tenants Task", + "depth": 4 + }, + { + "source": "/docs/java/event-queues#custom-outbox-services", + "title": "Custom Outbox Services", + "depth": 3 + }, + { + "source": "/docs/java/event-queues#shared-databases", + "title": "Shared Databases", + "depth": 3 + }, + { + "source": "/docs/java/event-queues#event-versions", + "title": "Event Versions", + "depth": 3 + }, + { + "source": "/docs/java/event-queues#troubleshooting", + "title": "Troubleshooting", + "depth": 2 + }, + { + "source": "/docs/java/event-queues#inspecting-cdsoutboxmessages", + "title": "Inspecting `cds.outbox.Messages`", + "depth": 3 + }, + { + "source": "/docs/java/event-queues#deleting-entries", + "title": "Deleting Entries", + "depth": 3 + }, + { + "source": "/docs/java/fiori-drafts", + "title": "Fiori Drafts", + "depth": 1 + }, + { + "source": "/docs/java/fiori-drafts#draftevents", + "title": "Overview", + "depth": 2 + }, + { + "source": "/docs/java/fiori-drafts#reading-drafts", + "title": "Reading Drafts", + "depth": 2 + }, + { + "source": "/docs/java/fiori-drafts#aggregation-queries", + "title": "Aggregation Queries", + "depth": 3 + }, + { + "source": "/docs/java/fiori-drafts#editing-drafts", + "title": "Editing Drafts", + "depth": 2 + }, + { + "source": "/docs/java/fiori-drafts#activating-drafts", + "title": "Activating Drafts", + "depth": 2 + }, + { + "source": "/docs/java/fiori-drafts#working-with-draft-enabled-entities", + "title": "Working with Draft-Enabled Entities", + "depth": 2 + }, + { + "source": "/docs/java/fiori-drafts#bypassing-draft-flow", + "title": "Bypassing the SAP Fiori Draft Flow", + "depth": 2 + }, + { + "source": "/docs/java/fiori-drafts#draft-lock", + "title": "Draft Lock", + "depth": 2 + }, + { + "source": "/docs/java/fiori-drafts#draft-gc", + "title": "Draft Garbage Collection", + "depth": 2 + }, + { + "source": "/docs/java/fiori-drafts#fioridraftnew", + "title": "Overriding SAP Fiori's Draft Creation Behaviour", + "depth": 2 + }, + { + "source": "/docs/java/fiori-drafts#draftservices", + "title": "Consuming Draft Services", + "depth": 2 + }, + { + "source": "/docs/java/messaging", + "title": "Pub-Sub Messaging", + "depth": 2 + }, + { + "source": "/docs/java/messaging#sending", + "title": "Sending", + "depth": 2 + }, + { + "source": "/docs/java/messaging#receiving", + "title": "Receiving", + "depth": 2 + }, + { + "source": "/docs/java/messaging#cds-declared-events", + "title": "CDS-Declared Events", + "depth": 2 + }, + { + "source": "/docs/java/messaging#supported-message-brokers", + "title": "Supported Message Brokers", + "depth": 2 + }, + { + "source": "/docs/java/messaging#local-testing", + "title": "Local Testing", + "depth": 3 + }, + { + "source": "/docs/java/messaging#using-real-brokers", + "title": "Using Real Brokers", + "depth": 3 + }, + { + "source": "/docs/java/messaging#configuring-sap-event-mesh-support", + "title": "Configuring SAP Event Mesh Support:", + "depth": 4 + }, + { + "source": "/docs/java/messaging#configuring-sap-event-hub-support", + "title": "Configuring SAP Cloud Application Event Hub Support:", + "depth": 4 + }, + { + "source": "/docs/java/messaging#configuring-advanced-event-mesh-support", + "title": "Configuring SAP Integration Suite, Advanced Event Mesh Support :", + "depth": 4 + }, + { + "source": "/docs/java/messaging#configuring-redis-pubsub-support-beta", + "title": "Configuring Redis PubSub Support :", + "depth": 4 + }, + { + "source": "/docs/java/messaging#injecting-messaging-services", + "title": "Injecting Messaging Services", + "depth": 4 + }, + { + "source": "/docs/java/messaging#using-message-brokers-in-cloud-foundry", + "title": "Using Message Brokers in Cloud Foundry", + "depth": 3 + }, + { + "source": "/docs/java/messaging#maven-dependency-for-cloud-foundry-support", + "title": "Maven Dependency for Cloud Foundry Support:", + "depth": 4 + }, + { + "source": "/docs/java/messaging#running-on-the-local-system", + "title": "Running on the Local System", + "depth": 4 + }, + { + "source": "/docs/java/messaging#vcap_services-template-for-sap-event-mesh", + "title": "VCAP_SERVICES Template for SAP Event Mesh", + "depth": 4 + }, + { + "source": "/docs/java/messaging#composite-messaging-service", + "title": "Composite Messaging Service", + "depth": 2 + }, + { + "source": "/docs/java/messaging#details-and-advanced-concepts", + "title": "Details and Advanced Concepts", + "depth": 2 + }, + { + "source": "/docs/java/messaging#queue-configuration", + "title": "Queue Configuration", + "depth": 3 + }, + { + "source": "/docs/java/messaging#queue-configuration-changes", + "title": "Queue Configuration Changes", + "depth": 3 + }, + { + "source": "/docs/java/messaging#using-multiple-queues", + "title": "Using Multiple Queues", + "depth": 3 + }, + { + "source": "/docs/java/messaging#consuming-from-a-queue", + "title": "Consuming from a Queue", + "depth": 3 + }, + { + "source": "/docs/java/messaging#dedicated-connections", + "title": "Dedicated Connections", + "depth": 3 + }, + { + "source": "/docs/java/messaging#error-handling", + "title": "Error Handling", + "depth": 3 + }, + { + "source": "/docs/java/messaging#acknowledgement-support", + "title": "Acknowledgement Support", + "depth": 4 + }, + { + "source": "/docs/java/messaging#sending-and-receiving-in-the-same-instance", + "title": "Sending and Receiving in the Same Instance", + "depth": 3 + }, + { + "source": "/docs/java/messaging#topic-prefixing", + "title": "Topic Prefixing", + "depth": 3 + }, + { + "source": "/docs/java/messaging#messages-representation", + "title": "Messages Representation", + "depth": 3 + }, + { + "source": "/docs/java/messaging#cloudevents", + "title": "CloudEvents", + "depth": 3 + }, + { + "source": "/docs/java/auditlog", + "title": "Audit Logging", + "depth": 1 + }, + { + "source": "/docs/java/auditlog#auditlog-service", + "title": "AuditLog Service", + "depth": 2 + }, + { + "source": "/docs/java/auditlog#overview", + "title": "Overview", + "depth": 3 + }, + { + "source": "/docs/java/auditlog#use-auditlogservice", + "title": "Use AuditLogService", + "depth": 3 + }, + { + "source": "/docs/java/auditlog#get-auditlogservice-instance", + "title": "Get AuditLogService Instance", + "depth": 4 + }, + { + "source": "/docs/java/auditlog#data-access", + "title": "Emit Personal Data Access Event", + "depth": 4 + }, + { + "source": "/docs/java/auditlog#data-modification", + "title": "Emit Personal Data Modification Event", + "depth": 4 + }, + { + "source": "/docs/java/auditlog#config-change", + "title": "Emit Configuration Change Event", + "depth": 4 + }, + { + "source": "/docs/java/auditlog#security-event", + "title": "Emit Security Event", + "depth": 4 + }, + { + "source": "/docs/java/auditlog#deferred", + "title": "Deferred AuditLog Events", + "depth": 3 + }, + { + "source": "/docs/java/auditlog#auditlog-handlers", + "title": "AuditLog Handlers", + "depth": 2 + }, + { + "source": "/docs/java/auditlog#default-handler", + "title": "Default Handler", + "depth": 3 + }, + { + "source": "/docs/java/auditlog#handler-v2", + "title": "AuditLog v2 Handler", + "depth": 3 + }, + { + "source": "/docs/java/auditlog#custom-auditlog-handler", + "title": "Custom AuditLog Handler", + "depth": 3 + }, + { + "source": "/docs/java/change-tracking", + "title": "Change Tracking", + "depth": 1 + }, + { + "source": "/docs/java/change-tracking#enabling-change-tracking", + "title": "Enabling Change Tracking", + "depth": 2 + }, + { + "source": "/docs/java/change-tracking#annotating-entities", + "title": "Annotating Entities", + "depth": 3 + }, + { + "source": "/docs/java/change-tracking#identifiers-for-entities", + "title": "Identifiers for Entities", + "depth": 3 + }, + { + "source": "/docs/java/change-tracking#identifiers-for-compositions", + "title": "Identifiers for Compositions", + "depth": 3 + }, + { + "source": "/docs/java/change-tracking#human-readable-values-for-associations", + "title": "Human-readable values for associations", + "depth": 3 + }, + { + "source": "/docs/java/change-tracking#caveats-of-identifiers", + "title": "Caveats of Identifiers", + "depth": 3 + }, + { + "source": "/docs/java/change-tracking#displaying-changes", + "title": "Displaying Changes", + "depth": 3 + }, + { + "source": "/docs/java/change-tracking#how-changes-are-stored", + "title": "How Changes are Stored", + "depth": 2 + }, + { + "source": "/docs/java/change-tracking#detection-of-changes", + "title": "Detection of Changes", + "depth": 2 + }, + { + "source": "/docs/java/change-tracking#changes-in-deeply-structured-documents", + "title": "Changes in Deeply Structured Documents", + "depth": 3 + }, + { + "source": "/docs/java/change-tracking#reacting-on-changes", + "title": "Reacting on Changes", + "depth": 2 + }, + { + "source": "/docs/java/change-tracking#tips-and-tricks", + "title": "Tips and Tricks", + "depth": 2 + }, + { + "source": "/docs/java/change-tracking#advanced-identifiers-for-associated-entities", + "title": "Advanced Identifiers for Associated Entities", + "depth": 3 + }, + { + "source": "/docs/java/change-tracking#things-to-consider-when-using-change-tracking", + "title": "Things to Consider when Using Change Tracking", + "depth": 2 + }, + { + "source": "/docs/java/multitenancy", + "title": "Multitenancy", + "depth": 1 + }, + { + "source": "/docs/java/multitenancy#setup-overview", + "title": "Setup Overview", + "depth": 2 + }, + { + "source": "/docs/java/multitenancy#custom-logic", + "title": "React on Tenant Events", + "depth": 2 + }, + { + "source": "/docs/java/multitenancy#subscribe-tenant", + "title": "Subscribe Tenant", + "depth": 3 + }, + { + "source": "/docs/java/multitenancy#defining-a-database-id", + "title": "Defining a Database ID", + "depth": 4 + }, + { + "source": "/docs/java/multitenancy#unsubscribe-tenant", + "title": "Unsubscribe Tenant", + "depth": 3 + }, + { + "source": "/docs/java/multitenancy#skipping-deletion-of-tenant-data", + "title": "Skipping Deletion of Tenant Data", + "depth": 4 + }, + { + "source": "/docs/java/multitenancy#define-dependent-services", + "title": "Define Dependent Services", + "depth": 3 + }, + { + "source": "/docs/java/multitenancy#database-update", + "title": "Database Schema Update", + "depth": 3 + }, + { + "source": "/docs/java/multitenancy#deploy-main-method", + "title": "Deploy Main Method", + "depth": 4 + }, + { + "source": "/docs/java/multitenancy#development-aspects", + "title": "Development Aspects", + "depth": 2 + }, + { + "source": "/docs/java/multitenancy#working-with-tenants", + "title": "Working with Tenants", + "depth": 3 + }, + { + "source": "/docs/java/multitenancy#switching-provider-tenant", + "title": "Switching to Provider Tenant", + "depth": 4 + }, + { + "source": "/docs/java/multitenancy#switching-subscriber-tenant", + "title": "Switching to Subscriber Tenants", + "depth": 4 + }, + { + "source": "/docs/java/multitenancy#enumerating-subscriber-tenants", + "title": "Enumerating Subscriber Tenants", + "depth": 4 + }, + { + "source": "/docs/java/multitenancy#db-connection-pooling", + "title": "DB Connection Pooling", + "depth": 3 + }, + { + "source": "/docs/java/multitenancy#pool-per-tenant---less-latency-more-resources", + "title": "Pool per tenant - less latency, more resources", + "depth": 4 + }, + { + "source": "/docs/java/multitenancy#combine-data-pools", + "title": "Pool per database - less resources, more latency", + "depth": 4 + }, + { + "source": "/docs/java/multitenancy#configure-data-pools", + "title": "Dynamic Data Source Pooling", + "depth": 4 + }, + { + "source": "/docs/java/multitenancy#app-log-support", + "title": "Logging Support", + "depth": 3 + }, + { + "source": "/docs/java/multitenancy#mtx-properties", + "title": "Configuration Properties", + "depth": 3 + }, + { + "source": "/docs/java/security", + "title": "Authentication", + "depth": 2 + }, + { + "source": "/docs/java/security#xsuaa-ias", + "title": "Auto Configuration", + "depth": 3 + }, + { + "source": "/docs/java/security#maven-dependencies", + "title": "Maven Dependencies", + "depth": 4 + }, + { + "source": "/docs/java/security#bindings", + "title": "Service Bindings", + "depth": 4 + }, + { + "source": "/docs/java/security#spring-boot", + "title": "Custom Authentication", + "depth": 3 + }, + { + "source": "/docs/java/security#auth-endpoints", + "title": "Authenticated Endpoints", + "depth": 4 + }, + { + "source": "/docs/java/security#auth-mode", + "title": "Authentication Modes", + "depth": 4 + }, + { + "source": "/docs/java/security#custom-spring-security-config", + "title": "Overrule Partially", + "depth": 4 + }, + { + "source": "/docs/java/security#custom-spring-security-alone", + "title": "Overrule Fully", + "depth": 4 + }, + { + "source": "/docs/java/security#custom-authentication", + "title": "CAP Users", + "depth": 2 + }, + { + "source": "/docs/java/security#mock-users", + "title": "Mock Users", + "depth": 3 + }, + { + "source": "/docs/java/security#preconfigured-mock-users", + "title": "Preconfigured Mock Users", + "depth": 4 + }, + { + "source": "/docs/java/security#custom-mock-users", + "title": "Custom Mock Users", + "depth": 4 + }, + { + "source": "/docs/java/security#mock-tenants", + "title": "Mock Tenants", + "depth": 4 + }, + { + "source": "/docs/java/security#custom-users", + "title": "Custom Users", + "depth": 3 + }, + { + "source": "/docs/java/spring-boot-integration", + "title": "Spring Boot Integration", + "depth": 1 + }, + { + "source": "/docs/java/spring-boot-integration#integration-configuration", + "title": "Integration Configuration", + "depth": 2 + }, + { + "source": "/docs/java/spring-boot-integration#integration-features", + "title": "Integration Features", + "depth": 2 + }, + { + "source": "/docs/java/spring-boot-integration#exposed-beans", + "title": "CDS Spring Beans", + "depth": 2 + }, + { + "source": "/docs/java/cap-plugins-in-spring-boot-apps", + "title": "Use CAP Plugins in Spring Boot Applications without a CDS Model", + "depth": 1 + }, + { + "source": "/docs/java/cap-plugins-in-spring-boot-apps#sap-audit-log-service", + "title": "SAP Audit Log Service", + "depth": 2 + }, + { + "source": "/docs/java/cap-plugins-in-spring-boot-apps#cap-messaging", + "title": "CAP Messaging", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/", + "title": "Developing CAP Java Applications", + "depth": 1 + }, + { + "source": "/docs/java/developing-applications/building", + "title": "Building Applications", + "depth": 1 + }, + { + "source": "/docs/java/developing-applications/building#modular-architecture", + "title": "Modular Stack Architecture", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/building#overview", + "title": "Overview", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#application-framework", + "title": "Application Framework", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#protocol-adapters", + "title": "Protocol Adapters", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#service-providers", + "title": "Service Providers", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#cqn-execution-engine", + "title": "CQN Execution Engine", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#application-features", + "title": "Application Features", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#stack-configuration", + "title": "Stack Configuration", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/building#module-dependencies", + "title": "Module Dependencies", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#standard-modules", + "title": "Standard Modules", + "depth": 4 + }, + { + "source": "/docs/java/developing-applications/building#starter-bundles", + "title": "Starter Bundles", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#the-maven-archetype", + "title": "Generating Projects with Maven", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/building#maven-build-options", + "title": "Building Projects with Maven", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/building#cds-maven-plugin", + "title": "CDS Maven Plugin", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#using-profiles", + "title": "Using profiles", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#codegen-config", + "title": "Code Generation for Typed Access", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/building#typed-results", + "title": "Typed Results", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#package-for-generated-code", + "title": "Package for Generated Code", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#filter-for-cds-entities", + "title": "Filter for CDS Entities", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#style-of-interfaces", + "title": "Style of Interfaces", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#code-generation-features", + "title": "Code Generation Features", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#annotation-detail-level", + "title": "Annotation Detail Level", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/building#using-a-local-cds-dk", + "title": "Using a Local cds-dk", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/building#migration-install-cdsdk", + "title": "Migrate From Goal `install-cdsdk` to `npm ci`", + "depth": 4 + }, + { + "source": "/docs/java/developing-applications/building#maintaining-cds-dk-in-packagejson-preferred", + "title": "Maintaining cds-dk in _package.json_ (preferred)", + "depth": 4 + }, + { + "source": "/docs/java/developing-applications/building#maintaining-cds-dk-in-pomxml-outdated", + "title": "Maintaining cds-dk in _pom.xml_ (outdated)", + "depth": 4 + }, + { + "source": "/docs/java/developing-applications/building#using-a-global-cds-dk", + "title": "Using a Global cds-dk", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/running", + "title": "Running Applications", + "depth": 1 + }, + { + "source": "/docs/java/developing-applications/running#use-cds-prefix-everywhere", + "title": "Use `cds` Prefix Everywhere", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/running#run-java-application-in-your-ide", + "title": "Run Java application in your IDE", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/running#cds-watch", + "title": "Run Java application with CDS Watch", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/running#multitenant-applications", + "title": "Multitenant Applications", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/running#debugging", + "title": "Debugging", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/running#spring-boot-devtools", + "title": "Spring Boot Devtools", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/testing", + "title": "Testing Applications", + "depth": 1 + }, + { + "source": "/docs/java/developing-applications/testing#sample-tests", + "title": "Sample Tests", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/testing#event-handler-layer-testing", + "title": "Event Handler Layer Testing", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/testing#service-layer-testing", + "title": "Service Layer Testing", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/testing#integration-testing", + "title": "Integration Testing", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/testing#using-mockmvc", + "title": "Using MockMvc", + "depth": 4 + }, + { + "source": "/docs/java/developing-applications/testing#using-resttestclient", + "title": "Using RestTestClient", + "depth": 4 + }, + { + "source": "/docs/java/developing-applications/testing#testing-with-h2", + "title": "Testing with H2", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/testing#setup--configuration", + "title": "Setup & Configuration", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/testing#using-the-maven-archetype", + "title": "Using the Maven Archetype", + "depth": 4 + }, + { + "source": "/docs/java/developing-applications/testing#manual-configuration", + "title": "Manual Configuration", + "depth": 4 + }, + { + "source": "/docs/java/developing-applications/testing#h2-limitations", + "title": "H2 Limitations", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/testing#hybrid-testing---a-way-to-overcome-limitations", + "title": "Hybrid Testing - a way to overcome limitations", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/testing#h2-and-spring-dev-tools-integration", + "title": "H2 and Spring Dev Tools Integration", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/testing#logging-sql-to-console", + "title": "Logging SQL to Console", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/configuring", + "title": "Configuring Applications", + "depth": 1 + }, + { + "source": "/docs/java/developing-applications/configuring#profiles-and-properties", + "title": "Profiles and Properties", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/configuring#production-profile", + "title": "Production Profile", + "depth": 3 + }, + { + "source": "/docs/java/developing-applications/configuring#buildpack", + "title": "Using SAP Java Buildpack", + "depth": 2 + }, + { + "source": "/docs/java/developing-applications/properties", + "title": "CDS Properties", + "depth": 1 + }, + { + "source": "/docs/java/operating-applications/", + "title": "Operating CAP Java Applications", + "depth": 1 + }, + { + "source": "/docs/java/operating-applications/optimizing", + "title": "Optimizing Applications", + "depth": 1 + }, + { + "source": "/docs/java/operating-applications/optimizing#profiling", + "title": "Profiling", + "depth": 2 + }, + { + "source": "/docs/java/operating-applications/optimizing#profiling-local", + "title": "Local Tools", + "depth": 3 + }, + { + "source": "/docs/java/operating-applications/optimizing#profiling-jmx", + "title": "Remote JMX-Based Tools", + "depth": 3 + }, + { + "source": "/docs/java/operating-applications/optimizing#graalvm-native-image-support-beta", + "title": "GraalVM Native Image Support ", + "depth": 2 + }, + { + "source": "/docs/java/operating-applications/observability", + "title": "Observability", + "depth": 1 + }, + { + "source": "/docs/java/operating-applications/observability#logging", + "title": "Logging", + "depth": 2 + }, + { + "source": "/docs/java/operating-applications/observability#logging-facade", + "title": "Logging Façade", + "depth": 3 + }, + { + "source": "/docs/java/operating-applications/observability#logging-api", + "title": "Logger API", + "depth": 3 + }, + { + "source": "/docs/java/operating-applications/observability#logging-configuration", + "title": "Spring Boot Logging", + "depth": 3 + }, + { + "source": "/docs/java/operating-applications/observability#logging-configuration-compiletime", + "title": "At Compile Time", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#logging-configuration-restart", + "title": "At Runtime with Restart", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#logging-configuration-runtime", + "title": "At Runtime Without Restart", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#predefined-loggers", + "title": "Predefined Loggers", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#log-cds-configuration", + "title": "Log CDS Configuration", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#logging-service", + "title": "Logging Service", + "depth": 3 + }, + { + "source": "/docs/java/operating-applications/observability#correlation-ids", + "title": "Correlation IDs", + "depth": 3 + }, + { + "source": "/docs/java/operating-applications/observability#jdbc-tracing-in-sap-hana", + "title": "JDBC Tracing in SAP Hana", + "depth": 3 + }, + { + "source": "/docs/java/operating-applications/observability#using-datasource-properties", + "title": "Using datasource properties", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#using-the-command-line", + "title": "Using the command line", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#on-kyma", + "title": "On Kyma", + "depth": 5 + }, + { + "source": "/docs/java/operating-applications/observability#monitoring", + "title": "Monitoring", + "depth": 2 + }, + { + "source": "/docs/java/operating-applications/observability#open-telemetry", + "title": "Open Telemetry", + "depth": 3 + }, + { + "source": "/docs/java/operating-applications/observability#agent-extension", + "title": "Configure Java Agent and Extension Library", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#open-telemetry-configuration-cls", + "title": "Configuration of Cloud Logging Service", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#open-telemetry-configuration-dynatrace", + "title": "Configuration of Dynatrace", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#cap-instrumentation", + "title": "CAP Instrumentation", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#custom-instrumentation", + "title": "Custom Instrumentation", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#dev-trace-output", + "title": "Dev Trace Output", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#example-output", + "title": "Example Output", + "depth": 5 + }, + { + "source": "/docs/java/operating-applications/observability#configuration", + "title": "Configuration", + "depth": 5 + }, + { + "source": "/docs/java/operating-applications/observability#relationship-to-the-opentelemetry-java-agent", + "title": "Relationship to the OpenTelemetry Java Agent", + "depth": 5 + }, + { + "source": "/docs/java/operating-applications/observability#dynatrace", + "title": "Dynatrace", + "depth": 3 + }, + { + "source": "/docs/java/operating-applications/observability#spring-boot-actuators", + "title": "Spring Boot Actuators", + "depth": 3 + }, + { + "source": "/docs/java/operating-applications/observability#cds-actuator", + "title": "CDS Actuator", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#custom-actuators", + "title": "Custom Actuators", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#availability", + "title": "Availability", + "depth": 3 + }, + { + "source": "/docs/java/operating-applications/observability#spring-health-checks", + "title": "Spring Boot Health Checks", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#custom-health-indicators", + "title": "Custom Health Indicators", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/observability#protected-health-checks", + "title": "Protected Health Checks", + "depth": 4 + }, + { + "source": "/docs/java/operating-applications/dashboard", + "title": "Developer Dashboard", + "depth": 1 + }, + { + "source": "/docs/java/operating-applications/dashboard#local-setup", + "title": "Local Setup", + "depth": 2 + }, + { + "source": "/docs/java/operating-applications/dashboard#cloud-setup", + "title": "Cloud Setup", + "depth": 2 + }, + { + "source": "/docs/java/operating-applications/dashboard#disable-authorization", + "title": "Disable Authorization", + "depth": 2 + }, + { + "source": "/docs/java/building-plugins", + "title": "Building Plugins", + "depth": 1 + }, + { + "source": "/docs/java/building-plugins#general-considerations", + "title": "General Considerations", + "depth": 2 + }, + { + "source": "/docs/java/building-plugins#java-version", + "title": "Java Version", + "depth": 3 + }, + { + "source": "/docs/java/building-plugins#maven-groupid-and-java-packages", + "title": "Maven GroupId and Java Packages", + "depth": 3 + }, + { + "source": "/docs/java/building-plugins#share-cds-models-via-maven-artifacts", + "title": "Share CDS Models via Maven Artifacts", + "depth": 2 + }, + { + "source": "/docs/java/building-plugins#create-the-cds-model-in-a-new-maven-artifact", + "title": "Create the CDS Model in a New Maven Artifact", + "depth": 3 + }, + { + "source": "/docs/java/building-plugins#reference-the-new-cds-model-in-an-existing-cap-java-project", + "title": "Reference the New CDS Model in an Existing CAP Java Project", + "depth": 3 + }, + { + "source": "/docs/java/building-plugins#event-handlers-for-custom-types-and-annotations", + "title": "Event Handlers for Custom Types and Annotations", + "depth": 2 + }, + { + "source": "/docs/java/building-plugins#service-loader", + "title": "Load Plugin Code via ServiceLoaders", + "depth": 3 + }, + { + "source": "/docs/java/building-plugins#spring-autoconfiguration", + "title": "Load Plugin Code with the Spring Component Model", + "depth": 3 + }, + { + "source": "/docs/java/building-plugins#protocol-adapter", + "title": "Custom Protocol Adapters", + "depth": 2 + }, + { + "source": "/docs/java/building-plugins#putting-it-all-together", + "title": "Putting It All Together", + "depth": 2 + }, + { + "source": "/docs/java/migration", + "title": "Migration Guides", + "depth": 1 + }, + { + "source": "/docs/java/migration#automatic-cap-java-migrations-with-openrewrite", + "title": "Automatic CAP Java Migrations with OpenRewrite", + "depth": 2 + }, + { + "source": "/docs/java/migration#running-openrewrite-recipes", + "title": "Running OpenRewrite Recipes", + "depth": 3 + }, + { + "source": "/docs/java/migration#currently-released-cap-java-migrations", + "title": "Currently Released CAP Java Migrations", + "depth": 3 + }, + { + "source": "/docs/java/migration#four-to-five", + "title": "CAP Java 4.9 to CAP Java 5.0", + "depth": 2 + }, + { + "source": "/docs/java/migration#spring-boot-4", + "title": "Spring Boot 4", + "depth": 3 + }, + { + "source": "/docs/java/migration#spring-boot-4-test-code", + "title": "Test Code Changes", + "depth": 4 + }, + { + "source": "/docs/java/migration#minimum-versions", + "title": "Minimum Versions", + "depth": 3 + }, + { + "source": "/docs/java/migration#adjusted-property-defaults", + "title": "Adjusted Property Defaults", + "depth": 3 + }, + { + "source": "/docs/java/migration#deprecated-properties", + "title": "Deprecated Properties", + "depth": 3 + }, + { + "source": "/docs/java/migration#removed-properties", + "title": "Removed Properties", + "depth": 3 + }, + { + "source": "/docs/java/migration#removed-java-apis-4-to-5", + "title": "Removed Java APIs", + "depth": 3 + }, + { + "source": "/docs/java/migration#changes-in-the-cds-maven-plugin", + "title": "Changes in the `cds-maven-plugin`", + "depth": 3 + }, + { + "source": "/docs/java/migration#minimum-maven-version", + "title": "Minimum Maven Version", + "depth": 4 + }, + { + "source": "/docs/java/migration#removed-deprecated-goal-install-cdsdk", + "title": "Removed Deprecated Goal `install-cdsdk`", + "depth": 4 + }, + { + "source": "/docs/java/migration#changes-in-goal-generate", + "title": "Changes in Goal `generate`", + "depth": 4 + }, + { + "source": "/docs/java/migration#changes-in-the-cds-services-archetype", + "title": "Changes in the `cds-services-archetype`", + "depth": 3 + }, + { + "source": "/docs/java/migration#default-jdk-version", + "title": "Default JDK Version", + "depth": 4 + }, + { + "source": "/docs/java/migration#removed-olingo-4-to-5", + "title": "Removed Repackaged Olingo Dependencies", + "depth": 3 + }, + { + "source": "/docs/java/migration#module-comsapcdscds4j-codegen-is-removed", + "title": "Module `com.sap.cds:cds4j-codegen` is Removed", + "depth": 4 + }, + { + "source": "/docs/java/migration#three-to-four", + "title": "CAP Java 3.10 to CAP Java 4.0", + "depth": 2 + }, + { + "source": "/docs/java/migration#new-license", + "title": "New License", + "depth": 3 + }, + { + "source": "/docs/java/migration#minimum-versions-1", + "title": "Minimum Versions", + "depth": 3 + }, + { + "source": "/docs/java/migration#removed-feature-cds-feature-event-hub", + "title": "Removed feature `cds-feature-event-hub`", + "depth": 3 + }, + { + "source": "/docs/java/migration#changes-in-goal-generate-of-the-cds-maven-plugin", + "title": "Changes in goal `generate` of the `cds-maven-plugin`", + "depth": 3 + }, + { + "source": "/docs/java/migration#removed-unstructured", + "title": "Removed unstructured messages from MessagingService", + "depth": 3 + }, + { + "source": "/docs/java/migration#adjusted-property-defaults-1", + "title": "Adjusted Property Defaults", + "depth": 3 + }, + { + "source": "/docs/java/migration#deprecated-properties-1", + "title": "Deprecated Properties", + "depth": 3 + }, + { + "source": "/docs/java/migration#removed-properties-1", + "title": "Removed Properties", + "depth": 3 + }, + { + "source": "/docs/java/migration#removed-java-apis", + "title": "Removed Java APIs", + "depth": 3 + }, + { + "source": "/docs/java/migration#two-to-three", + "title": "CAP Java 2.10 to CAP Java 3.0", + "depth": 2 + }, + { + "source": "/docs/java/migration#minimum-versions-2", + "title": "Minimum Versions", + "depth": 3 + }, + { + "source": "/docs/java/migration#production-profile-cloud", + "title": "Production Profile `cloud`", + "depth": 3 + }, + { + "source": "/docs/java/migration#removed-mtx-classic-support", + "title": "Removed MTX Classic Support", + "depth": 3 + }, + { + "source": "/docs/java/migration#removed-feature-cds-feature-xsuaa", + "title": "Removed feature `cds-feature-xsuaa`", + "depth": 3 + }, + { + "source": "/docs/java/migration#proof-of-possession-enforced-for-ias-based-authentication", + "title": "Proof-Of-Possession enforced for IAS-based authentication", + "depth": 3 + }, + { + "source": "/docs/java/migration#lazy-localization-by-default", + "title": "Lazy Localization by default", + "depth": 3 + }, + { + "source": "/docs/java/migration#star-expand-and-inline-all-are-no-longer-permitted", + "title": "Star-expand and inline-all are no longer permitted", + "depth": 3 + }, + { + "source": "/docs/java/migration#adjusted-pojo-class-generation", + "title": "Adjusted POJO class generation", + "depth": 3 + }, + { + "source": "/docs/java/migration#adjusted-property-defaults-2", + "title": "Adjusted Property Defaults", + "depth": 3 + }, + { + "source": "/docs/java/migration#adjusted-property-behavior", + "title": "Adjusted Property Behavior", + "depth": 3 + }, + { + "source": "/docs/java/migration#removed-properties-2", + "title": "Removed Properties", + "depth": 3 + }, + { + "source": "/docs/java/migration#deprecated-session-context-variables", + "title": "Deprecated Session Context Variables", + "depth": 3 + }, + { + "source": "/docs/java/migration#removed-java-apis-1", + "title": "Removed Java APIs", + "depth": 3 + }, + { + "source": "/docs/java/migration#removed-goals-in-cds-maven-plugin", + "title": "Removed goals in `cds-maven-plugin`", + "depth": 3 + }, + { + "source": "/docs/java/migration#cloudsdk5", + "title": "Cloud SDK 4 to 5", + "depth": 2 + }, + { + "source": "/docs/java/migration#one-to-two", + "title": "CAP Java 1.34 to CAP Java 2.0", + "depth": 2 + }, + { + "source": "/docs/java/migration#spring-boot-3", + "title": "Spring Boot 3", + "depth": 3 + }, + { + "source": "/docs/java/migration#java-17", + "title": "Java 17", + "depth": 4 + }, + { + "source": "/docs/java/migration#jakarta-ee-10", + "title": "Jakarta EE 10", + "depth": 4 + }, + { + "source": "/docs/java/migration#spring-security", + "title": "Spring Security", + "depth": 4 + }, + { + "source": "/docs/java/migration#minimum-dependency-versions", + "title": "Minimum Dependency Versions", + "depth": 3 + }, + { + "source": "/docs/java/migration#api-cleanup", + "title": "API Cleanup", + "depth": 3 + }, + { + "source": "/docs/java/migration#legacy-upsert", + "title": "Legacy Upsert", + "depth": 4 + }, + { + "source": "/docs/java/migration#limit", + "title": "Representation of Pagination", + "depth": 4 + }, + { + "source": "/docs/java/migration#modification", + "title": "Statement Modification", + "depth": 4 + }, + { + "source": "/docs/java/migration#removal-of-deprecated-cqnmodifier", + "title": "Removal of Deprecated CqnModifier", + "depth": 5 + }, + { + "source": "/docs/java/migration#modifier", + "title": "Removal of Deprecated Methods in Modifier", + "depth": 5 + }, + { + "source": "/docs/java/migration#overview-of-removed-interfaces-and-methods", + "title": "Removed Interfaces and Methods Overview", + "depth": 3 + }, + { + "source": "/docs/java/migration#comsapcds", + "title": "com.sap.cds", + "depth": 4 + }, + { + "source": "/docs/java/migration#comsapcdsql", + "title": "com.sap.cds.ql", + "depth": 4 + }, + { + "source": "/docs/java/migration#comsapcdsqlcqn", + "title": "com.sap.cds.ql.cqn", + "depth": 4 + }, + { + "source": "/docs/java/migration#comsapcdsreflect", + "title": "com.sap.cds.reflect", + "depth": 4 + }, + { + "source": "/docs/java/migration#comsapcdsservices", + "title": "com.sap.cds.services", + "depth": 4 + }, + { + "source": "/docs/java/migration#comsapcdsservicescds", + "title": "com.sap.cds.services.cds", + "depth": 4 + }, + { + "source": "/docs/java/migration#comsapcdsservicesenvironment", + "title": "com.sap.cds.services.environment", + "depth": 4 + }, + { + "source": "/docs/java/migration#interface-servicebinding", + "title": "Interface `ServiceBinding`", + "depth": 5 + }, + { + "source": "/docs/java/migration#comsapcdsserviceshandler", + "title": "com.sap.cds.services.handler", + "depth": 4 + }, + { + "source": "/docs/java/migration#interface-eventpredicate", + "title": "Interface `EventPredicate`", + "depth": 4 + }, + { + "source": "/docs/java/migration#comsapcdsservicesmessages", + "title": "com.sap.cds.services.messages", + "depth": 4 + }, + { + "source": "/docs/java/migration#comsapcdsservicespersistence", + "title": "com.sap.cds.services.persistence", + "depth": 4 + }, + { + "source": "/docs/java/migration#comsapcdsservicesrequest", + "title": "com.sap.cds.services.request", + "depth": 4 + }, + { + "source": "/docs/java/migration#comsapcdsservicesruntime", + "title": "com.sap.cds.services.runtime", + "depth": 4 + }, + { + "source": "/docs/java/migration#method-cdsruntimeruninrequestcontextrequest-functionconsumer", + "title": "Method `CdsRuntime.runInRequestContext(Request, Function|Consumer)`", + "depth": 4 + }, + { + "source": "/docs/java/migration#overview-of-removed-cds-properties", + "title": "Overview of Removed CDS Properties", + "depth": 4 + }, + { + "source": "/docs/java/migration#removed-annotations-overview", + "title": "Removed Annotations Overview", + "depth": 3 + }, + { + "source": "/docs/java/migration#changed-behavior", + "title": "Changed Behavior", + "depth": 3 + }, + { + "source": "/docs/java/migration#immutable-values", + "title": "Immutable Values", + "depth": 4 + }, + { + "source": "/docs/java/migration#immutable-references", + "title": "Immutable References", + "depth": 4 + }, + { + "source": "/docs/java/migration#--set-alias-or-type", + "title": "- Set alias or type", + "depth": 5 + }, + { + "source": "/docs/java/migration#--modify-ref-segments", + "title": "- Modify ref segments", + "depth": 5 + }, + { + "source": "/docs/java/migration#null-values-in-cds-ql-query-results", + "title": "Null Values in CDS QL Query Results", + "depth": 4 + }, + { + "source": "/docs/java/migration#result-of-updates-without-matching-entity", + "title": "Result of Updates Without Matching Entity", + "depth": 4 + }, + { + "source": "/docs/java/migration#provider-tenant-normalization", + "title": "Provider Tenant Normalization", + "depth": 4 + }, + { + "source": "/docs/java/migration#lean-draft", + "title": "Lean Draft", + "depth": 3 + }, + { + "source": "/docs/java/migration#changes-to-maven-plugins", + "title": "Changes to Maven Plugins", + "depth": 3 + }, + { + "source": "/docs/java/migration#cds-maven-plugin", + "title": "cds-maven-plugin", + "depth": 4 + }, + { + "source": "/docs/java/migration#cds4j-maven-plugin", + "title": "cds4j-maven-plugin", + "depth": 4 + }, + { + "source": "/docs/java/migration#classic-mtx-to-streamlined-mtx", + "title": "Classic MTX to Streamlined MTX", + "depth": 2 + }, + { + "source": "/docs/java/migration#cap-java-classic-to-cap-java-1x", + "title": "CAP Java Classic to CAP Java 1.x", + "depth": 2 + }, + { + "source": "/docs/java/migration#odata-protocol-version", + "title": "OData Protocol Version", + "depth": 3 + }, + { + "source": "/docs/java/migration#migrate-the-project-structure", + "title": "Migrate the Project Structure", + "depth": 3 + }, + { + "source": "/docs/java/migration#copy-the-cds-model", + "title": "Copy the CDS Model", + "depth": 3 + }, + { + "source": "/docs/java/migration#cds-configuration", + "title": "CDS Configuration", + "depth": 4 + }, + { + "source": "/docs/java/migration#first-build-and-deployment", + "title": "First Build and Deployment", + "depth": 4 + }, + { + "source": "/docs/java/migration#migrate-java-business-logic", + "title": "Migrate Java Business Logic", + "depth": 3 + }, + { + "source": "/docs/java/migration#migrate-dependencies", + "title": "Migrate Dependencies", + "depth": 4 + }, + { + "source": "/docs/java/migration#migrate-event-handlers", + "title": "Migrate Event Handlers", + "depth": 4 + }, + { + "source": "/docs/java/migration#annotations", + "title": "Annotations", + "depth": 5 + }, + { + "source": "/docs/java/migration#event-handler-signatures", + "title": "Event Handler Signatures", + "depth": 5 + }, + { + "source": "/docs/java/migration#delete-obsolete-files", + "title": "Delete Obsolete Files", + "depth": 3 + }, + { + "source": "/docs/java/migration#transaction-hooks", + "title": "Transaction Hooks", + "depth": 3 + }, + { + "source": "/docs/java/migration#security-settings", + "title": "Security Settings", + "depth": 3 + }, + { + "source": "/docs/java/migration#configuration-and-dependencies", + "title": "Configuration and Dependencies", + "depth": 4 + }, + { + "source": "/docs/java/migration#spring-boot", + "title": "Spring Boot", + "depth": 5 + }, + { + "source": "/docs/java/migration#plain-java", + "title": "Plain Java", + "depth": 5 + }, + { + "source": "/docs/java/migration#enforcement-api--custom-handlers", + "title": "Enforcement API & Custom Handlers", + "depth": 4 + }, + { + "source": "/docs/java/migration#data-access-and-manipulation", + "title": "Data Access and Manipulation", + "depth": 3 + }, + { + "source": "/docs/java/migration#access-application-service-in-custom-handler-and-query-execution", + "title": "Access Application Service in Custom Handler and Query Execution", + "depth": 4 + }, + { + "source": "/docs/java/migration#accessing-persistenceservice", + "title": "Accessing `PersistenceService`", + "depth": 4 + }, + { + "source": "/docs/java/migration#accessing-cdsdatastore", + "title": "Accessing `CdsDataStore`", + "depth": 4 + }, + { + "source": "/docs/java/migration#v2adapter", + "title": "CDS OData V2 Adapter", + "depth": 3 + }, + { + "source": "/docs/java/migration#enabling-odata-v2-and-v4-in-parallel", + "title": "Enabling OData V2 and V4 in Parallel", + "depth": 4 + }, + { + "source": "/docs/tools/", + "title": "Choose Your Preferred Tools", + "depth": 1 + }, + { + "source": "/docs/tools/cds-cli", + "title": "CDS Command Line Interface (CLI)", + "depth": 1 + }, + { + "source": "/docs/tools/cds-cli#cds-version", + "title": "cds version", + "depth": 2 + }, + { + "source": "/docs/tools/cds-cli#cds-completion", + "title": "cds completion ", + "depth": 2 + }, + { + "source": "/docs/tools/cds-cli#cds-help", + "title": "cds help", + "depth": 2 + }, + { + "source": "/docs/tools/cds-cli#cds-init", + "title": "cds init", + "depth": 2 + }, + { + "source": "/docs/tools/cds-cli#cds-add", + "title": "cds add", + "depth": 2 + }, + { + "source": "/docs/tools/cds-cli#sample", + "title": "sample", + "depth": 3 + }, + { + "source": "/docs/tools/cds-cli#tiny-sample", + "title": "tiny-sample", + "depth": 3 + }, + { + "source": "/docs/tools/cds-cli#data", + "title": "data", + "depth": 3 + }, + { + "source": "/docs/tools/cds-cli#data-filtering", + "title": "Filtering ", + "depth": 4 + }, + { + "source": "/docs/tools/cds-cli#sample-records", + "title": "Sample records ", + "depth": 4 + }, + { + "source": "/docs/tools/cds-cli#formats", + "title": "Formats ", + "depth": 4 + }, + { + "source": "/docs/tools/cds-cli#interactively-in-vs-code", + "title": "Interactively in VS Code ", + "depth": 4 + }, + { + "source": "/docs/tools/cds-cli#http", + "title": "http ", + "depth": 3 + }, + { + "source": "/docs/tools/cds-cli#http-filtering", + "title": "Filtering", + "depth": 4 + }, + { + "source": "/docs/tools/cds-cli#interactively-in-vs-code-1", + "title": "Interactively in VS Code", + "depth": 4 + }, + { + "source": "/docs/tools/cds-cli#authentication--authorization", + "title": "Authentication / Authorization", + "depth": 4 + }, + { + "source": "/docs/tools/cds-cli#--to-local-applications", + "title": "-> To local applications", + "depth": 5 + }, + { + "source": "/docs/tools/cds-cli#--to-remote-applications", + "title": "-> To remote applications", + "depth": 5 + }, + { + "source": "/docs/tools/cds-cli#handler", + "title": "handler ", + "depth": 3 + }, + { + "source": "/docs/tools/cds-cli#handler-filtering", + "title": "Filtering", + "depth": 4 + }, + { + "source": "/docs/tools/cds-cli#cds-env", + "title": "cds env", + "depth": 2 + }, + { + "source": "/docs/tools/cds-cli#cds-compile", + "title": "cds compile", + "depth": 2 + }, + { + "source": "/docs/tools/cds-cli#mermaid", + "title": "mermaid ", + "depth": 3 + }, + { + "source": "/docs/tools/cds-cli#interactively-in-vs-code-2", + "title": "Interactively in VS Code", + "depth": 4 + }, + { + "source": "/docs/tools/cds-cli#cds-export", + "title": "cds export", + "depth": 2 + }, + { + "source": "/docs/tools/cds-cli#cds-watch", + "title": "cds watch", + "depth": 2 + }, + { + "source": "/docs/tools/cds-cli#includes-and-excludes", + "title": "Includes and Excludes ", + "depth": 3 + }, + { + "source": "/docs/tools/cds-cli#cds-repl", + "title": "cds repl", + "depth": 2 + }, + { + "source": "/docs/tools/cds-cli#cds-debug", + "title": "Debugging with `cds debug`", + "depth": 2 + }, + { + "source": "/docs/tools/cds-cli#nodejs-applications", + "title": "Node.js Applications", + "depth": 3 + }, + { + "source": "/docs/tools/cds-cli#remote-applications", + "title": "Remote Applications", + "depth": 4 + }, + { + "source": "/docs/tools/cds-cli#local-applications", + "title": "Local Applications", + "depth": 4 + }, + { + "source": "/docs/tools/cds-cli#java-applications", + "title": "Java Applications ", + "depth": 3 + }, + { + "source": "/docs/tools/cds-cli#remote-applications-1", + "title": "Remote Applications", + "depth": 4 + }, + { + "source": "/docs/tools/cds-cli#local-applications-1", + "title": "Local Applications", + "depth": 4 + }, + { + "source": "/docs/tools/cds-cli#debugging-with-cds-watch", + "title": "Debugging with `cds watch`", + "depth": 2 + }, + { + "source": "/docs/tools/cds-cli#cds-upgrade", + "title": "cds upgrade ", + "depth": 2 + }, + { + "source": "/docs/tools/cds-bind", + "title": "Hybrid Testing", + "depth": 1 + }, + { + "source": "/docs/tools/cds-bind#bind-to-cloud-services", + "title": "Bind to Cloud Services", + "depth": 2 + }, + { + "source": "/docs/tools/cds-bind#services-on-cloud-foundry", + "title": "Services on Cloud Foundry", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#binding-user-provided-services", + "title": "User-Provided Services on Cloud Foundry", + "depth": 4 + }, + { + "source": "/docs/tools/cds-bind#binding-shared-service-instances", + "title": "Shared Service Instances on Cloud Foundry ", + "depth": 4 + }, + { + "source": "/docs/tools/cds-bind#services-on-kubernetes", + "title": "Services on Kubernetes", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#bind-to-kubernetes-service-bindings", + "title": "Bind to Kubernetes Service Bindings", + "depth": 4 + }, + { + "source": "/docs/tools/cds-bind#bind-to-kubernetes-secrets", + "title": "Bind to Kubernetes Secrets", + "depth": 4 + }, + { + "source": "/docs/tools/cds-bind#run-with-service-bindings", + "title": "Run with Service Bindings", + "depth": 2 + }, + { + "source": "/docs/tools/cds-bind#node", + "title": "Run CAP Node.js Apps with Service Bindings", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#run-arbitrary-commands-with-service-bindings", + "title": "Run Arbitrary Commands with Service Bindings", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#run-cap-java-apps-with-service-bindings", + "title": "Run CAP Java Apps with Service Bindings", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#bindings-from-a-cloud-application", + "title": "Bindings from a Cloud Application", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#cds-bind-usage", + "title": "`cds bind` Usage", + "depth": 2 + }, + { + "source": "/docs/tools/cds-bind#by-cloud-service-only", + "title": "By Cloud Service Only", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#with-different-profile", + "title": "With different profile", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#with-cds-service-and-kind", + "title": "With CDS Service and Kind", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#bind-multiple-services-with-one-command", + "title": "Bind Multiple Services with One Command", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#overwriting-service-credentials", + "title": "Overwrite Cloud Service Credentials", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#with-profile-and-output-file", + "title": "With Profile and Output File", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#cds-bind-exec", + "title": "Execute Commands with Bindings", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#use-cases", + "title": "Use Cases", + "depth": 2 + }, + { + "source": "/docs/tools/cds-bind#destinations", + "title": "Destinations", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#authentication-and-authorization", + "title": "Authentication and Authorization", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#integration-tests", + "title": "Integration Tests", + "depth": 3 + }, + { + "source": "/docs/tools/cds-bind#local-cds-bind---exec", + "title": "Local: `cds bind --exec`", + "depth": 4 + }, + { + "source": "/docs/tools/cds-bind#cicd-pipelines-export-resolved-bindings", + "title": "CI/CD Pipelines: Export Resolved Bindings", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors", + "title": "CDS Editors and IDEs", + "depth": 1 + }, + { + "source": "/docs/tools/cds-editors#vscode", + "title": "Visual Studio Code", + "depth": 2 + }, + { + "source": "/docs/tools/cds-editors#install-vscode", + "title": "Install Visual Studio Code", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#add-cds-editor", + "title": "Add CDS Editor", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#add-useful-plugins", + "title": "Add Useful Plugins", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#run-services", + "title": "Run Services", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#debug-services", + "title": "Debug Services", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#restart-the-server", + "title": "Restart the Server", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#intellij", + "title": "IntelliJ", + "depth": 2 + }, + { + "source": "/docs/tools/cds-editors#cds-editor", + "title": "CDS Editors & LSP", + "depth": 2 + }, + { + "source": "/docs/tools/cds-editors#features-and-functions", + "title": "Features and Functions", + "depth": 3 + }, + { + "source": "/docs/tools/cds-editors#syntax-coloring--code-completion", + "title": "Syntax Coloring & Code Completion", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#snippets", + "title": "Snippets", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#code-formatting", + "title": "Code Formatting", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#hover-information", + "title": "Hover Information", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#where-used-navigation", + "title": "Where-used Navigation", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#quick-fixes", + "title": "Quick Fixes", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#translation-support", + "title": "Translation Support", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#and-more", + "title": "And More…", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#settings", + "title": "Settings", + "depth": 3 + }, + { + "source": "/docs/tools/cds-editors#code-formatting-1", + "title": "Code formatting", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#format-on-type-format-on-paste-and-format-on-save-in-vs-code", + "title": "Format on Type, Format on Paste, and Format on Save in VS Code", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#cds-workspace-validation-mode", + "title": "Cds: [Workspace Validation Mode](vscode://settings/cds.workspaceValidationMode)", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#cds--contributions--enablement-odata", + "title": "Cds > Contributions > [Enablement: Odata](vscode://settings/cds.contributions.enablement.odata)", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#cds--workspace-scancsn", + "title": "Cds > [Workspace: ScanCsn](vscode://settings/cds.workspace.scanCsn)", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#cds--quickfix-importartifact", + "title": "Cds > [Quickfix: ImportArtifact](vscode://settings/cds.quickfix.importArtifact)", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#commands", + "title": "Commands", + "depth": 3 + }, + { + "source": "/docs/tools/cds-editors#welcome-page", + "title": "Welcome page", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#cap-notebooks-page", + "title": "CAP Notebooks Page", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#beautify-settings", + "title": "Beautify settings", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#preview-cds-sources", + "title": "Preview CDS sources", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#visualize-cds-file-dependencies", + "title": "Visualize CDS file dependencies", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#editor-performance", + "title": "Editor Performance", + "depth": 3 + }, + { + "source": "/docs/tools/cds-editors#editor--goto-location-alternative-definition-command", + "title": "Editor > Goto Location: Alternative Definition Command", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#workbench--editor--limit-value", + "title": "Workbench > Editor > Limit: Value", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#workbench--editor--limit-enabled", + "title": "Workbench > Editor > Limit: Enabled", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#additional-hints-to-increase-performance", + "title": "Additional Hints to Increase Performance:", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#cds-formatter", + "title": "CDS Source Formatter", + "depth": 3 + }, + { + "source": "/docs/tools/cds-editors#installation", + "title": "Installation", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#usage", + "title": "Usage", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#github-integration", + "title": "GitHub Integration", + "depth": 3 + }, + { + "source": "/docs/tools/cds-editors#cap-project-explorer", + "title": "CAP Project Explorer", + "depth": 2 + }, + { + "source": "/docs/tools/cds-editors#getting-started", + "title": "Getting Started", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#configuration", + "title": "Configuration", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#available-commands", + "title": "Available Commands", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#benefits", + "title": "Benefits", + "depth": 5 + }, + { + "source": "/docs/tools/cds-editors#docker", + "title": "Using Docker", + "depth": 2 + }, + { + "source": "/docs/tools/cds-editors#build-an-image", + "title": "Build an Image", + "depth": 4 + }, + { + "source": "/docs/tools/cds-editors#run-a-service-in-a-container", + "title": "Run a Service in a Container", + "depth": 4 + }, + { + "source": "/docs/tools/cds-typer", + "title": "CDS Typer", + "depth": 1 + }, + { + "source": "/docs/tools/cds-typer#cds-typer-vscode", + "title": "Quickstart using VS Code", + "depth": 2 + }, + { + "source": "/docs/tools/cds-typer#using-emitted-types-in-your-service", + "title": "Using Emitted Types in Your Service", + "depth": 2 + }, + { + "source": "/docs/tools/cds-typer#cql", + "title": "CQL", + "depth": 3 + }, + { + "source": "/docs/tools/cds-typer#crud-handlers", + "title": "CRUD Handlers", + "depth": 3 + }, + { + "source": "/docs/tools/cds-typer#actions", + "title": "Actions", + "depth": 3 + }, + { + "source": "/docs/tools/cds-typer#enums", + "title": "Enums", + "depth": 3 + }, + { + "source": "/docs/tools/cds-typer#handling-optional-properties", + "title": "Handling Optional Properties", + "depth": 3 + }, + { + "source": "/docs/tools/cds-typer#fine-tuning", + "title": "Fine Tuning", + "depth": 2 + }, + { + "source": "/docs/tools/cds-typer#singular-plural", + "title": "Singular/ Plural", + "depth": 3 + }, + { + "source": "/docs/tools/cds-typer#strict-property-checks-in-javascript-projects", + "title": "Strict Property Checks in JavaScript Projects", + "depth": 3 + }, + { + "source": "/docs/tools/cds-typer#usage-options", + "title": "Usage Options", + "depth": 2 + }, + { + "source": "/docs/tools/cds-typer#typer-cli", + "title": "Command Line Interface (CLI)", + "depth": 3 + }, + { + "source": "/docs/tools/cds-typer#configuration", + "title": "Configuration", + "depth": 3 + }, + { + "source": "/docs/tools/cds-typer#version-control", + "title": "Version Control", + "depth": 3 + }, + { + "source": "/docs/tools/cds-typer#integrate-into-typescript-projects", + "title": "Integrate Into TypeScript Projects", + "depth": 2 + }, + { + "source": "/docs/tools/cds-typer#integrate-into-your-ci", + "title": "Integrate Into Your CI", + "depth": 2 + }, + { + "source": "/docs/tools/cds-typer#integrate-into-your-build-process", + "title": "Integrate Into Your Build Process", + "depth": 2 + }, + { + "source": "/docs/tools/cds-typer#typer-facet", + "title": "About The Facet", + "depth": 2 + }, + { + "source": "/docs/tools/cds-typer#emitted-type-files", + "title": "About the Emitted Type Files", + "depth": 2 + }, + { + "source": "/docs/tools/cds-typer#subpath-imports", + "title": "Subpath Imports", + "depth": 2 + }, + { + "source": "/docs/tools/cds-typer#typer-top-level-imports", + "title": "Static Top-Level Imports ", + "depth": 3 + }, + { + "source": "/docs/tools/apis/", + "title": "CDS Design Time APIs", + "depth": 1 + }, + { + "source": "/docs/tools/apis/#install-sapcds-dk", + "title": "Install `@sap/cds-dk`", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-add", + "title": "Plugins for `cds add`", + "depth": 1 + }, + { + "source": "/docs/tools/apis/cds-add#built-in", + "title": "Built-in", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-add#create-a-plugin-from-scratch", + "title": "Create a Plugin from Scratch", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-add#example-cds-add-postgres", + "title": "Example: `cds add postgres`", + "depth": 3 + }, + { + "source": "/docs/tools/apis/cds-add#call-cds-add-for-an-npm-package", + "title": "Call `cds add` for an NPM package ", + "depth": 4 + }, + { + "source": "/docs/tools/apis/cds-add#plugin-api", + "title": "Plugin API", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-add#registername-impl", + "title": "`register(name, impl)`", + "depth": 3 + }, + { + "source": "/docs/tools/apis/cds-add#run", + "title": "`run()`", + "depth": 3 + }, + { + "source": "/docs/tools/apis/cds-add#combine", + "title": "`combine()`", + "depth": 3 + }, + { + "source": "/docs/tools/apis/cds-add#options", + "title": "`options()`", + "depth": 3 + }, + { + "source": "/docs/tools/apis/cds-add#requires", + "title": "`requires()`", + "depth": 3 + }, + { + "source": "/docs/tools/apis/cds-add#utilities-api", + "title": "Utilities API", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-add#readproject", + "title": "`readProject()`", + "depth": 3 + }, + { + "source": "/docs/tools/apis/cds-add#mergefromintofile-o", + "title": "`merge(from).into(file, o?)`", + "depth": 3 + }, + { + "source": "/docs/tools/apis/cds-add#registries", + "title": "`.registries`", + "depth": 3 + }, + { + "source": "/docs/tools/apis/cds-add#mvnadd", + "title": "`mvn.add()`", + "depth": 3 + }, + { + "source": "/docs/tools/apis/cds-add#checklist-for-production", + "title": "Checklist for Production", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-add#best-practices", + "title": "Best Practices", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-add#consider-cds-add-vs-cds-build", + "title": "Consider `cds add` vs `cds build`", + "depth": 3 + }, + { + "source": "/docs/tools/apis/cds-add#dont-do-too-much-work-in-cds-add", + "title": "Don't do too much work in `cds add`", + "depth": 3 + }, + { + "source": "/docs/tools/apis/cds-add#embrace-out-of-the-box", + "title": "Embrace out-of-the-box", + "depth": 3 + }, + { + "source": "/docs/tools/apis/cds-add#embrace-grow-as-you-go-and-separate-concerns", + "title": "Embrace grow-as-you-go and separate concerns", + "depth": 3 + }, + { + "source": "/docs/tools/apis/cds-import", + "title": "CDS Import API", + "depth": 1 + }, + { + "source": "/docs/tools/apis/cds-import#cdsimport", + "title": "cds.import()", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-import#arguments", + "title": "Arguments:", + "depth": 5 + }, + { + "source": "/docs/tools/apis/cds-import#optionskeepnamespace", + "title": "options.keepNamespace", + "depth": 4 + }, + { + "source": "/docs/tools/apis/cds-import#optionsincludenamespaces", + "title": "options.includeNamespaces", + "depth": 4 + }, + { + "source": "/docs/tools/apis/cds-import#cdsimportfromedmx", + "title": "cds.import.from.edmx()", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-import#cdsimportfromopenapi", + "title": "cds.import.from.openapi()", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-import#cdsimportfromasyncapi", + "title": "cds.import.from.asyncapi()", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-import#odata-type-mappings", + "title": "OData Type Mappings", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-import#openapi-to-cds-odata-csn-conversion-mapping", + "title": "OpenAPI to CDS (OData CSN) Conversion Mapping", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-build", + "title": "Implement Build Plugins ", + "depth": 1 + }, + { + "source": "/docs/tools/apis/cds-build#add-build-logic", + "title": "Add Build Logic", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-build#add-build-task-type-to-cds-schema", + "title": "Add build task type to cds schema ", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-build#write-build-output", + "title": "Write Build Output", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-build#handle-errors", + "title": "Handle Errors", + "depth": 2 + }, + { + "source": "/docs/tools/apis/cds-build#run-the-plugin", + "title": "Run the Plugin", + "depth": 2 + }, + { + "source": "/docs/plugins/", + "title": "CAP Plugins & Enhancements", + "depth": 1 + }, + { + "source": "/docs/plugins/#as-cds-plugins-for-nodejs", + "title": "As _cds-plugins_ for Node.js", + "depth": 2 + }, + { + "source": "/docs/plugins/#as-plugin-for-cap-java", + "title": "As Plugin for CAP Java", + "depth": 2 + }, + { + "source": "/docs/plugins/#support-for-plugins", + "title": "Support for Plugins", + "depth": 2 + }, + { + "source": "/docs/plugins/#odata-v2-proxy", + "title": "OData V2 Adapter", + "depth": 2 + }, + { + "source": "/docs/plugins/#websocket", + "title": "WebSocket", + "depth": 2 + }, + { + "source": "/docs/plugins/#ui5-dev-server", + "title": "UI5 Dev Server", + "depth": 2 + }, + { + "source": "/docs/plugins/#graphql-adapter", + "title": "GraphQL Adapter", + "depth": 2 + }, + { + "source": "/docs/plugins/#attachments", + "title": "Attachments", + "depth": 2 + }, + { + "source": "/docs/plugins/#@cap-js/sdm", + "title": "SAP Document Management Service", + "depth": 2 + }, + { + "source": "/docs/plugins/#audit-logging", + "title": "Audit Logging", + "depth": 2 + }, + { + "source": "/docs/plugins/#data-privacy", + "title": "Data Privacy ", + "depth": 2 + }, + { + "source": "/docs/plugins/#change-tracking", + "title": "Change Tracking", + "depth": 2 + }, + { + "source": "/docs/plugins/#notifications", + "title": "Notifications", + "depth": 2 + }, + { + "source": "/docs/plugins/#telemetry", + "title": "Telemetry", + "depth": 2 + }, + { + "source": "/docs/plugins/#ord-open-resource-discovery", + "title": "ORD (Open Resource Discovery)", + "depth": 2 + }, + { + "source": "/docs/plugins/#cap-operator-plugin", + "title": "CAP Operator for Kubernetes", + "depth": 2 + }, + { + "source": "/docs/plugins/#event-hub", + "title": "SAP Cloud Application Event Hub", + "depth": 2 + }, + { + "source": "/docs/plugins/#advanced-event-mesh", + "title": "SAP Integration Suite, Advanced Event Mesh", + "depth": 2 + }, + { + "source": "/docs/plugins/#abap-rfc", + "title": "ABAP RFC", + "depth": 2 + }, + { + "source": "/docs/plugins/#data-inspector", + "title": "Data Inspector", + "depth": 2 + }, + { + "source": "/docs/plugins/#ai", + "title": "AI", + "depth": 2 + }, + { + "source": "/docs/releases/", + "title": "CAP Releases", + "depth": 1 + }, + { + "source": "/docs/releases/#release-schedule", + "title": "Release Schedule", + "depth": 2 + }, + { + "source": "/docs/releases/#major-versions", + "title": "Major Versions", + "depth": 6 + }, + { + "source": "/docs/releases/#status-badges", + "title": "Status Badges", + "depth": 2 + }, + { + "source": "/docs/releases/#see-also", + "title": "See Also", + "depth": 2 + }, + { + "source": "/docs/releases/schedule", + "title": "CAP Release Schedule", + "depth": 1 + }, + { + "source": "/docs/releases/schedule#yearly-major-releases", + "title": "Major Releases", + "depth": 2 + }, + { + "source": "/docs/releases/schedule#cap-nodejs", + "title": "CAP Node.js", + "depth": 3 + }, + { + "source": "/docs/releases/schedule#cap-java", + "title": "CAP Java", + "depth": 3 + }, + { + "source": "/docs/releases/schedule#minor", + "title": "Monthly Minor Releases", + "depth": 2 + }, + { + "source": "/docs/releases/schedule#patch", + "title": "Patch Releases", + "depth": 2 + }, + { + "source": "/docs/releases/schedule#active", + "title": "Active Release Status", + "depth": 2 + }, + { + "source": "/docs/releases/schedule#maintenance-status", + "title": "Maintenance Status", + "depth": 2 + }, + { + "source": "/docs/releases/schedule#end-of-life-status", + "title": "End of Life Status", + "depth": 2 + }, + { + "source": "/docs/releases/schedule#adoption-strategy", + "title": "Adoption Strategy", + "depth": 2 + }, + { + "source": "/docs/releases/2026/jun26", + "title": "June 2026", + "depth": 1 + }, + { + "source": "/docs/releases/2026/jun26#major-release-upgrade", + "title": "Major Release Upgrade", + "depth": 2 + }, + { + "source": "/docs/releases/2026/jun26#upgrade-nodejs--jdk", + "title": "Upgrade Node.js & JDK", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#new-migration-guides", + "title": "**New** Migration Guides", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#new-cds-upgrade", + "title": "**New** `cds upgrade` ", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#highlights", + "title": "Highlights", + "depth": 2 + }, + { + "source": "/docs/releases/2026/jun26#aggregates-in-hierarchies", + "title": "Aggregates in Hierarchies ", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#new-cap-project-explorer", + "title": "**New** CAP Project Explorer ", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#new-hcql-protocol-adapter", + "title": "**New** HCQL Protocol Adapter ", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#new-mcp-protocol-adapter", + "title": "**New** MCP Protocol Adapter ", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#new-cap-level-agents", + "title": "**New** CAP-level Agents ", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#new-cap-skills-library", + "title": "**New** CAP Skills Library ", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#new-ai-core-plugin", + "title": "**New** AI Core Plugin", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#new-data-inspector-plugin", + "title": "**New** Data Inspector Plugin", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#new-data-privacy-plugin", + "title": "**New** Data Privacy Plugin ", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#new-event-mesh-support", + "title": "**New** Event Mesh Support ", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#event-queues-evolution", + "title": "Event Queues Evolution", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#new-guide", + "title": "New Guide", + "depth": 4 + }, + { + "source": "/docs/releases/2026/jun26#scheduling-api", + "title": "Scheduling API", + "depth": 4 + }, + { + "source": "/docs/releases/2026/jun26#more-efficient-processing", + "title": "More Efficient Processing", + "depth": 4 + }, + { + "source": "/docs/releases/2026/jun26#cds-export-ga", + "title": "`cds export` GA", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#cap-nodejs", + "title": "CAP Node.js", + "depth": 2 + }, + { + "source": "/docs/releases/2026/jun26#consolidated-service-apis", + "title": "Consolidated Service APIs", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#bypass-drafts-by-default", + "title": "Bypass Drafts by Default", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#going-for-vitest--esm", + "title": "Going for Vitest & ESM", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#going-native", + "title": "Going Native...", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#telemetry-plugin-v2", + "title": "Telemetry Plugin v2", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#cap-java", + "title": "CAP Java", + "depth": 2 + }, + { + "source": "/docs/releases/2026/jun26#important-changes-in-java", + "title": "Important Changes ❗️", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#major-dependency-updates", + "title": "Major Dependency Updates", + "depth": 4 + }, + { + "source": "/docs/releases/2026/jun26#upgrade-support-with-openrewrite-recipes", + "title": "Upgrade Support with OpenRewrite Recipes", + "depth": 4 + }, + { + "source": "/docs/releases/2026/jun26#changes-to-cds-properties", + "title": "Changes to CDS Properties", + "depth": 4 + }, + { + "source": "/docs/releases/2026/jun26#api-changes", + "title": "API Changes", + "depth": 4 + }, + { + "source": "/docs/releases/2026/jun26#spring-boot", + "title": "Spring Boot 4", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#jdk25", + "title": "JDK 25", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#harmonized-search", + "title": "Harmonized Search", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#search-syntax", + "title": "Search Syntax", + "depth": 4 + }, + { + "source": "/docs/releases/2026/jun26#fuzzy-search", + "title": "Fuzzy Search", + "depth": 4 + }, + { + "source": "/docs/releases/2026/jun26#faster-typed-data-access", + "title": "Faster Typed Data Access", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#built-in-odata-processing", + "title": "Built-in OData Processing", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#draft-new-action", + "title": "Draft New Action", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#miscellaneous", + "title": "Miscellaneous", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jun26#mtx-services", + "title": "MTX Services", + "depth": 2 + }, + { + "source": "/docs/releases/2026/jun26#stricter-annotation-validation-for-extensions", + "title": "Stricter Annotation Validation for Extensions", + "depth": 4 + }, + { + "source": "/docs/releases/2026/jun26#migration-support-from-sapcds-mtx-to-sapcds-mtxs-removed", + "title": "Migration Support from `@sap/cds-mtx` to `@sap/cds-mtxs` Removed", + "depth": 4 + }, + { + "source": "/docs/releases/2026/apr26", + "title": "CAP Release April 26", + "depth": 1 + }, + { + "source": "/docs/releases/2026/apr26#preparing-for-cds-10", + "title": "Preparing for cds 10", + "depth": 2 + }, + { + "source": "/docs/releases/2026/apr26#cap-nodejs", + "title": "CAP Node.js", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#nodejs", + "title": "node.js", + "depth": 6 + }, + { + "source": "/docs/releases/2026/apr26#flags-with-changed-defaults", + "title": "Flags with Changed Defaults", + "depth": 4 + }, + { + "source": "/docs/releases/2026/apr26#flags-entirely-removed", + "title": "Flags Entirely Removed", + "depth": 4 + }, + { + "source": "/docs/releases/2026/apr26#are-you-affected", + "title": "Are you affected?", + "depth": 4 + }, + { + "source": "/docs/releases/2026/apr26#try-it-now", + "title": "Try it _now_", + "depth": 4 + }, + { + "source": "/docs/releases/2026/apr26#cap-java", + "title": "CAP Java", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#java", + "title": "java", + "depth": 6 + }, + { + "source": "/docs/releases/2026/apr26#cds-language", + "title": "CDS Language", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#annotate-with-invalid-target", + "title": "Annotate with invalid target", + "depth": 4 + }, + { + "source": "/docs/releases/2026/apr26#vector-embeddings", + "title": "Vector Embeddings", + "depth": 2 + }, + { + "source": "/docs/releases/2026/apr26#now-also-for-h2-sqlite-", + "title": "Now also for H2, SQLite, ...", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#new-vector-embedding-function", + "title": "New _Vector Embedding_ Function ", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#cds-language-1", + "title": "CDS Language", + "depth": 2 + }, + { + "source": "/docs/releases/2026/apr26#declarative-constraints-ga", + "title": "Declarative Constraints GA", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#extending-views-with-cql-clauses", + "title": "Extending Views with CQL Clauses", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#extending-derived-enums", + "title": "Extending Derived Enums", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#cap-nodejs-1", + "title": "CAP Node.js", + "depth": 2 + }, + { + "source": "/docs/releases/2026/apr26#new-cds-test-version-10", + "title": "New _cds test_ Version 1.0", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#upgraded-to-chai-6", + "title": "Upgraded to Chai 6", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#featuring-vitest", + "title": "Featuring Vitest", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#node-native-fetch-api", + "title": "Node-native Fetch API", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#no-cloud-sdk", + "title": "N/o Cloud SDK", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#cap-java-1", + "title": "CAP Java", + "depth": 2 + }, + { + "source": "/docs/releases/2026/apr26#important-changes-in-java", + "title": "Important Changes ❗️", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#new-datetime-functions", + "title": "New `date`/`time` Functions", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#protocol-configuration", + "title": "Protocol Configuration", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#brownfield-projects", + "title": "'Brownfield' Projects", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#optimised-draft-deletion", + "title": "Optimised Draft Deletion", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#miscellaneous", + "title": "Miscellaneous", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#cap-plugins", + "title": "CAP Plugins", + "depth": 2 + }, + { + "source": "/docs/releases/2026/apr26#change-tracking-v2", + "title": "Change Tracking v2", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#performance-benefits", + "title": "Performance Benefits", + "depth": 4 + }, + { + "source": "/docs/releases/2026/apr26#tree-table-visualization", + "title": "Tree Table Visualization", + "depth": 4 + }, + { + "source": "/docs/releases/2026/apr26#cds-expression-language-in-annotations", + "title": "CDS Expression Language in Annotations", + "depth": 4 + }, + { + "source": "/docs/releases/2026/apr26#further-improvements", + "title": "Further Improvements", + "depth": 4 + }, + { + "source": "/docs/releases/2026/apr26#schema-changes-and-migration", + "title": "Schema Changes and Migration", + "depth": 4 + }, + { + "source": "/docs/releases/2026/apr26#attachments", + "title": "Attachments", + "depth": 3 + }, + { + "source": "/docs/releases/2026/apr26#cds-feature-attachments-java", + "title": "cds-feature-attachments (Java)", + "depth": 4 + }, + { + "source": "/docs/releases/2026/apr26#cap-jsattachments-nodejs", + "title": "@cap-js/attachments (Node.js)", + "depth": 4 + }, + { + "source": "/docs/releases/2026/apr26#new-print-service", + "title": "**New:** Print Service", + "depth": 3 + }, + { + "source": "/docs/releases/2026/feb26", + "title": "February 2026", + "depth": 1 + }, + { + "source": "/docs/releases/2026/feb26#live-queries-in-documentation", + "title": "Live Queries in Documentation", + "depth": 2 + }, + { + "source": "/docs/releases/2026/feb26#nodejs", + "title": "Node.js", + "depth": 2 + }, + { + "source": "/docs/releases/2026/feb26#parallel-gets-in-batch", + "title": "Parallel `GET`s in `$batch`", + "depth": 3 + }, + { + "source": "/docs/releases/2026/feb26#calculated-elements-for-drafts", + "title": "Calculated Elements for Drafts", + "depth": 3 + }, + { + "source": "/docs/releases/2026/feb26#native-sqlite-support", + "title": "Native SQLite Support ", + "depth": 3 + }, + { + "source": "/docs/releases/2026/feb26#java", + "title": "Java", + "depth": 2 + }, + { + "source": "/docs/releases/2026/feb26#important-changes-in-java", + "title": "Important Change ❗️", + "depth": 3 + }, + { + "source": "/docs/releases/2026/feb26#performance-improvements", + "title": "Performance Improvements", + "depth": 3 + }, + { + "source": "/docs/releases/2026/feb26#tools", + "title": "Tools", + "depth": 2 + }, + { + "source": "/docs/releases/2026/feb26#query-mode-in-cds-repl", + "title": "Query Mode in `cds repl`", + "depth": 3 + }, + { + "source": "/docs/releases/2026/feb26#support-for-eslint-10", + "title": "Support for ESLint 10", + "depth": 3 + }, + { + "source": "/docs/releases/2026/feb26#mta-extensions-with-cds-up", + "title": "MTA Extensions with `cds up`", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26", + "title": "January 2026", + "depth": 1 + }, + { + "source": "/docs/releases/2026/jan26#capire-renovations", + "title": "Capire Renovations", + "depth": 2 + }, + { + "source": "/docs/releases/2026/jan26#getting-started-guides", + "title": "Getting Started Guides", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#databases-guides", + "title": "Databases Guides", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#integration-guides", + "title": "Integration Guides", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#security--data-privacy", + "title": "Security & Data Privacy", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#cds-expression-language", + "title": "CDS Expression Language", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#nodejs", + "title": "Node.js", + "depth": 2 + }, + { + "source": "/docs/releases/2026/jan26#support-for-express5", + "title": "Support for `express^5`", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#initial-data-folders-configuration", + "title": "Initial Data Folders Configuration", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#new-cdsqlclone-method", + "title": "New `cds.ql.clone()` Method", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#java", + "title": "Java", + "depth": 2 + }, + { + "source": "/docs/releases/2026/jan26#important-changes-in-java", + "title": "Important Changes ❗️", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#relaxed-path-based-inserts", + "title": "Relaxed Path-based Inserts", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#miscellaneous", + "title": "Miscellaneous", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#cds-editor", + "title": "CDS Editor", + "depth": 2 + }, + { + "source": "/docs/releases/2026/jan26#tools", + "title": "tools", + "depth": 6 + }, + { + "source": "/docs/releases/2026/jan26#multi-cursor-for-selection-ranges", + "title": "Multi-cursor for Selection Ranges", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#folding-ranges-improvements", + "title": "Folding Ranges Improvements", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#faster-references-search-and-workspace-symbols", + "title": "Faster References Search and Workspace Symbols ", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#user-settings-with-category-groups", + "title": "User Settings with Category Groups", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#kyma", + "title": "Kyma", + "depth": 2 + }, + { + "source": "/docs/releases/2026/jan26#multitenancy-upgrades-for-kyma", + "title": "Multitenancy Upgrades for Kyma", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#cds-debug---k8s", + "title": "`cds debug --k8s`", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#capire-updates", + "title": "Capire Updates", + "depth": 2 + }, + { + "source": "/docs/releases/2026/jan26#exclude-elements-with-cdsjavaignore", + "title": "Exclude Elements with `@cds.java.ignore`", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#enhanced-cds-deploy---to-hana-documentation", + "title": "Enhanced `cds deploy --to hana` Documentation", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#runtime-flag-for-sample-projects", + "title": "Runtime Flag for Sample Projects", + "depth": 3 + }, + { + "source": "/docs/releases/2026/jan26#enhanced-java-plugin-development-guide", + "title": "Enhanced Java Plugin Development Guide", + "depth": 3 + }, + { + "source": "/docs/releases/2025/dec25", + "title": "December 2025", + "depth": 1 + }, + { + "source": "/docs/releases/2025/dec25#status-transition-flows", + "title": "Status-Transition Flows ", + "depth": 2 + }, + { + "source": "/docs/releases/2025/dec25#declarative-constraints", + "title": "Declarative Constraints ", + "depth": 2 + }, + { + "source": "/docs/releases/2025/dec25#new-hierarchy-annotation", + "title": "New `@hierarchy` Annotation", + "depth": 2 + }, + { + "source": "/docs/releases/2025/dec25#new-cds-export", + "title": "New `cds export` ", + "depth": 2 + }, + { + "source": "/docs/releases/2025/dec25#cds", + "title": "CDS Language & Compiler", + "depth": 2 + }, + { + "source": "/docs/releases/2025/dec25#loading-from-app-subfolders", + "title": "Loading from `app/*` subfolders", + "depth": 3 + }, + { + "source": "/docs/releases/2025/dec25#enums-in-annotation-expressions", + "title": "Enums in Annotation Expressions", + "depth": 3 + }, + { + "source": "/docs/releases/2025/dec25#cds-js", + "title": "Node.js", + "depth": 2 + }, + { + "source": "/docs/releases/2025/dec25#direct-crud-on-draft-enabled-entities", + "title": "Direct CRUD on Draft-enabled Entities ", + "depth": 3 + }, + { + "source": "/docs/releases/2025/dec25#improved-event-queue-processing", + "title": "Improved Event Queue Processing", + "depth": 3 + }, + { + "source": "/docs/releases/2025/dec25#cleaned-up-model-reflection-apis", + "title": "Cleaned-up Model Reflection APIs", + "depth": 3 + }, + { + "source": "/docs/releases/2025/dec25#cds-java", + "title": "Java", + "depth": 2 + }, + { + "source": "/docs/releases/2025/dec25#important-changes-in-java", + "title": "Important Changes ❗️", + "depth": 3 + }, + { + "source": "/docs/releases/2025/dec25#deep-updates-for-draft", + "title": "Deep Updates for Draft", + "depth": 3 + }, + { + "source": "/docs/releases/2025/dec25#miscellaneous", + "title": "Miscellaneous", + "depth": 3 + }, + { + "source": "/docs/releases/2025/dec25#api-for-aliasing-cqnvalue", + "title": "API for Aliasing CqnValue", + "depth": 4 + }, + { + "source": "/docs/releases/2025/dec25#tools", + "title": "Tools", + "depth": 2 + }, + { + "source": "/docs/releases/2025/dec25#cds-up-convenience-for-kyma", + "title": "`cds up` Convenience for Kyma", + "depth": 3 + }, + { + "source": "/docs/releases/2025/dec25#plugins", + "title": "Plugins", + "depth": 2 + }, + { + "source": "/docs/releases/2025/dec25#sapcds-rfc", + "title": "@sap/cds-rfc", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25", + "title": "November 2025", + "depth": 1 + }, + { + "source": "/docs/releases/2025/nov25#status-transition-flows", + "title": "Status-Transition Flows ", + "depth": 2 + }, + { + "source": "/docs/releases/2025/nov25#cds", + "title": "CDS Language & Compiler", + "depth": 2 + }, + { + "source": "/docs/releases/2025/nov25#security-annotations", + "title": "Security Annotations", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#cds-js", + "title": "Node.js", + "depth": 2 + }, + { + "source": "/docs/releases/2025/nov25#compute-in-odata-v4", + "title": "`$compute` in OData V4 ", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#cds-java", + "title": "Java", + "depth": 2 + }, + { + "source": "/docs/releases/2025/nov25#important-changes-java", + "title": "Important Changes ❗️", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#numeric-type-promotion", + "title": "Numeric Type Promotion", + "depth": 4 + }, + { + "source": "/docs/releases/2025/nov25#calculated-elements-for-drafts", + "title": "Calculated Elements for Drafts", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#outbox-improvements", + "title": "Outbox Improvements", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#processing-strategy", + "title": "Processing Strategy", + "depth": 4 + }, + { + "source": "/docs/releases/2025/nov25#locking", + "title": "Locking", + "depth": 4 + }, + { + "source": "/docs/releases/2025/nov25#observability", + "title": "Observability", + "depth": 4 + }, + { + "source": "/docs/releases/2025/nov25#support-for-cds-data-in-spring-rest-controllers", + "title": "Support for CDS Data in Spring REST Controllers", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#cds-map-enhancements", + "title": "CDS Map Enhancements", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#search-in-sub-elements-of-map", + "title": "Search in Sub-Elements of Map ", + "depth": 4 + }, + { + "source": "/docs/releases/2025/nov25#static-builder-methods-to-access-sub-elements-of-map", + "title": "Static Builder Methods to Access Sub-Elements of Map", + "depth": 4 + }, + { + "source": "/docs/releases/2025/nov25#tree-views-on-sqlite", + "title": "Tree Views on SQLite", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#avoiding-transactions-for-select", + "title": "Avoiding Transactions for Select", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#protocol-annotations", + "title": "Protocol Annotations", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#plugins", + "title": "Plugins", + "depth": 2 + }, + { + "source": "/docs/releases/2025/nov25#cap-jsattachments-enhancements", + "title": "`cap-js/attachments` Enhancements", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#important-changes-in-cap-js-attachments", + "title": "Important Fixes", + "depth": 4 + }, + { + "source": "/docs/releases/2025/nov25#all-hyper-scaler-object-stores-are-now-supported", + "title": "All Hyper-Scaler Object Stores Are Now Supported", + "depth": 4 + }, + { + "source": "/docs/releases/2025/nov25#malware-scanning-improvements", + "title": "Malware Scanning Improvements", + "depth": 4 + }, + { + "source": "/docs/releases/2025/nov25#localization", + "title": "Localization", + "depth": 4 + }, + { + "source": "/docs/releases/2025/nov25#dynamically-hiding-the-ui-section", + "title": "Dynamically Hiding the UI Section", + "depth": 4 + }, + { + "source": "/docs/releases/2025/nov25#tools", + "title": "Tools", + "depth": 2 + }, + { + "source": "/docs/releases/2025/nov25#simplified-kyma-project-setup", + "title": "Simplified Kyma Project Setup", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#github-actions-for-java-projects", + "title": "GitHub Actions for Java Projects", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#command-line-formatter-allows-piping", + "title": "Command-Line Formatter Allows Piping", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#capire-updates", + "title": "capire Updates", + "depth": 2 + }, + { + "source": "/docs/releases/2025/nov25#improved-localized-data-documentation", + "title": "Improved Localized Data Documentation", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#draft-protection-configuration-update", + "title": "Draft Protection Configuration Update", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#odata-hierarchy-vocabulary-support", + "title": "OData Hierarchy Vocabulary Support", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#draft-aggregation-queries", + "title": "Draft Aggregation Queries", + "depth": 3 + }, + { + "source": "/docs/releases/2025/nov25#misc", + "title": "Misc", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25", + "title": "September 2025", + "depth": 1 + }, + { + "source": "/docs/releases/2025/sep25#cds-js", + "title": "Node.js", + "depth": 2 + }, + { + "source": "/docs/releases/2025/sep25#translated-error-messages", + "title": "Translated Error Messages", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#simplified-support-for-streaming", + "title": "Simplified Support for Streaming", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#revised-fiori-support", + "title": "Revised Fiori Support", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#combined-input-validation", + "title": "Combined Input Validation", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#cds-java", + "title": "Java", + "depth": 2 + }, + { + "source": "/docs/releases/2025/sep25#jdk-25-compliance", + "title": "JDK 25 Compliance", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#cds-services-archetype", + "title": "cds-services-archetype", + "depth": 4 + }, + { + "source": "/docs/releases/2025/sep25#aggregating-over-associations", + "title": "Aggregating over Associations ", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#streamlined-cds-models-from-maven-dependencies", + "title": "Streamlined CDS Models from Maven Dependencies", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#miscellaneous", + "title": "Miscellaneous", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#streamlined-ordering-of-security-configs", + "title": "Streamlined Ordering of Security Configs", + "depth": 4 + }, + { + "source": "/docs/releases/2025/sep25#setting-application-ui-url-programmatically", + "title": "Setting Application UI URL Programmatically", + "depth": 4 + }, + { + "source": "/docs/releases/2025/sep25#disable-draft-garbage-collection-for-entity", + "title": "Disable Draft Garbage Collection for Entity", + "depth": 4 + }, + { + "source": "/docs/releases/2025/sep25#simplified-bound-action-calls-in-odata", + "title": "Simplified Bound Action Calls in OData", + "depth": 4 + }, + { + "source": "/docs/releases/2025/sep25#performance", + "title": "Performance", + "depth": 4 + }, + { + "source": "/docs/releases/2025/sep25#tools", + "title": "Tools", + "depth": 2 + }, + { + "source": "/docs/releases/2025/sep25#important-changes-in-tools", + "title": "Important Changes ❗️", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#edmstring-now-becomes-cds-string-in-cds-import", + "title": "`Edm.String` now Becomes CDS `String` in `cds import`", + "depth": 4 + }, + { + "source": "/docs/releases/2025/sep25#cds-add-app-frontend", + "title": "`cds add app-frontend`", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#annotations-with-better-editor-support", + "title": "Annotations with Better Editor Support", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#sap-integration-suite-advanced-event-mesh-ga", + "title": "SAP Integration Suite, Advanced Event Mesh GA", + "depth": 2 + }, + { + "source": "/docs/releases/2025/sep25#capire-updates", + "title": "capire Updates", + "depth": 2 + }, + { + "source": "/docs/releases/2025/sep25#java-cql-query-api-enhancements", + "title": "Java CQL Query API Enhancements", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#java-security-configuration-improvements", + "title": "Java Security Configuration Improvements", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#outbox-pattern-with-shared-database", + "title": "Outbox Pattern with Shared Database", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#extensibility-and-composition-improvements", + "title": "Extensibility and Composition Improvements", + "depth": 3 + }, + { + "source": "/docs/releases/2025/sep25#and-more", + "title": "...and more", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25", + "title": "August 2025", + "depth": 1 + }, + { + "source": "/docs/releases/2025/aug25#new-capire-docs--samples", + "title": "New capire docs & samples", + "depth": 2 + }, + { + "source": "/docs/releases/2025/aug25#continuous-deployments", + "title": "Continuous Deployments", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#github-packages", + "title": "GitHub Packages", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#github-discussions", + "title": "GitHub Discussions", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#new--general-available", + "title": "New & General Available", + "depth": 2 + }, + { + "source": "/docs/releases/2025/aug25#fiori-draft-messages", + "title": "Fiori Draft Messages", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#cap-mcp-server", + "title": "CAP MCP Server", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#cds-language--compiler", + "title": "CDS Language & Compiler", + "depth": 2 + }, + { + "source": "/docs/releases/2025/aug25#cxl", + "title": "Expressions in Annotations GA", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#optimized-path-expressions", + "title": "Optimized Path Expressions", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#auto-coerced-associations", + "title": "Auto-coerced Associations", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#nodejs", + "title": "Node.js", + "depth": 2 + }, + { + "source": "/docs/releases/2025/aug25#search-by-commontext", + "title": "Search by `@Common.Text`", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#streaming-query-results", + "title": "Streaming Query Results", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#cdsrequires-wo-kind", + "title": "`cds.requires` w/o `kind`", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#cdsconnecttourl", + "title": "`cds.connect.to()`", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#cdslinkedcsncollect", + "title": "`cds.linked(csn).collect()`", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#cdsuserauthinfo", + "title": "`cds.User.authInfo`", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#java", + "title": "Java", + "depth": 2 + }, + { + "source": "/docs/releases/2025/aug25#typed-query-results", + "title": "Typed Query Results", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#attachments-in-object-store", + "title": "Attachments in Object Store ", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#code-generator-documentation", + "title": "Code Generator Documentation", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#link-to-index-page", + "title": "Link to Index Page", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#tools", + "title": "Tools", + "depth": 2 + }, + { + "source": "/docs/releases/2025/aug25#intellij-community-support", + "title": "IntelliJ Community Support", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#comprehensive-ide-features", + "title": "Comprehensive IDE Features", + "depth": 4 + }, + { + "source": "/docs/releases/2025/aug25#enhanced-lsp-integration", + "title": "Enhanced LSP Integration", + "depth": 4 + }, + { + "source": "/docs/releases/2025/aug25#configuration--settings", + "title": "Configuration & Settings", + "depth": 4 + }, + { + "source": "/docs/releases/2025/aug25#eslint-checks-for-javascript", + "title": "ESLint Checks for JavaScript", + "depth": 3 + }, + { + "source": "/docs/releases/2025/aug25#github-actions", + "title": "GitHub Actions Guides & Samples", + "depth": 2 + }, + { + "source": "/docs/releases/2025/jul25", + "title": "July 2025", + "depth": 1 + }, + { + "source": "/docs/releases/2025/jul25#customize-validation-messages", + "title": "Customize Validation Messages", + "depth": 2 + }, + { + "source": "/docs/releases/2025/jul25#fiori-tree-views-ga", + "title": "Fiori Tree Views GA", + "depth": 2 + }, + { + "source": "/docs/releases/2025/jul25#cds-js", + "title": "Node.js", + "depth": 2 + }, + { + "source": "/docs/releases/2025/jul25#improved-documentation", + "title": "Improved Documentation", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jul25#xsuaa-fallback-in-ias-auth", + "title": "XSUAA Fallback in IAS Auth", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jul25#token-caching", + "title": "Token Caching", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jul25#numeric-values-in-csv-files", + "title": "Numeric Values in .csv Files", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jul25#cds-java", + "title": "Java", + "depth": 2 + }, + { + "source": "/docs/releases/2025/jul25#generic-exception-handling-for-resultsingle", + "title": "Generic Exception Handling for `Result.single`", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jul25#simplified-api-of-draftservice", + "title": "Simplified API of DraftService", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jul25#restrictions-on-expand", + "title": "Restrictions on `$expand`", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jul25#tools", + "title": "Tools", + "depth": 2 + }, + { + "source": "/docs/releases/2025/jul25#go-to-implementations-experimental", + "title": "Go to Implementations (Experimental)", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jul25#formatting-parameter-lists", + "title": "Formatting Parameter Lists", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jul25#cds-add-github-actions", + "title": "`cds add github-actions`", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jun25", + "title": "June 2025", + "depth": 1 + }, + { + "source": "/docs/releases/2025/jun25#cds-js", + "title": "Node.js", + "depth": 2 + }, + { + "source": "/docs/releases/2025/jun25#hierarchy-maintenance-in-tree-views", + "title": "Hierarchy Maintenance in Tree Views ", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jun25#ui5-state-messages-for-drafts", + "title": "UI5 State Messages for Drafts ", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jun25#cds-java", + "title": "Java", + "depth": 2 + }, + { + "source": "/docs/releases/2025/jun25#important-changes-in-java", + "title": "Important Changes ❗️", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jun25#add-devdependency-to-sapcds-mtxs", + "title": "Add DevDependency to `@sap/cds-mtxs`", + "depth": 4 + }, + { + "source": "/docs/releases/2025/jun25#tree-views-w-h2-database", + "title": "Tree Views w/ H2 Database ", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jun25#event-handler-enhancements", + "title": "Event Handler Enhancements", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jun25#returning-arbitrary-types", + "title": "Returning Arbitrary Types", + "depth": 4 + }, + { + "source": "/docs/releases/2025/jun25#accessing-the-service", + "title": "Accessing the Service", + "depth": 4 + }, + { + "source": "/docs/releases/2025/jun25#typed-entity-references", + "title": "Typed Entity References", + "depth": 4 + }, + { + "source": "/docs/releases/2025/jun25#bringing-it-all-together", + "title": "Bringing It All Together", + "depth": 4 + }, + { + "source": "/docs/releases/2025/jun25#media-data-in-actions-and-functions", + "title": "Media Data in Actions and Functions", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jun25#media-elements-in-remote-odata", + "title": "Media Elements in Remote OData", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jun25#tools", + "title": "Tools", + "depth": 2 + }, + { + "source": "/docs/releases/2025/jun25#intellij-community-edition-supported-by-cds-plugin", + "title": "IntelliJ Community Edition Supported by CDS Plugin", + "depth": 3 + }, + { + "source": "/docs/releases/2025/jun25#faster-editor-feedback-in-vs-code", + "title": "Faster Editor Feedback in VS Code", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25", + "title": "May 2025", + "depth": 1 + }, + { + "source": "/docs/releases/2025/may25#new-major-versions", + "title": "New Major Versions", + "depth": 2 + }, + { + "source": "/docs/releases/2025/may25#cap-nodejs-v9", + "title": "CAP Node.js v9", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#cap-java-v4", + "title": "CAP Java v4", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#migration", + "title": "Migration", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#new-license", + "title": "New License", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#tree-views", + "title": "Hierarchical Tree Views, beta 2 ", + "depth": 2 + }, + { + "source": "/docs/releases/2025/may25#consolidated-configuration", + "title": "Consolidated Configuration", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#1-configure-the-treetable-in-ui5s-manifestjson-for-example", + "title": "1. Configure the TreeTable in UI5's _manifest.json_, for example:", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#2-annotateextend-the-entity-in-the-service-as-follows", + "title": "2. Annotate/extend the entity in the service as follows:", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#support-for-sqlite-and-postgresql", + "title": "Support for SQLite and PostgreSQL", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#task-queues", + "title": "Transactional Event Queues ", + "depth": 2 + }, + { + "source": "/docs/releases/2025/may25#in-nodejs", + "title": "In Node.js", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#event-scheduling", + "title": "Event Scheduling ", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#event-callbacks", + "title": "Event Callbacks ", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#more-efficient-locking", + "title": "More Efficient Locking ", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#in-java", + "title": "In Java", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#inbox", + "title": "Inbox", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#enabled-by-default", + "title": "Enabled By Default", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#cds", + "title": "CDS Language & Compiler", + "depth": 2 + }, + { + "source": "/docs/releases/2025/may25#new-parser-for-cdl", + "title": "New Parser for CDL", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#agnostic-database-functions", + "title": "Agnostic Database Functions", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#consistent-operators", + "title": "Consistent Operators", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#miscellaneous", + "title": "Miscellaneous", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#nesting-definitions-when-compiling-to-cdl", + "title": "Nesting Definitions when Compiling to CDL", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#generated-entities-and-cdspersistencejournal", + "title": "Generated Entities and `@cds.persistence.journal`", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#syntax-cleanup", + "title": "Syntax Cleanup", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#changes-in-cds", + "title": "Important Changes ❗️", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#unspecified-assoc", + "title": "Association to many w/o `ON` Conditions", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#virtual-elements-in-views", + "title": "Virtual Elements in Views", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#now-is-transaction-time", + "title": "$now is Transaction Time", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#doc-comments-are-not-propagated", + "title": "Doc Comments are not Propagated", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#databases", + "title": "Databases", + "depth": 2 + }, + { + "source": "/docs/releases/2025/may25#sap-hana-using-alter-table-add-column", + "title": "SAP HANA using `ALTER TABLE ADD COLUMN`", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#skipped-native-associations-for-sap-hana", + "title": "Skipped Native Associations for SAP HANA", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#removed-hdbcds-format", + "title": "Removed `hdbcds` Format", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#cds-js", + "title": "Node.js", + "depth": 2 + }, + { + "source": "/docs/releases/2025/may25#improved-error-handling", + "title": "Improved Error Handling", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#tree-views-w-sqlite-postgres", + "title": "Tree Views w/ SQLite, Postgres ", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#new-database-services-v2", + "title": "New Database Services v2", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#assumptions-for-unique-constraints", + "title": "Assumptions for Unique Constraints", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#generic-pool", + "title": "Opt-in Replacement for Generic-Pool ", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#open-sourced-cdstest", + "title": "Open-Sourced `cds.test`", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#removed-features", + "title": "Removed Features", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#changes-in-node-js", + "title": "Important Changes ❗️", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#changed-structure-of-reqparams", + "title": "Changed Structure of `req.params`", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#service-level-restrictions", + "title": "Service Level Restrictions", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#no-fallback-to-default-language-for-technical-apis", + "title": "No Fallback to Default Language for Technical APIs", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#revised-handling-of-put-requests", + "title": "Revised Handling of PUT Requests", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#cds-java", + "title": "Java", + "depth": 2 + }, + { + "source": "/docs/releases/2025/may25#important-changes-in-java", + "title": "Important Changes ❗️", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#tools", + "title": "Tools", + "depth": 2 + }, + { + "source": "/docs/releases/2025/may25#richer-tooltips-in-cds-editor", + "title": "Richer Tooltips in CDS Editor", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#compact-formatting-of-case-expressions", + "title": "Compact Formatting of `case` Expressions", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#tools-misc", + "title": "Miscellaneous", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#eslint-9-required", + "title": "ESLint 9 Required", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#removed-legacy-build-configuration", + "title": "Removed Legacy Build Configuration", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#cds-mtxs", + "title": "Multitenancy", + "depth": 2 + }, + { + "source": "/docs/releases/2025/may25#changes-in-multitenancy", + "title": "Important Changes ❗️", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#configuration", + "title": "Configuration", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#java-setup", + "title": "Java Setup", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#extensibility", + "title": "Extensibility", + "depth": 4 + }, + { + "source": "/docs/releases/2025/may25#cap-plugins", + "title": "CAP Plugins", + "depth": 2 + }, + { + "source": "/docs/releases/2025/may25#eh", + "title": "SAP Cloud Application Event Hub", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#aem", + "title": "SAP Integration Suite, Advanced Event Mesh ", + "depth": 3 + }, + { + "source": "/docs/releases/2025/may25#microservices-with-cap", + "title": "Microservices with CAP", + "depth": 2 + }, + { + "source": "/docs/releases/migration/cds10", + "title": "Migrating to cds 10", + "depth": 1 + }, + { + "source": "/docs/releases/migration/cds10#cds--improved-checks", + "title": "CDS – Improved Checks", + "depth": 2 + }, + { + "source": "/docs/releases/migration/cds10#annotations-without-targets", + "title": "Annotations Without Targets", + "depth": 3 + }, + { + "source": "/docs/releases/migration/cds10#invalid-defaults-for-structs", + "title": "Invalid Defaults for Structs", + "depth": 3 + }, + { + "source": "/docs/releases/migration/cds10#duplicate-elements", + "title": "Duplicate Elements", + "depth": 3 + }, + { + "source": "/docs/releases/migration/cds10#keys-not-propagated-into-types", + "title": "Keys Not Propagated into Types", + "depth": 3 + }, + { + "source": "/docs/releases/migration/cds10#potentially-breaking-fixes", + "title": "Potentially Breaking Fixes", + "depth": 2 + }, + { + "source": "/docs/releases/migration/cds10#decimals--int64-as-strings", + "title": "Decimals & Int64 as Strings", + "depth": 3 + }, + { + "source": "/docs/releases/migration/cds10#bypass-drafts-by-default", + "title": "Bypass Drafts by Default", + "depth": 3 + }, + { + "source": "/docs/releases/migration/cds10#fixed-service-results", + "title": "Fixed Service Results", + "depth": 3 + }, + { + "source": "/docs/releases/migration/cds10#opt-in--kill-switches", + "title": "Opt-in & Kill Switches", + "depth": 4 + }, + { + "source": "/docs/releases/migration/cds10#fixed-srventities", + "title": "Fixed `srv.entities()`", + "depth": 3 + }, + { + "source": "/docs/releases/migration/cds10#fixed-cdsqlclone", + "title": "Fixed `cds.ql.clone()`", + "depth": 3 + }, + { + "source": "/docs/releases/migration/cds10#are-you-affected", + "title": "Are you affected?", + "depth": 4 + }, + { + "source": "/docs/releases/migration/cds10#how-to-address", + "title": "How to address?", + "depth": 4 + }, + { + "source": "/docs/releases/migration/cds10#kill-switch", + "title": "Kill Switch", + "depth": 4 + }, + { + "source": "/docs/releases/migration/cds10#fixed-bulk-inserts-via-rest", + "title": "Fixed Bulk Inserts via REST", + "depth": 3 + }, + { + "source": "/docs/releases/migration/cds10#are-you-affected-1", + "title": "Are you affected?", + "depth": 4 + }, + { + "source": "/docs/releases/migration/cds10#how-to-address-1", + "title": "How to address?", + "depth": 4 + }, + { + "source": "/docs/releases/migration/cds10#kill-switch-1", + "title": "Kill Switch", + "depth": 4 + }, + { + "source": "/docs/releases/migration/cds10#non-breaking-changes", + "title": "Non-Breaking Changes", + "depth": 2 + }, + { + "source": "/docs/releases/migration/cds10#node-native-fetch-api", + "title": "Node-native Fetch API", + "depth": 3 + }, + { + "source": "/docs/releases/migration/cds10#node-native-sqlite", + "title": "Node-native SQLite", + "depth": 3 + }, + { + "source": "/docs/releases/migration/cds10#new-connection-pool", + "title": "New Connection Pool", + "depth": 3 + }, + { + "source": "/docs/releases/migration/cds10#fixed-affinity-for-decimals", + "title": "Fixed Affinity for Decimals", + "depth": 3 + }, + { + "source": "/docs/releases/migration/cds10#flags-entirely-removed", + "title": "Flags Entirely Removed", + "depth": 2 + }, + { + "source": "/docs/releases/migration/cds10#change-tracking-plugin-v2", + "title": "Change Tracking Plugin v2", + "depth": 2 + }, + { + "source": "/docs/releases/migration/cds10#are-you-affected-2", + "title": "Are you affected?", + "depth": 4 + }, + { + "source": "/docs/releases/migration/cds10#how-to-address-2", + "title": "How to address?", + "depth": 4 + }, + { + "source": "/docs/resources/", + "title": "Resources and Support", + "depth": 1 + }, + { + "source": "/docs/resources/#if-you-are-new-to-cap", + "title": "If you are new to CAP", + "depth": 2 + }, + { + "source": "/docs/resources/#public-resources", + "title": "Public Resources", + "depth": 2 + }, + { + "source": "/docs/resources/#support-channels", + "title": "Support Channels", + "depth": 2 + }, + { + "source": "/docs/resources/#references", + "title": "References", + "depth": 2 + }, + { + "source": "/docs/resources/#legal-notices", + "title": "Legal Notices", + "depth": 2 + }, + { + "source": "/docs/resources/events", + "title": "CAP Events Overview", + "depth": 1 + }, + { + "source": "/docs/resources/events#customer-roundtables", + "title": "Customer Roundtables ![](./assets/Roundtable.png){}", + "depth": 2 + }, + { + "source": "/docs/resources/events#roundtable06-26", + "title": "June 2026", + "depth": 3 + }, + { + "source": "/docs/resources/events#roundtable02-26", + "title": "February 2026", + "depth": 3 + }, + { + "source": "/docs/resources/events#roundtable12-25", + "title": "December 2025", + "depth": 3 + }, + { + "source": "/docs/resources/events#roundtable10-25", + "title": "October 2025", + "depth": 3 + }, + { + "source": "/docs/resources/events#roundtable06-25", + "title": "June 2025", + "depth": 3 + }, + { + "source": "/docs/resources/events#roundtable12-24", + "title": "December 2024", + "depth": 3 + }, + { + "source": "/docs/resources/events#roundtable03-24", + "title": "March 2024", + "depth": 3 + }, + { + "source": "/docs/resources/events#roundtable10-23", + "title": "October 2023", + "depth": 3 + }, + { + "source": "/docs/resources/events#roundtable04-23", + "title": "April 2023", + "depth": 3 + }, + { + "source": "/docs/resources/events#roundtable01-23", + "title": "January 2023", + "depth": 3 + }, + { + "source": "/docs/resources/events#roundtable10-22", + "title": "October 2022", + "depth": 3 + }, + { + "source": "/docs/resources/events#roundtable07-22", + "title": "July 2022", + "depth": 3 + }, + { + "source": "/docs/resources/events#roundtable02-22", + "title": "April 2022", + "depth": 3 + }, + { + "source": "/docs/resources/events#re8829cap", + "title": "re≽cap ![](./assets/recap2023.svg){}", + "depth": 2 + }, + { + "source": "/docs/resources/events#recap2026", + "title": "re≽cap 26", + "depth": 3 + }, + { + "source": "/docs/resources/events#recap2025", + "title": "re≽cap 25", + "depth": 3 + }, + { + "source": "/docs/resources/events#recap2024", + "title": "re≽cap 24", + "depth": 3 + }, + { + "source": "/docs/resources/events#recap2023", + "title": "re≽cap 23", + "depth": 3 + }, + { + "source": "/docs/resources/events#recap2022", + "title": "re≽cap 22", + "depth": 3 + }, + { + "source": "/docs/resources/events#recap2021", + "title": "re≽cap 21", + "depth": 3 + }, + { + "source": "/docs/resources/events#recap2020", + "title": "re≽cap 20", + "depth": 3 + }, + { + "source": "/docs/resources/events#teched22", + "title": "CAP at SAP TechEd 2022", + "depth": 2 + }, + { + "source": "/docs/resources/events#webinar-opensource", + "title": "Webinar: Open Sourcing CAP", + "depth": 2 + }, + { + "source": "/docs/resources/events#community-call-february-2022", + "title": "Community Call February 2022", + "depth": 2 + }, + { + "source": "/docs/resources/cap-license", + "title": "CAP License", + "depth": 1 + }, + { + "source": "/docs/resources/cap-license#where-can-i-find-the-new-cap-license", + "title": "Where can I find the new CAP license?", + "depth": 2 + }, + { + "source": "/docs/resources/cap-license#tldr---main-benefits-and-changes", + "title": "tl;dr - Main Benefits and Changes", + "depth": 2 + }, + { + "source": "/docs/resources/cap-license#faqs", + "title": "FAQs", + "depth": 1 + }, + { + "source": "/docs/resources/cap-license#what-did-we-announce-with-cap-v9", + "title": "What did we announce with CAP v9?", + "depth": 2 + }, + { + "source": "/docs/resources/cap-license#why-are-we-making-this-change", + "title": "Why are we making this change?", + "depth": 2 + }, + { + "source": "/docs/resources/cap-license#which-parts-of-cap-are-affected", + "title": "Which parts of CAP are affected?", + "depth": 2 + }, + { + "source": "/docs/resources/cap-license#what-are-the-implications-of-this-change-for-users-of-cap", + "title": "What are the implications of this change for users of CAP?", + "depth": 2 + }, + { + "source": "/docs/resources/cap-license#who-is-impacted-by-this-change", + "title": "Who is impacted by this change?", + "depth": 2 + }, + { + "source": "/docs/resources/cap-license#what-are-the-implications-on-the-cap-open-source-strategy", + "title": "What are the implications on the CAP Open Source strategy?", + "depth": 2 + }, + { + "source": "/docs/resources/cap-license#what-is-a-customer-productive-application", + "title": "What is a _Customer Productive Application_?", + "depth": 2 + }, + { + "source": "/docs/resources/cap-license#what-do-you-and-your-customer-mean", + "title": "What do _You_ and _Your Customer_ mean?", + "depth": 2 + }, + { + "source": "/docs/resources/cap-license#can-i-get-support-if-i-have-issues-with-cap", + "title": "Can I get support if I have issues with CAP?", + "depth": 2 + }, + { + "source": "/docs/resources/cap-license#can-i-continue-to-use-previous-versions-of-cap-that-were-provided-under-the-original-sap-developer-license-31--32", + "title": "Can I continue to use previous versions of CAP that were provided under the original _SAP Developer License 3.1 / 3.2_?", + "depth": 2 + }, + { + "source": "/docs/resources/cap-license#will-sap-provide-security-patches-to-previous-releases-under-the-original-sap-developer-license-31--32", + "title": "Will SAP provide security patches to previous releases under the original _SAP Developer License 3.1 / 3.2_?", + "depth": 2 + } +] \ No newline at end of file diff --git a/evals/docs/METRICS.md b/evals/docs/METRICS.md new file mode 100644 index 0000000..db04285 --- /dev/null +++ b/evals/docs/METRICS.md @@ -0,0 +1,174 @@ +# CAP MCP RAG — Eval Metrics + +## The setup every metric shares + +For one golden question we have: + +- **`relevant`** — the set of doc ids a human judged relevant to that question + (`relevant_doc_ids`). Call its size `R = |relevant|`. +- **`retrieved`** — the retriever's ranked list of doc ids, best first. We only look at + the top **K** of them (`K` is configurable, default 5). +- **`hits`** — the relevant docs that made it into the top-K: `relevant ∩ top-K`. + +Every metric below is a different, deliberately simple question about those `hits` and +*where* they landed. Each is computed **per question**, then **averaged (arithmetic +mean) over the golden set** to give the aggregate value you see in the report. + +Running example used throughout — question `cap-001`, `K = 5`: + +``` +relevant = { compositions, managed-compositions } (R = 2) +retrieved = [ associations, # rank 1 ✗ + compositions, # rank 2 ✓ ← first hit + domain-modeling, # rank 3 ✗ + managed-compositions,# rank 4 ✓ + entities ] # rank 5 ✗ +hits at ranks 2 and 4 +``` + +## The metrics + +### Recall@K — *did we retrieve the relevant docs at all?* + +**Formula:** `Recall@K = |relevant ∩ top-K| / |relevant|` + +**Why it makes sense.** This is the most fundamental retrieval question: of everything a +human said was relevant, what fraction actually showed up in the top-K the model +returned? If a relevant chunk never appears, nothing downstream — ranking, the LLM's +answer — can use it. Recall is the ceiling on how good the pipeline can possibly be. + +**A drop means** the right chunk isn't being *retrieved* at all → a **chunking or +embedding** problem (bad chunk boundaries, a weak embedding for that topic), not a +ranking problem. + +**Example:** both relevant docs are in the top 5, so `Recall@5 = 2/2 = 1.00`. If only +`compositions` had been retrieved, it would be `1/2 = 0.50`. + +### Precision@K — *how much of the top-K is actually useful?* + +**Formula:** `Precision@K = |relevant ∩ top-K| / K` + +**Why it makes sense.** Recall ignores the junk; precision measures it. Of the K results +we hand back (and pay context-window tokens for), what fraction is relevant? Low +precision means the model is padding the top-K with noise, which costs tokens and can +distract a downstream LLM even when recall is fine. We divide by **K**, not by the +number of results returned — returning fewer than K docs is penalised, because a short +list that happens to be clean shouldn't score the same as a full, clean one. + +**A drop means** the top-K is being **padded with irrelevant docs** (noise), typically +after a change that loosened ranking or added lower-quality chunks. + +**Example:** 2 of the 5 returned are relevant, so `Precision@5 = 2/5 = 0.40`. + +### MRR — *how high is the first relevant doc ranked?* + +**Formula:** `MRR = 1 / rank(first relevant doc in top-K)`, or `0` if none is in top-K. +(MRR = *Mean* Reciprocal Rank once averaged across questions.) + +**Why it makes sense.** Recall and precision are *set* metrics — they don't care about +order. But order matters: a relevant doc at rank 1 is far more useful than the same doc +at rank 5 (an LLM reads top-down and may be cut off by a context budget). The reciprocal +rank rewards putting *a* relevant doc as high as possible, and it falls off steeply +(1, ½, ⅓, ¼, …) so the difference between rank 1 and rank 3 is large and between rank 8 +and rank 9 is tiny — which matches how much rank actually matters to a reader. + +**A drop means** relevant chunks are still being retrieved (recall holds) but are ranked +**lower** → a **ranking / scoring** regression, not a chunking one. This recall-stable / +MRR-down pattern is exactly what the diagnosis rule keys on. + +**Example:** the first relevant doc is at rank 2, so `MRR = 1/2 = 0.50`. If it had been +at rank 1, `MRR = 1.00`. + +### Hit-Rate@K — *did we get at least one relevant doc? (the pass@k stand-in)* + +**Formula:** `Hit-Rate@K = 1` if `|relevant ∩ top-K| ≥ 1`, else `0`. + +**Why it makes sense.** This is the cheapest, most binary signal: did the retrieval +"work at all" for this question? Averaged over the golden set it's the fraction of +questions where the model surfaced *something* relevant in the top-K — a good smoke-test +/ CI tripwire that's easy to reason about and hard to game. + +**Relationship to `pass@k`.** `pass@k` (from agentic evals) means "give the system k +attempts, count success if any attempt succeeds" — it needs a *success oracle* to judge +each attempt. With **no LLM judge** and a **deterministic** retriever (k identical +attempts), the only honest, computable notion of "an attempt succeeded" is "a relevant +doc was retrieved" — which is precisely **Hit-Rate@K**. So there is deliberately no +separate `pass@k` metric or LLM call; `pass@k` collapses into Hit-Rate@K here. +(It differs from Recall@K only when a question has multiple relevant docs: Hit-Rate asks +"≥1?", Recall asks "what fraction?".) + +**Example:** at least one relevant doc is in the top 5, so `Hit-Rate@5 = 1`. + +### nDCG@K — *is the whole ranking well-ordered, not just the first hit?* + +**Formula (binary relevance):** + +``` +DCG@K = Σ_{i=1..K} rel_i / log2(i + 1) # rel_i ∈ {0,1}; i is the 1-based rank +IDCG@K = DCG@K of the ideal ordering # all relevant docs first +nDCG@K = DCG@K / IDCG@K # 0 if the question has no relevant docs +``` + +**Why it makes sense.** MRR only looks at the *first* relevant doc; nDCG grades the +*entire* top-K ordering. Each relevant doc contributes a gain discounted by its rank +(`1/log2(rank+1)`), so hits lower down still count but count less. Normalising by the +ideal DCG (what you'd get if every relevant doc were packed at the top) puts it on a +0–1 scale where **1.0 means "perfectly ordered"** regardless of how many relevant docs +the question has. It's the most complete ranking-quality signal, which is why we report +it — but it needs graded relevance to shine, so with our binary labels it mostly +corroborates MRR. + +> **Duplicates:** unlike Precision/Hit-Rate, nDCG credits each *distinct* relevant doc +> once, at its best (earliest) rank. If a relevant page fills several slots, the extra +> slots add no gain — otherwise a ranking that spams one good page would score a +> perfect ordering, hiding the very defect nDCG exists to catch. + +**A drop means** the ordering degraded even if the retrieved *set* is unchanged → +**ranking / scoring**, same family as MRR. + +**Example:** hits at ranks 2 and 4 → +`DCG = 1/log2(3) + 1/log2(5) = 0.6309 + 0.4307 = 1.0616`; +ideal (ranks 1 and 2) `IDCG = 1/log2(2) + 1/log2(3) = 1.0 + 0.6309 = 1.6309`; +`nDCG@5 = 1.0616 / 1.6309 ≈ 0.651`. + +## How the metrics work together + +They're layered on purpose — reading them side by side localises a regression to a +pipeline stage without any guessing: + +| Question the metric answers | Metric | Cares about rank? | +|---|---|:--:| +| Were the relevant docs retrieved at all? | Recall@K | no | +| Is the top-K free of noise? | Precision@K | no | +| Did we get at least one? (pass@k) | Hit-Rate@K | no | +| How high is the first relevant doc? | MRR | yes (first hit) | +| Is the whole ordering good? | nDCG@K | yes (all hits) | + +The **set** metrics (Recall, Precision, Hit-Rate) tell you *what* was found; the +**rank** metrics (MRR, nDCG) tell you *where*. A regression in the first group points at +chunking/embedding; a regression only in the second points at ranking/scoring. + +### Diagnosis (code-derived, advisory) + +The runner reports **two independent signals**, and it's important not to conflate them: + +- **The gate → `RESULT: PASS/FAIL`** is an **absolute floor**: each gated metric is + checked against its threshold (`value ≥ gate`), *no baseline involved*. This is the + CI tripwire — it answers "is retrieval good enough right now?" +- **The diagnosis** is **advisory** and **delta-based**: it looks at the *direction of + change vs the baseline* to suggest *where* a regression came from. It does not affect + pass/fail. A run can PASS the gate while the diagnosis notes a downward drift, or FAIL + the gate with `no_regression` (below the floor but unchanged since baseline). + +A delta must exceed a small **dead-band** (0.02, two rounding steps) to count — so +rounding-level noise on a small golden set doesn't trip a confident cause. All causes +above the dead-band are reported (not just the first), so a change that hurts several +stages isn't described as monocausal: + +1. Recall down → `recall_down → chunking/embedding regression` + *(the relevant docs stopped being retrieved — a set problem)* +2. Recall stable/up but MRR or nDCG down → `recall_stable_mrr_down → ranking/scoring regression` + *(same docs retrieved, ranked worse — a rank problem)* +3. Recall & MRR ok but Precision down → `precision_down → top-K padded with noise` + *(still finding + ranking the good ones, but adding junk around them)* +4. Nothing past the dead-band → `no_regression` \ No newline at end of file diff --git a/evals/docs/README.md b/evals/docs/README.md new file mode 100644 index 0000000..1ffe26a --- /dev/null +++ b/evals/docs/README.md @@ -0,0 +1,146 @@ +# CAP MCP RAG — Deterministic Retrieval Evals + +A **pure-code, fully deterministic** evaluation harness for the CAP MCP server's +`search_docs` retrieval. It scores the real retrieval path against a **frozen golden +set** with human-authored relevance labels, computes standard IR metrics by pure +arithmetic, compares against a stored baseline, applies gate thresholds, and emits a +machine-readable JSON report plus a human console summary. + +See [`METRICS.md`](./METRICS.md) for metric definitions, the stable-identifier scheme, +and the determinism guarantees. + +## Folder structure + +``` +evals/ + config.json # single source of all config (committed) — see "Configuration" + bin/ # thin CLI entry files invoked by the npm scripts + eval.js # `npm run evals` → evaluateAndCompare() + compare.js # `npm run evals:compare` → compare() + lib/ # implementation (imported by bin/ and the tests) + config.js # config loader (config.json + EVAL_* env overrides) + evaluate.js # evaluate(): orchestration — load → preflight → retrieve → score → append + compare.js # compare(): chart every run's metrics into an HTML dashboard + store.js # result.jsonl read/append (cap to keepRuns) + baseline = oldest run + report.js # pure core: buildReport, diagnose, worstQuestions, console render + metrics.js # pure metric math (Recall@K, Precision@K, MRR, Hit-Rate@K, nDCG@K) + ids.js # parse doc id (the Source: URL) from a chunk's first line + search-docs.js # index loader + adapter for the search_docs tool under test; pluggable + data/ # committed input + golden-set.json # frozen { id, question, relevant_doc_ids }; relevance authored once + docs/ + README.md # this file + METRICS.md # metric definitions + stable-ID scheme + determinism + runs/ # transient run output (git-ignored; created on demand) + result.jsonl # one JSON run report per line; capped to keepRuns (newest kept) + compare.html # metric-trend dashboard (or compare.md if compareFormat=md) + tests/ + unit/ # unit tests + metrics.test.js runner.test.js config.test.js ids.test.js cli.test.js compare.test.js +``` + +## Run + +From the repo root (`@cap-js/mcp-server`): + +```sh +npm run evals # run the eval → appends to result.jsonl, then compares +npm run evals:compare # (re)build the comparison report from result.jsonl +npm run evals:test # unit + determinism + config tests +``` + +`npm run evals` runs the eval once (each run appended to +`runs/result.jsonl`) and **always builds the comparison report afterwards** +(`runs/compare.html`, or `compare.md` when `compareFormat: md`). Each run appends one line (its JSON report) to +**`runs/result.jsonl`**, which is capped +to the most recent `output.keepRuns` runs. Every run is compared against the **oldest +run on file** (the baseline) — the first run has no baseline and becomes the reference. +The terminal shows the summary + the 3 +weakest questions; `evals:compare` charts all runs. There is no per-run folder and no markdown report. + +### Compare runs (`evals:compare`) + +Reads every run in `runs/result.jsonl`, ordered chronologically by run_id, and writes a +comparison report whose format is set by `output.compareFormat` in `config.json`: + +- **`html`** (default) → `runs/compare.html`: one line chart per metric (Recall@K, + Precision@K, MRR, Hit-Rate@K, nDCG@K) plotting its aggregate value across all runs (gate + threshold as a dashed line on gated metrics, below-gate points in red), a per-question + sparkline grid (all 5 metrics per question across runs), and a per-run drill-down + (click a run to expand its aggregate + per-question tables). Self-contained, no + dependencies, dark-mode aware, hover for exact values. +- **`md`** → `runs/compare.md`: the same data as GitHub-flavored markdown tables (no + charts) — an aggregate metric×run matrix, a per-question×run matrix per metric, and a + per-run drill-down section each with aggregate + per-question tables. + +```sh +npm run evals:compare # format from config.json (html default; set output.compareFormat: "md" for markdown) +``` + +It reads only `result.jsonl` — running it never triggers an eval. + +The eval **always runs the retriever offline** — it scores against the already-downloaded +chunk embeddings + model and never re-fetches the corpus during a run (that would break +determinism). So the cache (`embeddings/code-chunks.*` and the ONNX model under +`models/`) **must already exist**: on a fresh checkout, run any `search_docs` query once +(online) to populate it, then all eval runs work against that frozen snapshot. + +## Configuration + +All behaviour lives in [`config.json`](../config.json) — edit it in one place. Two +env vars are honoured for day-to-day runs, and everything is also overridable +programmatically via `evaluate({ overrides })` in `lib/evaluate.js` (overrides win last). + +| `config.json` key | Default | Meaning | +|---|---|---| +| `k` | `5` | Cutoff K for all @K metrics. Change it and clear `runs/` (K and the baseline are coupled). | +| `capire_version` | `2026.5.0` | capire docs version, recorded in the report for provenance. | +| `label` | _(unset)_ | Human-readable tag shown in reports to tell runs apart. Also settable via `EVAL_LABEL`. Display-only. | +| `baselineRunId` | _(unset)_ | Pin the baseline to a specific `run_id`. Unset → baseline is the oldest run on file. | +| `paths.goldenSet` | `data/golden-set.json` | Path to the golden set (relative to `evals/`, or absolute). | +| `paths.runsDir` | `runs` | Directory for run output. Also settable via `EVAL_RUNS_DIR` to score another corpus' results. | +| `gates.` | see file | Per-metric gate threshold (number in `[0,1]`) or `null` (reported only). | +| `output.keepRuns` | `20` (file ships `100`) | Max runs to keep in `result.jsonl` — `-1` = all, else a positive integer. | +| `output.resultsName` | `result.jsonl` | Name of the append-only results file. | +| `output.compareFormat` | `html` | `evals:compare` output: `html` (charts) or `md` (tables). | + +Only `EVAL_LABEL` and `EVAL_RUNS_DIR` are read from the environment (they're what +multi-corpus experiments vary run-to-run). Everything else is edited in `config.json`. + +### Useful commands + +```sh +# Run with a human-readable label (shows in compare.html leaderboard) +EVAL_LABEL="my-experiment" npm run evals + +# Point at a different corpus' results directory +EVAL_RUNS_DIR=runs-xenova npm run evals +EVAL_RUNS_DIR=runs-pplx npm run evals + +# Build compare.html from any result.jsonl +node evals/bin/compare.js --runs runs-xenova/result.jsonl +node evals/bin/compare.js --runs runs-xenova/result.jsonl --out runs-xenova/compare.html +``` + + +> **K and the baseline are coupled.** When you change `k`, clear `runs/` first — the +> baseline is the oldest run on file, so mixing different-K runs produces misleading +> deltas. + +> **The default gates are aspirational.** The golden set labels canonical reference pages; +> the retriever is release-notes-biased and currently scores ~Recall 0.65 / MRR 0.60 / +> Hit-Rate 0.80, so `npm run evals` **fails on Recall by design**. This is a real quality +> gap, not a broken harness. Raise retrieval quality (or lower the gates consciously) to +> turn it green. + +## Outputs + +Each run **appends one line** to `runs/result.jsonl` — the run's JSON report, matching +an exact contract (`run_id`, `config`, `aggregate` with +`value`/`baseline`/`delta`/`gate`/`status`, `overall_status`, `gated_failures`, +code-derived `diagnosis`, `per_question` sorted by `id`). Aggregate values are rounded +to 2 dp; per-question to 3 dp. + +The **console** prints a summary (header, metrics table with trend arrows and +gate/status icons, diagnosis, result line, and the 3 weakest questions). Chart all +runs (including per-question trends) with `evals:compare`. \ No newline at end of file diff --git a/evals/lib/buildSourceMap.js b/evals/lib/buildSourceMap.js new file mode 100644 index 0000000..b06578c --- /dev/null +++ b/evals/lib/buildSourceMap.js @@ -0,0 +1,46 @@ +import fs from 'fs/promises' +import path from 'path' +import { fileURLToPath } from 'url' +const HERE = path.dirname(fileURLToPath(import.meta.url)) +const OUT = path.join(HERE, '..', 'data', 'sourceMap.json') +// Default location of the docs export; override with argv[2]. +const DEFAULT_SRC = 'llms-full.txt' + +function isHeadingLine(lines, index) { + const match = /^(\s*#{1,6}) (.+)$/.exec(lines[index]); + if (!match) return null; + const window = lines.slice(index + 1, index + 4); + const offset = window.findIndex(l => /^>\s*Source:\s/.test(l)); + if (offset === -1) return null; + return { depth: match[1].length, title: match[2], source: window[offset] }; +} + +function buildSourceMap(text, config) { + const lines = text.split('\n'); + const roots = [] + + for (let i = 0; i < lines.length; i++) { + const heading = isHeadingLine(lines, i); + if (heading) { + const source = heading.source.split('> Source: ')?.[1] + roots.push({ source, title: heading.title, depth: heading.depth }); + } + } + return roots; +} + +async function build() { + const srcPath = process.argv[2] ? path.resolve(process.argv[2]) : DEFAULT_SRC + const text = await fs.readFile(srcPath, 'utf8') + const sourceMap = buildSourceMap(text) + await fs.writeFile( + OUT, + JSON.stringify(sourceMap, null, 2) + ) +} + +build() + .catch(e => { + console.error(e) + process.exit(3) + }) diff --git a/evals/lib/compare.js b/evals/lib/compare.js new file mode 100644 index 0000000..ee7c208 --- /dev/null +++ b/evals/lib/compare.js @@ -0,0 +1,699 @@ +import path from 'path' +import fs from 'fs/promises' +import { loadConfig, METRIC_KEYS, METRIC_LABEL } from './config.js' +import { readRuns, sortByRunId } from './store.js' +import { round } from './metrics.js' + +// Markdown can't be searched/lazy-loaded, so its per-question tables are capped +// to the top-N most-attention-worthy (regressed/weakest first). Full set lives +// in compare.html / result.jsonl. +const MD_TOP_N = 50 + +// Escape a cell for a markdown table: backslashes first, then pipes. +function mdCell(text) { + return (text || '').replace(/\\/g, '\\\\').replace(/\|/g, '\\|') +} + +// Escape text for HTML text content (prevent `<` injection). +function escHtml(text) { + return (text || '').replace(/ (s === 'pass' ? '✅' : s === 'fail' ? '❌' : 'ℹ️') +const gateStr = g => (g === null || g === undefined ? '—' : `≥ ${g.toFixed(2)}`) + +async function collectRuns(cfg) { + return sortByRunId(await readRuns(cfg)) +} + +// Shared per-question model for both the HTML and Markdown reports: question +// order + latest text, and one row per question with {id, question, series +// (per-metric values across runs, 3dp), avg, delta-vs-baseline}. Rows are sorted +// regressed/weakest-first (biggest MRR drop, then lowest MRR, then id). Baseline +// = oldest run. Uses a per-run id→metrics Map so lookups aren't O(questions²). +function buildPerQuestionModel(runs) { + const order = [] + const seen = new Set() + const textById = new Map() + const metricsByRun = runs.map(r => { + const m = new Map() + for (const q of r.per_question || []) { + if (!seen.has(q.id)) { + seen.add(q.id) + order.push(q.id) + } + textById.set(q.id, q.question) + m.set(q.id, q.metrics) + } + return m + }) + const baseMetrics = metricsByRun[0] + + const rows = order.map(id => { + const series = {} + const avg = {} + const delta = {} + for (const key of METRIC_KEYS) { + const vals = [] + for (const m of metricsByRun) { + const qm = m.get(id) + if (qm) vals.push(round(qm[key], 3)) + } + series[key] = vals + avg[key] = vals.length ? round(vals.reduce((s, v) => s + v, 0) / vals.length, 3) : 0 + const b = baseMetrics.get(id) + const last = vals.length ? vals[vals.length - 1] : 0 + delta[key] = b ? round(last - b[key], 3) : null + } + return { id, question: textById.get(id) || '', series, avg, delta } + }) + + rows.sort((a, b) => { + const da = a.delta.mrr === null ? 0 : a.delta.mrr + const db = b.delta.mrr === null ? 0 : b.delta.mrr + if (da !== db) return da - db // biggest MRR drop first + if (a.avg.mrr !== b.avg.mrr) return a.avg.mrr - b.avg.mrr // then lowest MRR + return a.id < b.id ? -1 : 1 + }) + + return { order, textById, rows } +} + +// Inline browser script for the per-question section: builds table rows from the +// embedded JSON blob, supports search + column sort, and lazily renders a row's +// 5 line charts only when expanded. Plain string so the page stays self-contained. +const PQ_SCRIPT = ` +(function(){ + var blob = JSON.parse(document.getElementById('pq-data').textContent); + var KEYS = blob.metricKeys, LBL = blob.metricLabels, GATES = blob.gates; + var RUNS = blob.runShorts, K = blob.k, DATA = blob.data; + var tbody = document.querySelector('#pq-table tbody'); + var search = document.getElementById('pq-search'); + var countEl = document.getElementById('pq-count'); + var sortKey = null, sortDir = 1; // null = default order (already regressed-first) + + function fmt(v){ return (v==null)?'—':v.toFixed(2); } + function deltaStr(d){ if(d==null) return ''; var s = d>0?'▲':d<0?'▼':'═'; return ' '+s+(d>0?'+':d<0?'−':'')+Math.abs(d).toFixed(2); } + + // Lazy chart: mirrors the server-side lineChartSvg geometry. + function chartSvg(key, vals){ + var W=520,H=240,m={top:20,right:16,bottom:64,left:38},iw=W-m.left-m.right,ih=H-m.top-m.bottom; + var n=vals.length, gate=GATES[key]; + function x(i){ return m.left+(n<=1?iw/2:(i/(n-1))*iw); } + function y(v){ return m.top+(1-v)*ih; } + var out=''; + [0,0.25,0.5,0.75,1].forEach(function(g){ var yy=y(g).toFixed(1); + out+=''; + out+=''+g.toFixed(2)+''; + }); + if(gate!=null){ out+=''; + out+='gate ≥ '+gate.toFixed(2)+''; } + var d=vals.map(function(v,i){ return (i?'L':'M')+x(i).toFixed(1)+','+y(v).toFixed(1); }).join(' '); + out+=''; + vals.forEach(function(v,i){ var below=gate!=null&&v'+RUNS[i]+': '+v.toFixed(3)+''; }); + var dly=(H-m.bottom+12).toFixed(1); + vals.forEach(function(v,i){ + var tx=x(i).toFixed(1); + out+=''+RUNS[i]+''; }); + out+=''; + var avg=vals.reduce(function(s,v){return s+v;},0)/(n||1); + var gated = gate!=null; + return '
'+LBL[key]+'@K' + +(gated?' gated':' reported') + +'avg '+avg.toFixed(2)+'
'+out+'
'; + } + + function render(list){ + tbody.innerHTML=''; + countEl.textContent = list.length + (list.length===DATA.length?'':' / '+DATA.length) + ' shown'; + var frag=document.createDocumentFragment(); + list.forEach(function(q){ + var tr=document.createElement('tr'); tr.className='pq-row'; + var cells=''+q.id+''+q.question.replace(/'; + KEYS.forEach(function(k){ cells+=''+fmt(q.avg[k])+''+deltaStr(q.delta[k])+''; }); + tr.innerHTML=cells; + var det=document.createElement('tr'); det.className='pq-detail'; det.style.display='none'; + det.innerHTML='
'; + var built=false; + tr.addEventListener('click', function(){ + var open = det.style.display!=='none'; + det.style.display = open?'none':'table-row'; + tr.classList.toggle('open', !open); + if(!open && !built){ det.querySelector('.pq-charts').innerHTML = KEYS.map(function(k){return chartSvg(k,q.series[k]);}).join(''); built=true; } + }); + frag.appendChild(tr); frag.appendChild(det); + }); + tbody.appendChild(frag); + } + + function apply(){ + var term=search.value.trim().toLowerCase(); + var list=DATA.filter(function(q){ return !term || q.id.toLowerCase().indexOf(term)>=0 || q.question.toLowerCase().indexOf(term)>=0; }); + if(sortKey){ list=list.slice().sort(function(a,b){ + var av,bv; + if(sortKey==='id'){ av=a.id; bv=b.id; return (avbv?1:0)*sortDir; } + if(sortKey==='question'){ av=a.question; bv=b.question; return (avbv?1:0)*sortDir; } + av=a.avg[sortKey]; bv=b.avg[sortKey]; return (av-bv)*sortDir; + }); } + render(list); + } + + search.addEventListener('input', apply); + document.querySelectorAll('#pq-table thead th').forEach(function(th){ + var key = th.getAttribute('data-sort') || th.getAttribute('data-metric'); + if(!key) return; + th.classList.add('sortable'); + th.addEventListener('click', function(){ + if(sortKey===key){ sortDir=-sortDir; } else { sortKey=key; sortDir = (key==='id'||key==='question')?1:-1; } + document.querySelectorAll('#pq-table thead th').forEach(function(t){t.classList.remove('asc','desc');}); + th.classList.add(sortDir>0?'asc':'desc'); + apply(); + }); + }); + render(DATA); // default: already sorted regressed-first + countEl.textContent = DATA.length + ' shown'; +})(); +` + +// ---- tiny SVG line chart (no dependencies) -------------------------------- +function lineChartSvg({ label, points, gate, avg }) { + const W = 680 + const H = 260 + const m = { top: 24, right: 20, bottom: 90, left: 44 } + const iw = W - m.left - m.right + const ih = H - m.top - m.bottom + const n = points.length + + // y spans the metric's natural [0,1] range so charts are comparable. + const yMin = 0 + const yMax = 1 + const x = i => m.left + (n <= 1 ? iw / 2 : (i / (n - 1)) * iw) + const y = v => m.top + (1 - (v - yMin) / (yMax - yMin)) * ih + + const gridVals = [0, 0.25, 0.5, 0.75, 1] + const grid = gridVals + .map(v => { + const yy = y(v).toFixed(1) + return `` + + `${v.toFixed(2)}` + }) + .join('') + + const gateLine = + gate !== null && gate !== undefined + ? `` + + `gate ≥ ${gate.toFixed(2)}` + : '' + + const linePath = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${x(i).toFixed(1)},${y(p.value).toFixed(1)}`).join(' ') + + const dots = points + .map((p, i) => { + const cx = x(i).toFixed(1) + const cy = y(p.value).toFixed(1) + const below = gate !== null && gate !== undefined && p.value < gate + return `${p.runFull || p.runShort}\n${label}: ${p.value.toFixed(3)}${below ? ' (below gate)' : ''}` + }) + .join('') + + // One label per dot, placed directly under each circle and rotated -45°. + // Uses the short label for space; the full label is in the dot's tooltip. + const dotLabelY = (H - m.bottom + 12).toFixed(1) + const xticks = points + .map((p, i) => { + const tx = x(i).toFixed(1) + return `${p.runShort}` + }) + .join('') + + return `
+
${label}${gate !== null && gate !== undefined ? ' gated' : ' reported'}avg ${avg.toFixed(2)} across ${n} run${n === 1 ? '' : 's'}
+ + ${grid} + ${gateLine} + + ${dots} + ${xticks} + +
` +} + +// Per-question section, built to scale to 1000+ questions: ONE searchable / +// sortable table + a compact JSON blob; a row's 5 charts render lazily (in JS) +// only when expanded. No external deps; embedded data is fixed → deterministic. +function renderPerQuestionSection(runs) { + const { order, rows: data } = buildPerQuestionModel(runs) + if (order.length === 0) return '' + + const runShorts = runs.map(runDisplay) + const gates = {} + for (const key of METRIC_KEYS) gates[key] = runs[runs.length - 1].aggregate[key].gate ?? null + + const headCells = METRIC_KEYS.map(k => `${METRIC_LABEL[k]}`).join('') + + const blob = { runShorts, gates, k: runs[runs.length - 1].config.k, metricKeys: METRIC_KEYS, metricLabels: METRIC_LABEL, data } + + return `

Per-question metric trends + ${data.length} questions · sorted by MRR drop then lowest MRR · click a row for its charts +

+
+ + +
+ + ${headCells} + +
idquestion
+ + ` +} + +// short run id for chart tooltips / x-axis (time-of-day) +function shortRunId(r) { + return r.run_id.replace(/T/, ' ').replace(/(\.\d+)?Z.*$/, '').slice(5) +} + +// Display name for a run: its label if set, else the short timestamp. +function runDisplay(r) { + return (r.config && r.config.label) || shortRunId(r) +} + +// Truncated label for chart axes (28 chars max + ellipsis). Full label in tooltips. +function shortLabel(r) { + const full = runDisplay(r) + return full.length > 28 ? full.slice(0, 27) + '…' : full +} + +// Rank each run for each metric (1 = best). Returns Map>. +function buildRanks(runs) { + const ranks = new Map(runs.map(r => [r.run_id, {}])) + for (const key of METRIC_KEYS) { + const sorted = [...runs].sort((a, b) => b.aggregate[key].value - a.aggregate[key].value) + sorted.forEach((r, i) => { ranks.get(r.run_id)[key] = i + 1 }) + } + return ranks +} + +// Leaderboard table: gated metrics as rows, runs as columns sorted by each metric's rank. +// Gold / silver / bronze cells make the winner instantly obvious. +function renderLeaderboard(runs) { + if (runs.length < 2) return '' + const ranks = buildRanks(runs) + const gated = METRIC_KEYS.filter(k => runs[runs.length - 1].aggregate[k].gate !== null) + if (!gated.length) return '' + + // Order columns by total rank score (lower = better) across gated metrics only. + // Show ALL metrics in the table; ungated ones have no gate threshold but still rank. + const totalScore = r => gated.reduce((s, k) => s + ranks.get(r.run_id)[k], 0) + const cols = [...runs].sort((a, b) => totalScore(a) - totalScore(b)) + + const medalClass = rank => rank === 1 ? 'rank-1' : rank === 2 ? 'rank-2' : rank === 3 ? 'rank-3' : '' + const medal = rank => rank === 1 ? '🥇' : rank === 2 ? '🥈' : rank === 3 ? '🥉' : `#${rank}` + + const headerCells = cols.map((r, i) => + `${medal(i + 1)} ${escHtml(shortLabel(r))}` + ).join('') + + const rows = METRIC_KEYS.map(key => { + const isGated = runs[runs.length - 1].aggregate[key].gate !== null + const cells = cols.map(r => { + const rk = ranks.get(r.run_id)[key] + return `${r.aggregate[key].value.toFixed(2)}` + }).join('') + const label = `${METRIC_LABEL[key]}@K${isGated ? '' : ' reported'}` + return `${label}${cells}` + }).join('') + + return `
+

Leaderboard (🥇 = best per metric · columns sorted by total rank on gated metrics)

+
+ + ${headerCells} + ${rows} +
metric
+
+
` +} + +// A click-to-expand card for one run: summary row + aggregate table + +// per-question table (all 5 metrics). Native
— no JS. +// `textById` (optional Map) resolves retrieved ids to chunk text for expansion. +// `runRanks` (optional Map) shows rank vs other runs. +function renderRunDetails(r, textById, runRanks) { + const summaryCells = METRIC_KEYS.map(k => `${METRIC_LABEL[k]} ${r.aggregate[k].value.toFixed(2)}`).join('') + const res = r.overall_status === 'fail' ? '❌ FAIL' : '✅ PASS' + + const aggRows = METRIC_KEYS.map(k => { + const a = r.aggregate[k] + const rk = runRanks ? runRanks.get(r.run_id)[k] : null + const rankCell = rk !== null ? `#${rk}` : '—' + return `${METRIC_LABEL[k]}@${r.config.k}${a.value.toFixed(2)}${rankCell}${gateStr(a.gate)}${statusIcon(a.status)}` + }).join('') + + const pqHead = METRIC_KEYS.map(k => `${METRIC_LABEL[k]}`).join('') + const pqRows = (r.per_question || []) + .map(q => { + const cells = METRIC_KEYS.map(k => `${(q.metrics[k] ?? 0).toFixed(3)}`).join('') + const ranks = q.relevant_hits_at_rank && q.relevant_hits_at_rank.length ? q.relevant_hits_at_rank.join(', ') : '—' + const question = escHtml(q.question) + return `${q.id}${question}${cells}${ranks}` + }) + .join('') + + const pqSection = (r.per_question || []).length + ? `
Per-question metrics
+ + ${pqHead} + ${pqRows} +
idquestionhit ranks
` + : '
No per-question data recorded for this run.
' + + // per-question retrieval detail: what search_docs returned, ranked, hits marked + const retrievalSection = (r.per_question || []).length + ? `
Retrieved results per question (rank order; ✅ = relevant, ✗ = not)
+ ${(r.per_question).map(q => { + const relevant = new Set(q.relevant_doc_ids || []) + const question = escHtml(q.question) + const relList = (q.relevant_doc_ids || []).map(id => `
  • ${id}
  • `).join('') + const retList = (q.retrieved_ids || []).map((id, i) => { + const hit = relevant.has(id) + const marker = `${i + 1}. ${hit ? '✅' : '✗'} ${id}` + // Prefer the run-time text snapshot; fall back to the live corpus for + // older reports without one. + const text = (q.retrieved_texts && q.retrieved_texts[i]) || (textById && textById.get(id)) + if (text) { + return `
  • ${marker}
    ${escHtml(text)}
  • ` + } + return `
  • ${marker} (text unavailable)
  • ` + }).join('') + return `
    + ${q.id} ${question} +
    +
    relevant (${(q.relevant_doc_ids || []).length})
    +
      ${relList}
    +
    retrieved top-${r.config.k}
    +
      ${retList || '
    • (none)
    • '}
    +
    +
    ` + }).join('')}` + : '' + + const label = r.config && r.config.label + return `
    + ${label ? `${label} ` : ''}${r.run_id} ${summaryCells} ${res} +
    +
    Aggregate metrics · capire ${r.config.capire_version} · K=${r.config.k}
    + + + ${aggRows} +
    metricvaluerankgate
    +
    Diagnosis: ${r.diagnosis}
    + ${pqSection} + ${retrievalSection} +
    +
    ` +} + +function renderHtml(runs, textById) { + const ranks = buildRanks(runs) + + // Build per-metric point series. + const charts = METRIC_KEYS.map(key => { + const points = runs.map(r => ({ + value: r.aggregate[key].value, + runShort: shortLabel(r), + runFull: runDisplay(r), + runId: r.run_id + })) + // gate: take the most recent run's gate for this metric (null = reported only) + const gate = runs.length ? runs[runs.length - 1].aggregate[key].gate : null + const avg = points.length ? points.reduce((s, p) => s + p.value, 0) / points.length : 0 + return lineChartSvg({ label: `${METRIC_LABEL[key]}@K`, points, gate, avg }) + }).join('\n') + + // Newest run first so the most recent is easiest to open. + const runDetails = [...runs].reverse().map(r => renderRunDetails(r, textById, ranks)).join('\n') + + const perQuestion = renderPerQuestionSection(runs) + const leaderboard = renderLeaderboard(runs) + + return ` + + + + +CAP MCP RAG — Metric trends across runs + + + +
    +

    CAP MCP RAG — Metric trends across runs

    +
    ${runs.length} run${runs.length === 1 ? '' : 's'} · dashed red = gate threshold · red dot = below gate
    +
    +${leaderboard} +
    +${charts} +
    +${perQuestion} +

    Inspect each run (newest first — click to expand aggregate + per-question metrics)

    +
    +${runDetails} +
    + + +` +} + +// ---- markdown compare report (full parity, tables only — no charts) ------- +function renderMarkdownCompare(runs) { + const L = [] + const first = runs[0].run_id + const last = runs[runs.length - 1].run_id + + L.push('# CAP MCP RAG — Metric trends across runs') + L.push('') + L.push(`${runs.length} run${runs.length === 1 ? '' : 's'} · ${first} → ${last}`) + L.push('') + + // 1) Aggregate: metric × run matrix + L.push('## Aggregate metrics across runs') + L.push('') + L.push(`| metric | gate | ${runs.map(r => mdCell(runDisplay(r))).join(' | ')} |`) + L.push(`|---|:--:|${runs.map(() => '--:').join('|')}|`) + for (const key of METRIC_KEYS) { + const gate = runs[runs.length - 1].aggregate[key].gate + const cells = runs + .map(r => { + const a = r.aggregate[key] + const flag = a.gate !== null && a.gate !== undefined && a.value < a.gate ? ' ❌' : '' + return `${a.value.toFixed(2)}${flag}` + }) + .join(' | ') + L.push(`| ${METRIC_LABEL[key]}@K | ${gateStr(gate)} | ${cells} |`) + } + L.push(`| **result** | | ${runs.map(r => (r.overall_status === 'fail' ? '❌' : '✅')).join(' | ')} |`) + L.push('') + + // 2) Per-question: one compact avg+Δ table, regressed/weakest-first, capped to MD_TOP_N. + const { order: qOrder, rows } = buildPerQuestionModel(runs) + if (qOrder.length) { + const shown = rows.slice(0, MD_TOP_N) + + L.push('## Per-question metrics (avg across runs)') + L.push('') + L.push( + qOrder.length > shown.length + ? `Showing the ${shown.length} most-attention-worthy of ${qOrder.length} questions (sorted by MRR drop vs baseline, then lowest MRR). Full per-question detail: open \`compare.html\` or read \`result.jsonl\`.` + : `All ${qOrder.length} questions (sorted by MRR drop vs baseline, then lowest MRR).` + ) + L.push('') + const fmtCell = (avg, d) => { + const dz = d === null ? '' : d > 0 ? ` (▲+${d.toFixed(2)})` : d < 0 ? ` (▼−${Math.abs(d).toFixed(2)})` : '' + return `${avg.toFixed(2)}${dz}` + } + L.push(`| id | question | ${METRIC_KEYS.map(k => METRIC_LABEL[k]).join(' | ')} |`) + L.push(`|---|---|${METRIC_KEYS.map(() => '--:').join('|')}|`) + for (const row of shown) { + const cells = METRIC_KEYS.map(k => fmtCell(row.avg[k], row.delta[k])).join(' | ') + const text = mdCell(row.question) + L.push(`| ${row.id} | ${text} | ${cells} |`) + } + L.push('') + } + + // 3) Per-run drill-down: aggregate + per-question tables (newest first) + L.push('## Inspect each run') + for (const r of [...runs].reverse()) { + L.push('') + const heading = r.config && r.config.label ? `${r.config.label} — \`${r.run_id}\`` : `\`${r.run_id}\`` + L.push(`### ${heading} — ${r.overall_status === 'fail' ? '❌ FAIL' : '✅ PASS'}`) + L.push('') + L.push(`capire ${r.config.capire_version} · K=${r.config.k} · baseline ${r.baseline_run_id ? `\`${r.baseline_run_id}\`` : '—'} · diagnosis: \`${r.diagnosis}\``) + L.push('') + L.push('| metric | value | Δ vs base | baseline | gate | status |') + L.push('|---|--:|:--:|--:|:--:|:--:|') + for (const key of METRIC_KEYS) { + const a = r.aggregate[key] + const delta = a.delta === null || a.delta === undefined ? '—' : `${a.delta > 0 ? '+' : a.delta < 0 ? '−' : ''}${Math.abs(a.delta).toFixed(2)}` + const base = a.baseline === null || a.baseline === undefined ? '—' : a.baseline.toFixed(2) + L.push(`| ${METRIC_LABEL[key]}@${r.config.k} | ${a.value.toFixed(2)} | ${delta} | ${base} | ${gateStr(a.gate)} | ${statusIcon(a.status)} |`) + } + if ((r.per_question || []).length) { + L.push('') + const pq = r.per_question + const shownPq = pq.slice(0, MD_TOP_N) + if (pq.length > shownPq.length) { + L.push(`_First ${shownPq.length} of ${pq.length} questions (full detail in \`compare.html\` / \`result.jsonl\`)._`) + L.push('') + } + L.push(`| id | question | ${METRIC_KEYS.map(k => METRIC_LABEL[k]).join(' | ')} | hit ranks |`) + L.push(`|---|---|${METRIC_KEYS.map(() => '--:').join('|')}|:--|`) + for (const q of shownPq) { + const cells = METRIC_KEYS.map(k => (q.metrics[k] ?? 0).toFixed(3)).join(' | ') + const ranks = q.relevant_hits_at_rank && q.relevant_hits_at_rank.length ? q.relevant_hits_at_rank.join(', ') : '—' + const text = mdCell(q.question) + L.push(`| ${q.id} | ${text} | ${cells} | ${ranks} |`) + } + } + } + L.push('') + return L.join('\n') +} + +export async function compare({ configPath, overrides, outPath, logger = console, perQuestionRaw, deps = {} } = {}) { + const cfg = await loadConfig({ configPath, overrides }) + const runs = await collectRuns(cfg) + + if (runs.length === 0) { + logger.error(`No runs found under ${path.relative(process.cwd(), cfg.paths.runsDir)}. Run the eval first.`) + return { code: 3 } + } + + const fmt = cfg.output.compareFormat // 'html' | 'md' (validated in config) + let content + if (fmt === 'md') { + content = renderMarkdownCompare(runs) + } else { + content = renderHtml(runs, perQuestionRaw) + } + const out = outPath ? path.resolve(outPath) : path.join(cfg.paths.runsDir, `compare.${fmt}`) + await fs.writeFile(out, content) + + const rel = path.relative(process.cwd(), out) + logger.log(`Compared ${runs.length} run(s) → ${rel}`) + if (fmt === 'html') { + const opener = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open' + logger.log(`Open it in your browser: ${opener} ${rel}`) + logger.log(` or paste this into the address bar: file://${out}`) + } else { + logger.log(`Open it in your editor/viewer: ${rel}`) + } + return { code: 0, runs: runs.length, outPath: out, format: fmt } +} diff --git a/evals/lib/config.js b/evals/lib/config.js new file mode 100644 index 0000000..406ca5f --- /dev/null +++ b/evals/lib/config.js @@ -0,0 +1,101 @@ +import { fileURLToPath } from 'url' +import path from 'path' +import fs from 'fs/promises' + +// evals/ root (this file lives in evals/lib/) +export const EVALS_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') + +export const METRIC_KEYS = ['recall_at_k', 'mrr', 'precision_at_k', 'hit_rate_at_k', 'ndcg_at_k'] +export const GATED_KEYS = ['recall_at_k', 'mrr', 'hit_rate_at_k'] +export const METRIC_LABEL = { + recall_at_k: 'Recall', + mrr: 'MRR', + precision_at_k: 'Precision', + hit_rate_at_k: 'Hit-Rate', + ndcg_at_k: 'nDCG' +} + +function envStr(name, fallback) { + const v = process.env[name] + return v === undefined || v === '' ? fallback : v +} + +// Load and resolve the effective configuration. +// Everything lives in config.json. Two env vars are honoured for day-to-day +// runs — EVAL_LABEL (tag a run) and EVAL_RUNS_DIR (point at another corpus' +// results) — plus programmatic overrides used by tests and bin/compare.js. +export async function loadConfig({ configPath, overrides } = {}) { + const cfgPath = configPath ? path.resolve(configPath) : path.join(EVALS_DIR, 'config.json') + + let file = {} + try { + file = JSON.parse(await fs.readFile(cfgPath, 'utf8')) + } catch (err) { + if (err.code !== 'ENOENT') throw err + // No config.json → pure defaults. + } + + const paths = file.paths || {} + const gatesFile = { ...(file.gates || {}) } + delete gatesFile.$comment + const output = file.output || {} + + const resolve = p => (path.isAbsolute(p) ? p : path.join(EVALS_DIR, p)) + + const cfg = { + configPath: cfgPath, + k: file.k ?? 5, + capire_version: file.capire_version || 'unknown', + // Optional human-readable tag to tell runs apart in reports; '' = unset. + label: envStr('EVAL_LABEL', file.label || ''), + // Pinned baseline run_id; empty/absent → baseline is the oldest run on file. + baselineRunId: file.baselineRunId || null, + paths: { + goldenSet: resolve(paths.goldenSet || 'data/golden-set.json'), + runsDir: resolve(envStr('EVAL_RUNS_DIR', paths.runsDir || 'runs')) + }, + gates: {}, + output: { + keepRuns: output.keepRuns ?? 20, + resultsName: output.resultsName || 'result.jsonl', + compareFormat: output.compareFormat || 'html' + } + } + + // Gates: file value if present, else default (0 for gated metrics, null otherwise). + for (const key of METRIC_KEYS) { + cfg.gates[key] = key in gatesFile ? gatesFile[key] : GATED_KEYS.includes(key) ? 0 : null + } + + // Programmatic overrides (used by tests / bin/compare.js) win last. + if (overrides) { + if (overrides.k !== undefined) cfg.k = overrides.k + if (overrides.capire_version !== undefined) cfg.capire_version = overrides.capire_version + if (overrides.label !== undefined) cfg.label = overrides.label + if (overrides.baselineRunId !== undefined) cfg.baselineRunId = overrides.baselineRunId + if (overrides.gates) Object.assign(cfg.gates, overrides.gates) + if (overrides.paths) Object.assign(cfg.paths, overrides.paths) + if (overrides.output) Object.assign(cfg.output, overrides.output) + } + + validateConfig(cfg) + return cfg +} + +function validateConfig(cfg) { + if (!Number.isInteger(cfg.k) || cfg.k <= 0) throw new Error(`config: k must be a positive integer (got ${cfg.k})`) + // keepRuns: -1 (keep all) or a positive integer; 0 would wipe the just-appended run. + const keep = cfg.output.keepRuns + if (keep !== -1 && (!Number.isInteger(keep) || keep <= 0)) { + throw new Error(`config: keepRuns must be -1 (keep all) or a positive integer (got ${keep})`) + } + if (!['html', 'md'].includes(cfg.output.compareFormat)) { + throw new Error(`config: compareFormat must be "html" or "md" (got ${cfg.output.compareFormat})`) + } + for (const key of METRIC_KEYS) { + const g = cfg.gates[key] + if (g !== null && (typeof g !== 'number' || Number.isNaN(g) || g < 0 || g > 1)) { + throw new Error(`config: gate ${key} must be null or a number in [0,1] (got ${g})`) + } + } +} diff --git a/evals/lib/evaluate.js b/evals/lib/evaluate.js new file mode 100644 index 0000000..cfbedd1 --- /dev/null +++ b/evals/lib/evaluate.js @@ -0,0 +1,96 @@ +import path from 'path' +import fs from 'fs/promises' +import { loadConfig, EVALS_DIR } from './config.js' +import { makeSearchDocsRunner } from './search-docs.js' +import { preflight, validateGolden, buildReport, makeRunId } from './report.js' +import { appendRun, readRuns, baselineRun } from './store.js' + +async function readJsonOrNull(p) { + try { + return JSON.parse(await fs.readFile(p, 'utf8')) + } catch (err) { + if (err.code === 'ENOENT') return null + throw err + } +} + +// `deps` is a test seam: pass { loadIndex, makeRetriever } to score against a +// fixture without loading the ONNX model. Production omits it. +export async function evaluate({ configPath, overrides, logger = console, deps = {} } = {}) { + const makeRetrieverFn = deps.makeRetriever || makeSearchDocsRunner + + const cfg = await loadConfig({ configPath, overrides }) + + const golden = await readJsonOrNull(cfg.paths.goldenSet) + if (!golden || !Array.isArray(golden.questions)) { + logger.error(`Golden set missing or malformed at ${cfg.paths.goldenSet}`) + return { code: 3 } + } + const problems = validateGolden(golden.questions) + if (problems.length > 0) { + logger.error(`Golden set at ${cfg.paths.goldenSet} has ${problems.length} problem(s):`) + for (const p of problems) logger.error(` ${p}`) + return { code: 3 } + } + // Baseline (read before this run is appended): pinned run if set, else oldest. + const baseline = baselineRun(await readRuns(cfg), cfg.baselineRunId) + if (cfg.baselineRunId && !baseline) { + logger.error(`(note: pinned baseline "${cfg.baselineRunId}" not found in result.jsonl — this run has no baseline)`) + } + + const sourceMap = await readJsonOrNull(path.join(EVALS_DIR, 'data', 'sourceMap.json')) + + // Warn (don't abort) on stale relevant_doc_ids — the corpus likely re-indexed + // and these labels no longer match; they'll score as misses until refreshed. + const stale = preflight(golden.questions, sourceMap) + if (stale.length > 0) { + logger.error(`PRE-FLIGHT WARNING: ${stale.length} golden doc id(s) not in the current index (will score as misses — refresh the golden set, see docs/README.md):`) + for (const s of stale) logger.error(` ${s.question}: ${s.doc_id}`) + } + + const retrieve = await makeRetrieverFn(cfg.k, sourceMap) + const perQuestionRaw = [] + for (const q of golden.questions) { + const resolvedChunk = await retrieve(q.question) + perQuestionRaw.push({ + id: q.id, + question: q.question, + relevant_doc_ids: q.relevant_doc_ids, + retrievedIds: resolvedChunk + }) + } + + const config = { + capire_version: cfg.capire_version, + golden_set: golden.golden_set, + golden_set_size: golden.questions.length, + k: cfg.k, + label: cfg.label + } + + const report = buildReport({ config, perQuestionRaw, baseline, gates: cfg.gates }) + const run_id = makeRunId() + const full = { run_id, ...report } + + const { path: resultsFile, total } = await appendRun(cfg, full) + + const status = report.overall_status === 'fail' ? `FAIL (${report.gated_failures.join(', ')})` : 'PASS' + logger.error(`${status} — appended run ${run_id} → ${path.relative(process.cwd(), resultsFile)}; ${total} run(s) on file`) + + return { code: report.overall_status === 'fail' ? 1 : 0, report: full, resultsFile, perQuestionRaw } +} + +// Entry point for `npm run evals`: run the eval once, then build the comparison report. +export async function evaluateAndCompare({ configPath, overrides, logger = console, deps = {} } = {}) { + const { code, perQuestionRaw } = await evaluate({ configPath, overrides, logger, deps }) + + // Always compare afterwards; best-effort, doesn't override the eval exit code. + try { + const { compare } = await import('./compare.js') + await compare({ configPath, overrides, logger, perQuestionRaw }) + } catch (err) { + logger.error(`(compare step failed: ${err.message})`) + } + + return { code } +} diff --git a/evals/lib/ids.js b/evals/lib/ids.js new file mode 100644 index 0000000..16021a3 --- /dev/null +++ b/evals/lib/ids.js @@ -0,0 +1,88 @@ +const HEADING = /^#{1,6}\s+(.*\S)\s*$/ +const SOURCE = /Source:\s*(\S+)/i + +function getSourceByBreadCrump(headings, finds, sourceMap) { + let candidates = finds + for (const cand of candidates) { + const idx = sourceMap.indexOf(cand) + const path = [cand.title] + let currDepth = cand.depth + for (let i = idx - 1; i >= 0; i--) { + const entry = sourceMap[i] + if (entry.depth < currDepth) { + path.unshift(entry.title) + currDepth = entry.depth + } + if (entry.depth <= 1) break + } + const breadCrumb = path.join(' > ') + if (breadCrumb === headings.join(' > ')) return cand.source + } + throw Error('No source found') +} + +let duration = 0 +let question = 0 +export function resolveIds(chunks, sourceMap) { + question++ + const start = performance.now() + const resolvedChunks = [] + for (const text of chunks) { + const ids = [] + const lines = (text || '').split('\n') + let i = 0 + const headings = text.split('\n')[0] + .split(' > ') + .map(h => h.replace(/^#{1,6}\s+/, '').trim()) + .filter(Boolean) + if (!headings.length) throw Error('No breadcrumb') + + for (const line of lines) { + if(line.trim() === '') { + i++ + continue + } + const m = lines[i+2]?.match(SOURCE) + if (m) { + ids.push(m[1]) + i = i + 3 + continue + } + if (/^#{1,6}\s+/.test(line)) { + const match = /^(\s*#{1,6}) (.+)$/.exec(line) + const depth = match[1].length + const heading = line.replace(/^#{1,6}\s+/, '') + const finds = sourceMap.filter(s => s.title === heading && s.depth === depth) + if (!finds.length) continue + else if (finds.length === 1) { + ids.push(finds[0].source) + continue + } + + const breadCrumb = [] + breadCrumb.push(heading) + const idx = lines.indexOf(line) + let currDepth = depth + for (let i = idx - 1; i >= 0; i--) { + const entry = lines[i] + const match = /^(\s*#{1,6}) (.+)$/.exec(entry) + if (!match) continue + const entryDepth = match[1].length + if (entryDepth < currDepth) { + breadCrumb.unshift(entry.replace(/^#{1,6}\s+/, '')) + currDepth = entryDepth + } + if (entryDepth <= 1) break + } + const id = getSourceByBreadCrump([...headings, ...breadCrumb], finds, sourceMap) + if(id) ids.push(id) + } + i++ + } + if (!ids.length) throw Error('No IDs found') + resolvedChunks.push({ ids, text }) + } + duration = duration + performance.now() - start + if (question === 100) console.log(duration) + return resolvedChunks +} diff --git a/evals/lib/metrics.js b/evals/lib/metrics.js new file mode 100644 index 0000000..bc5ea5b --- /dev/null +++ b/evals/lib/metrics.js @@ -0,0 +1,115 @@ +// Pure-arithmetic retrieval metrics, binary relevance. Deterministic: no I/O, +// randomness, LLM, or wall-clock. +// +// `retrieved` is the raw ranked slots search_docs returned (best first). +// Duplicates are KEPT — the eval scores the tool's real output, not a cleaned +// copy, so a page filling 3 of the K slots counts as 3 slots. + +function topK(retrieved, k) { + return retrieved.slice(0, k) +} + +// 1-based ranks (within top-K) at which a relevant doc appears, per slot. +export function relevantHitsAtRank(relevant, retrieved, k) { + const rel = new Set(relevant) + const ranks = [] + const top = topK(retrieved, k) + for (let i = 0; i < top.length; i++) { + if (rel.has(top[i])) ranks.push(i + 1) + } + return ranks +} + +// Distinct relevant docs found / total relevant (a relevant page in several +// slots is still one doc). ∈ [0,1]. +export function recallAtK(relevant, retrieved, k) { + const rel = new Set(relevant) + if (rel.size === 0) return 0 + const top = topK(retrieved, k) + const found = new Set() + for (const t of top) { + for (const id of t.ids) { + if (rel.has(id)) found.add(id) + } + } + return found.size / rel.size +} + +// Relevant slots / k (duplicates count). +export function precisionAtK(relevant, retrieved, k) { + if (k <= 0) return 0 + const rel = new Set(relevant) + const top = topK(retrieved, k) + let hits = 0 + for (const t of top) { + for (const id of t.ids) if (rel.has(id)) hits++ + } + return hits / k +} + +export function mrr(relevant, retrieved, k) { + const rel = new Set(relevant) + const top = topK(retrieved, k) + for (let i = 0; i < top.length; i++) { + for (const id of top[i].ids) if (rel.has(id)) return 1 / (i + 1) + } + return 0 +} + +export function hitRateAtK(relevant, retrieved, k) { + const rel = new Set(relevant) + const top = topK(retrieved, k) + for (const t of top) { + for (const id of t.ids) if (rel.has(id)) return 1 + } + return 0 +} + +// nDCG over the top-K ranking. Each DISTINCT relevant doc is credited once, at +// its best rank, so duplicate slots can't inflate DCG past the ideal and mask a +// bad ordering. Ideal DCG packs the distinct relevant docs at the top. ∈ [0,1]. +export function ndcgAtK(relevant, retrieved, k) { + const rel = new Set(relevant) + if (rel.size === 0) return 0 + const top = topK(retrieved, k) + let dcg = 0 + const credited = new Set() + for (let i = 0; i < top.length; i++) { + for (const id of top[i].ids) { + if (rel.has(id) && !credited.has(id)) { + credited.add(id) + dcg += 1 / Math.log2(i + 2) // rank i+1 → 1/log2(i+2) + } + } + } + const idealHits = Math.min(rel.size, k) + let idcg = 0 + for (let i = 0; i < idealHits; i++) idcg += 1 / Math.log2(i + 2) + return idcg === 0 ? 0 : dcg / idcg +} + +export function metricsFor(relevant, retrieved, k) { + return { + recall_at_k: recallAtK(relevant, retrieved, k), + precision_at_k: precisionAtK(relevant, retrieved, k), + mrr: mrr(relevant, retrieved, k), + hit_rate_at_k: hitRateAtK(relevant, retrieved, k), + ndcg_at_k: ndcgAtK(relevant, retrieved, k) + } +} + +export function mean(values) { + if (values.length === 0) return 0 + let s = 0 + for (const v of values) s += v + return s / values.length +} + +export function round(value, dp) { + const f = Math.pow(10, dp) + // Round on magnitude so negatives round symmetrically with positives (e.g. + // -0.005 → -0.01, mirroring 0.005 → 0.01). Epsilon counters binary-float + // representation of exact halves so rounding is stable across platforms. + const r = Math.sign(value) * Math.round(Math.abs(value) * f + Number.EPSILON) / f + return r === 0 ? 0 : r // normalise -0 → 0 +} diff --git a/evals/lib/report.js b/evals/lib/report.js new file mode 100644 index 0000000..ebcfda5 --- /dev/null +++ b/evals/lib/report.js @@ -0,0 +1,127 @@ +import { metricsFor, relevantHitsAtRank, mean, round } from './metrics.js' +import { METRIC_KEYS } from './config.js' + +// Structural validation of the golden set. Returns human-readable problem +// strings ([] = valid); catches malformed labels that would crash the run or +// silently skew scoring. +export function validateGolden(questions) { + const problems = [] + const seenIds = new Set() + questions.forEach((q, i) => { + const where = q && q.id ? `question "${q.id}"` : `question #${i + 1}` + if (!q || typeof q !== 'object') { + problems.push(`${where}: not an object`) + return + } + if (typeof q.id !== 'string' || !q.id) problems.push(`${where}: missing string "id"`) + else if (seenIds.has(q.id)) problems.push(`${where}: duplicate id`) + else seenIds.add(q.id) + if (typeof q.question !== 'string' || !q.question.trim()) problems.push(`${where}: missing non-empty "question"`) + if (!Array.isArray(q.relevant_doc_ids)) problems.push(`${where}: "relevant_doc_ids" must be an array`) + else if (q.relevant_doc_ids.length === 0) problems.push(`${where}: "relevant_doc_ids" is empty (no ground truth)`) + else if (q.relevant_doc_ids.some(id => typeof id !== 'string' || !id)) problems.push(`${where}: "relevant_doc_ids" contains a non-string/empty id`) + }) + return problems +} + +// Pre-flight: every relevant_doc_id must exist in the current index. +export function preflight(goldenQuestions, sourceMap) { + const stale = [] + for (const q of goldenQuestions) { + for (const id of q.relevant_doc_ids) { + if (!sourceMap.some(m => m.source === id)) stale.push({ question: q.id, doc_id: id }) + } + } + return stale +} + +// Pure core: build the report object (run_id added by the caller). +export function buildReport({ config, perQuestionRaw, baseline, gates }) { + const k = config.k + + const per_question = perQuestionRaw + .map(q => { + const m = metricsFor(q.relevant_doc_ids, q.retrievedIds, k) + return { + id: q.id, + question: q.question, + relevant_doc_ids: q.relevant_doc_ids, + retrieved_ids: q.retrievedIds.slice(0, k), + // Per-slot text snapshot (when provided), so compare needn't re-read the corpus. + ...(q.retrieved_texts ? { retrieved_texts: q.retrieved_texts.slice(0, k) } : {}), + relevant_hits_at_rank: relevantHitsAtRank(q.relevant_doc_ids, q.retrievedIds, k), + metrics: { + recall_at_k: round(m.recall_at_k, 3), + precision_at_k: round(m.precision_at_k, 3), + mrr: round(m.mrr, 3), + hit_rate_at_k: m.hit_rate_at_k, + ndcg_at_k: round(m.ndcg_at_k, 3) + }, + _full: m // full precision for aggregation; stripped before serialize + } + }) + .sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)) + + // aggregate = mean of full-precision per-question values, rounded to 2 dp + const aggregate = {} + const baseAgg = baseline ? baseline.aggregate : null + const gated_failures = [] + for (const key of METRIC_KEYS) { + const value = round(mean(per_question.map(q => q._full[key])), 2) + const gate = key in gates ? gates[key] : null + const baselineVal = baseAgg && baseAgg[key] != null ? baseAgg[key].value : null + const delta = baselineVal === null || baselineVal === undefined ? null : round(value - baselineVal, 2) + let status + if (gate === null || gate === undefined) status = 'info' + else if (value >= gate) status = 'pass' + else { + status = 'fail' + gated_failures.push(key) + } + aggregate[key] = { value, baseline: baselineVal, delta, gate: gate ?? null, status } + } + + const overall_status = gated_failures.length > 0 ? 'fail' : 'pass' + const diagnosis = diagnose(aggregate) + + for (const q of per_question) delete q._full + + return { + config, + baseline_run_id: baseline ? baseline.run_id : null, + aggregate, + overall_status, + gated_failures, + diagnosis, + per_question + } +} + +// Min delta magnitude to count as a regression — below this is rounding noise on +// a small golden set (aggregates are 2dp). +export const DIAGNOSE_DEADBAND = 0.02 + +// Diagnosis from aggregate deltas vs baseline. Reports ALL causes past the +// dead-band (not first-match-wins), so a multi-stage regression isn't monocausal. +export function diagnose(aggregate) { + const d = key => (aggregate[key] ? aggregate[key].delta : null) + const down = v => v !== null && v < -DIAGNOSE_DEADBAND + const recall = d('recall_at_k') + const mrrD = d('mrr') + const ndcg = d('ndcg_at_k') + const prec = d('precision_at_k') + + const causes = [] + if (down(recall)) causes.push('recall_down → chunking/embedding regression') + if (down(mrrD) || down(ndcg)) causes.push('recall_stable_mrr_down → ranking/scoring regression') + if (down(prec)) causes.push('precision_down → top-K padded with noise') + return causes.length ? causes.join('; ') : 'no_regression' +} + +// run_id is the ONLY nondeterministic value (`rand` is fixed in tests). Full ms +// timestamp so same-second runs sort by execution time, not by the suffix. +export function makeRunId(now = new Date(), rand) { + const ts = now.toISOString() + const suffix = rand || Math.random().toString(16).slice(2, 8) + return `${ts}_${suffix}` +} diff --git a/evals/lib/search-docs.js b/evals/lib/search-docs.js new file mode 100644 index 0000000..108ae9b --- /dev/null +++ b/evals/lib/search-docs.js @@ -0,0 +1,10 @@ +import { resolveIds } from './ids.js' +import tools from '../../lib/tools.js' + +export async function makeSearchDocsRunner(k, sourceMap) { + const retrieve = async function (question, time) { + const out = await tools.search_docs.handler({ query: question, maxResults: k }) + return resolveIds(out ? out.split('\n---\n') : [], sourceMap) + } + return retrieve +} diff --git a/evals/lib/store.js b/evals/lib/store.js new file mode 100644 index 0000000..6fd20c7 --- /dev/null +++ b/evals/lib/store.js @@ -0,0 +1,69 @@ +import path from 'path' +import fs from 'fs/promises' + +// The append-only results file: one JSON run report per line (JSONL). +export function resultsPath(cfg) { + return path.join(cfg.paths.runsDir, cfg.output.resultsName) +} + +// All run reports (write order). Skips blank/corrupt lines; [] if no file. +export async function readRuns(cfg) { + let text + try { + text = await fs.readFile(resultsPath(cfg), 'utf8') + } catch (err) { + if (err.code === 'ENOENT') return [] + throw err + } + const runs = [] + for (const line of text.split('\n')) { + const t = line.trim() + if (!t) continue + let parsed + try { + parsed = JSON.parse(t) + } catch { + continue // skip a corrupt line rather than fail the whole read + } + // Skip structurally-valid JSON that isn't a run report, so downstream code + // reading r.aggregate[...].value can't crash on a wrong-shape line. + if (!parsed || typeof parsed !== 'object' || typeof parsed.run_id !== 'string' || typeof parsed.aggregate !== 'object' || parsed.aggregate === null) { + continue + } + runs.push(parsed) + } + return runs +} + +// run_id is an ISO-timestamp prefix, so string sort is chronological. +export function sortByRunId(runs) { + return [...runs].sort((a, b) => (a.run_id < b.run_id ? -1 : a.run_id > b.run_id ? 1 : 0)) +} + +// The baseline this run is compared against: +// - pinnedId set → that specific run (null if it's not on file — never a +// silent substitute), a stable anchor that doesn't move as runs are pruned. +// - otherwise → the oldest run on file (slides forward as runs are pruned). +// null when there are no runs. +export function baselineRun(runs, pinnedId) { + const sorted = sortByRunId(runs) + if (pinnedId) return sorted.find(r => r.run_id === pinnedId) || null + return sorted.length ? sorted[0] : null +} + +// Append one run, then cap the file to the most recent `keep` runs (keep < 0 = +// keep all). Rewrites the whole file so the cap is enforced deterministically. +export async function appendRun(cfg, report) { + await fs.mkdir(cfg.paths.runsDir, { recursive: true }) + const runs = await readRuns(cfg) + runs.push(report) + let kept = sortByRunId(runs) + const keep = cfg.output.keepRuns + if (keep >= 0 && kept.length > keep) { + kept = kept.slice(kept.length - keep) + } + const body = kept.map(r => JSON.stringify(r)).join('\n') + (kept.length ? '\n' : '') + await fs.writeFile(resultsPath(cfg), body) + return { path: resultsPath(cfg), total: kept.length } +} + diff --git a/evals/tests/unit/compare.test.js b/evals/tests/unit/compare.test.js new file mode 100644 index 0000000..a34fb6a --- /dev/null +++ b/evals/tests/unit/compare.test.js @@ -0,0 +1,344 @@ +import { test, describe, beforeEach, afterEach } from 'node:test' +import assert from 'node:assert/strict' +import fs from 'fs/promises' +import path from 'path' +import os from 'os' +import { compare } from '../../lib/compare.js' + +const silentLogger = { log() {}, error() {} } + +// Minimal valid report object for a given run_id + metric values. +// `perQuestion` (optional) is an array of { id, question, metrics{...} }. +function fakeReport(run_id, { recall = 1, mrr = 1, precision = 0.4, hit = 1, ndcg = 1, perQuestion = [], label = '' } = {}) { + const agg = (value, gate) => ({ value, baseline: null, delta: null, gate, status: gate === null ? 'info' : value >= gate ? 'pass' : 'fail' }) + return { + run_id, + config: { capire_version: '2026.5.0', golden_set: 'g', golden_set_size: 1, k: 5, label }, + baseline_run_id: null, + aggregate: { + recall_at_k: agg(recall, 0.8), + mrr: agg(mrr, 0.5), + precision_at_k: agg(precision, null), + hit_rate_at_k: agg(hit, 0.8), + ndcg_at_k: agg(ndcg, null) + }, + overall_status: recall >= 0.8 && mrr >= 0.5 && hit >= 0.8 ? 'pass' : 'fail', + gated_failures: [], + diagnosis: 'no_regression', + per_question: perQuestion + } +} + +// Build a per-question entry with all five metrics. +function pq(id, question, { recall = 1, precision = 0.4, mrr = 1, hit = 1, ndcg = 1 } = {}) { + return { id, question, relevant_doc_ids: [], retrieved_ids: [], relevant_hits_at_rank: [], metrics: { recall_at_k: recall, precision_at_k: precision, mrr, hit_rate_at_k: hit, ndcg_at_k: ndcg } } +} + +describe('compare tests', () => { + let tmpDir, runsDir + beforeEach(async () => { + tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'evals-compare-')) + runsDir = path.join(tmpDir, 'runs') + await fs.mkdir(runsDir, { recursive: true }) + }) + afterEach(async () => { + await fs.rm(tmpDir, { recursive: true, force: true }) + }) + + // Append a run report as a line to result.jsonl (the store format). + async function writeRun(_folderIgnored, report) { + const p = path.join(runsDir, 'result.jsonl') + await fs.appendFile(p, JSON.stringify(report) + '\n') + } + + const overrides = () => ({ paths: { runsDir } }) + + test('no runs → exit 3, no file written', async () => { + const res = await compare({ overrides: overrides(), logger: silentLogger }) + assert.equal(res.code, 3) + await assert.rejects(() => fs.access(path.join(runsDir, 'compare.html'))) + }) + + test('collects all runs from result.jsonl, writes html', async () => { + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_aaa')) + await writeRun(null, fakeReport('2026-07-30T11:00:00Z_bbb', { recall: 0.5 })) + const res = await compare({ overrides: overrides(), logger: silentLogger }) + assert.equal(res.code, 0) + assert.equal(res.runs, 2) + + const html = await fs.readFile(path.join(runsDir, 'compare.html'), 'utf8') + // one chart per metric, one expandable run-detail card per run + assert.equal((html.match(/
    /g) || []).length, 5) + assert.equal((html.match(/
    /g) || []).length, 2) + // gated metrics draw a gate line (recall, mrr, hit_rate = 3) + assert.equal((html.match(/class="gate"/g) || []).length, 3) + }) + + test('label shows in html (drill-down) and falls back to timestamp when unset', async () => { + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_aaa', { label: 'tuned chunker' })) + await writeRun(null, fakeReport('2026-07-30T11:00:00Z_bbb')) // no label + await compare({ overrides: overrides(), logger: silentLogger }) + const html = await fs.readFile(path.join(runsDir, 'compare.html'), 'utf8') + assert.ok(html.includes('tuned chunker')) // labeled run + assert.ok(html.includes('07-30 11:00:00')) // unlabeled run → short timestamp + }) + + test('label shows in the md aggregate matrix header', async () => { + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_aaa', { label: 'tuned chunker' })) + await compare({ overrides: { paths: { runsDir }, output: { compareFormat: 'md' } }, logger: silentLogger }) + const md = await fs.readFile(path.join(runsDir, 'compare.md'), 'utf8') + assert.ok(md.includes('| metric | gate | tuned chunker |')) // column header = label + assert.ok(md.includes('tuned chunker — `2026-07-30T10:00:00Z_aaa`')) // drill-down heading keeps run_id + }) + + test('md format: writes compare.md with tables (no svg)', async () => { + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_a', { perQuestion: [pq('cap-001', 'How do I define X?', { mrr: 0.5 })] })) + const res = await compare({ overrides: { paths: { runsDir }, output: { compareFormat: 'md' } }, logger: silentLogger }) + assert.equal(res.code, 0) + assert.equal(res.format, 'md') + assert.ok(res.outPath.endsWith('compare.md')) + const md = await fs.readFile(path.join(runsDir, 'compare.md'), 'utf8') + assert.match(md, /^# CAP MCP RAG/) + assert.ok(md.includes('## Aggregate metrics across runs')) + assert.ok(md.includes('## Per-question metrics (avg across runs)')) + assert.ok(md.includes('## Inspect each run')) + assert.ok(md.includes('cap-001')) + assert.ok(!md.includes(' fs.access(path.join(runsDir, 'compare.html'))) + }) + + test('md format: escapes backslashes and pipes in question text', async () => { + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_a', { perQuestion: [pq('cap-001', 'a\\b | c', { mrr: 0.5 })] })) + await compare({ overrides: { paths: { runsDir }, output: { compareFormat: 'md' } }, logger: silentLogger }) + const md = await fs.readFile(path.join(runsDir, 'compare.md'), 'utf8') + // backslash doubled, pipe escaped → renders as one table cell + assert.ok(md.includes('a\\\\b \\| c')) + }) + + test('md format: caps the per-question table to top-N for large golden sets', async () => { + // 120 questions across 2 runs → MD should cap to 50 and say so + const mk = (rid, seed) => { + const per = [] + for (let i = 1; i <= 120; i++) { + per.push(pq('cap-' + String(i).padStart(3, '0'), 'Q' + i, { mrr: ((i + seed) % 10) / 10 })) + } + return fakeReport(rid, { perQuestion: per }) + } + await writeRun(null, mk('2026-07-30T10:00:00Z_a', 0)) + await writeRun(null, mk('2026-07-30T11:00:00Z_b', 2)) + await compare({ overrides: { paths: { runsDir }, output: { compareFormat: 'md' } }, logger: silentLogger }) + const md = await fs.readFile(path.join(runsDir, 'compare.md'), 'utf8') + assert.ok(md.includes('Showing the 50 most-attention-worthy of 120 questions')) + // per-question section table rows are capped (count '| cap-' lines in that section) + const section = md.split('## Per-question metrics (avg across runs)')[1].split('## Inspect each run')[0] + const rowCount = (section.match(/\n\| cap-/g) || []).length + assert.equal(rowCount, 50) + }) + + test('run-detail card includes aggregate + per-question tables', async () => { + const q = pq('cap-001', 'How do I define X?', { recall: 1, mrr: 0.5, precision: 0.4, hit: 1, ndcg: 0.65 }) + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_a', { perQuestion: [q] })) + await compare({ overrides: overrides(), logger: silentLogger }) + const html = await fs.readFile(path.join(runsDir, 'compare.html'), 'utf8') + // one card, two rd-tables (aggregate + per-question) + assert.equal((html.match(/
    /g) || []).length, 1) + assert.equal((html.match(/class="rd-table"/g) || []).length, 2) + // per-question row present with the question id + its MRR (3dp) + assert.ok(html.includes('cap-001') && html.includes('How do I define X?')) + assert.ok(html.includes('0.500')) // per-question MRR at 3dp + }) + + test('retrieved ids are expandable to chunk text when the corpus resolves them', async () => { + const q = { + id: 'cap-001', question: 'How do I define X?', + relevant_doc_ids: ['https://x/a#hit'], + retrieved_ids: ['https://x/a#hit', 'https://x/b#miss'], + relevant_hits_at_rank: [1], + metrics: { recall_at_k: 1, precision_at_k: 0.5, mrr: 1, hit_rate_at_k: 1, ndcg_at_k: 1 } + } + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_a', { perQuestion: [q] })) + const textById = new Map([ + ['https://x/a#hit', 'Heading > Source: https://x/a#hit\nthe relevant chunk body'], + ['https://x/b#miss', 'Other > Source: https://x/b#miss\nan irrelevant chunk body'] + ]) + await compare({ overrides: overrides(), logger: silentLogger, deps: { loadChunkText: async () => textById } }) + const html = await fs.readFile(path.join(runsDir, 'compare.html'), 'utf8') + // each retrieved id renders as an expandable chunk with its text inside a
    +    assert.ok(html.includes('
    ')) + assert.ok(html.includes('the relevant chunk body')) + assert.ok(html.includes('an irrelevant chunk body')) + }) + + test('retrieved chunk text comes from the report snapshot even if corpus lookup is empty', async () => { + // retrieved_texts (snapshotted at run time) resolves regardless of the live corpus. + const q = { + id: 'cap-001', question: 'q', relevant_doc_ids: ['https://x/a#hit'], + retrieved_ids: ['https://x/a#hit', 'https://x/b#miss'], + retrieved_texts: ['snapshot body A', 'snapshot body B'], + relevant_hits_at_rank: [1], + metrics: { recall_at_k: 1, precision_at_k: 0.5, mrr: 1, hit_rate_at_k: 1, ndcg_at_k: 1 } + } + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_a', { perQuestion: [q] })) + // empty corpus lookup — text must still show from the snapshot + await compare({ overrides: overrides(), logger: silentLogger, deps: { loadChunkText: async () => new Map() } }) + const html = await fs.readFile(path.join(runsDir, 'compare.html'), 'utf8') + assert.ok(html.includes('
    ')) + assert.ok(html.includes('snapshot body A') && html.includes('snapshot body B')) + }) + + test('retrieved id with no text (no snapshot, not in corpus) renders without expansion', async () => { + const q = { + id: 'cap-001', question: 'q', relevant_doc_ids: ['https://x/a#hit'], + retrieved_ids: ['https://x/gone#stale'], relevant_hits_at_rank: [], + metrics: { recall_at_k: 0, precision_at_k: 0, mrr: 0, hit_rate_at_k: 0, ndcg_at_k: 0 } + } + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_a', { recall: 0, mrr: 0, hit: 0, perQuestion: [q] })) + await compare({ overrides: overrides(), logger: silentLogger, deps: { loadChunkText: async () => new Map() } }) + const html = await fs.readFile(path.join(runsDir, 'compare.html'), 'utf8') + assert.ok(html.includes('(text unavailable)')) + assert.ok(!html.includes('
    ')) + }) + + test('runs are ordered chronologically by run_id in the run-list section', async () => { + // write out of order; expect sorted output in the run-list (not the leaderboard) + await writeRun(null, fakeReport('2026-07-30T12:00:00Z_zzz')) + await writeRun(null, fakeReport('2026-07-30T09:00:00Z_aaa')) + await compare({ overrides: overrides(), logger: silentLogger }) + const html = await fs.readFile(path.join(runsDir, 'compare.html'), 'utf8') + // The run-list renders newest first, so zzz (later) appears before aaa in that section. + const runListStart = html.indexOf('class="run-list"') + assert.ok(runListStart !== -1) + const runList = html.slice(runListStart) + const iLate = runList.indexOf('2026-07-30T12:00:00Z_zzz') + const iEarly = runList.indexOf('2026-07-30T09:00:00Z_aaa') + assert.ok(iLate < iEarly, 'run-list: later (newest) run should appear before earlier run') + }) + + test('skips corrupt/blank lines in result.jsonl', async () => { + const p = path.join(runsDir, 'result.jsonl') + await fs.writeFile(p, JSON.stringify(fakeReport('2026-07-30T10:00:00Z_ok')) + '\n\nnot-json\n') + const res = await compare({ overrides: overrides(), logger: silentLogger }) + assert.equal(res.runs, 1) + }) + + test('skips structurally-valid-but-wrong-shape lines (no aggregate)', async () => { + const p = path.join(runsDir, 'result.jsonl') + // valid JSON that isn't a run report — must be skipped, not crash downstream + await fs.writeFile(p, [ + JSON.stringify(fakeReport('2026-07-30T10:00:00Z_ok')), + '123', + '"a string"', + '{"run_id":"x"}', // no aggregate + '{"aggregate":{}}' // no run_id + ].join('\n') + '\n') + const res = await compare({ overrides: overrides(), logger: silentLogger }) + assert.equal(res.runs, 1) // only the well-formed report survives + }) + + test('per-question section: searchable table + embedded data blob (scales, lazy charts)', async () => { + const q1a = pq('cap-001', 'How do I define X?', { mrr: 1 }) + const q2a = pq('cap-002', 'How do I do Y?', { mrr: 0.5 }) + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_a', { perQuestion: [q1a, q2a] })) + // second run: cap-001 MRR regresses; cap-002 stable + const q1b = pq('cap-001', 'How do I define X?', { mrr: 0.333 }) + const q2b = pq('cap-002', 'How do I do Y?', { mrr: 0.5 }) + await writeRun(null, fakeReport('2026-07-30T11:00:00Z_b', { perQuestion: [q1b, q2b] })) + + await compare({ overrides: overrides(), logger: silentLogger }) + const html = await fs.readFile(path.join(runsDir, 'compare.html'), 'utf8') + + assert.ok(html.includes('Per-question metric trends')) + // a searchable table + search box (not a chart per question) + assert.ok(html.includes('id="pq-table"')) + assert.ok(html.includes('id="pq-search"')) + // only the 5 overall charts are baked as SVG; per-question charts are lazy (JS) + assert.equal((html.match(/
    /g) || []).length, 5) + // the questions live in the embedded JSON data blob, regressed-first + const m = html.match(/id="pq-data"[^>]*>(.*?)<\/script>/s) + assert.ok(m, 'pq-data blob present') + const blob = JSON.parse(m[1].replace(/\\u003c/g, '<')) + assert.equal(blob.data.length, 2) + assert.equal(blob.data[0].id, 'cap-001') // biggest MRR drop first + assert.ok(blob.data[0].question === 'How do I define X?') + }) + + test('per-question section omitted when no run has per_question data', async () => { + await writeRun('eval-run-x', fakeReport('2026-07-30T10:00:00Z_x')) // per_question: [] + await compare({ overrides: overrides(), logger: silentLogger }) + const html = await fs.readFile(path.join(runsDir, 'compare.html'), 'utf8') + assert.ok(!html.includes('Per-question metric trends')) + }) + + test('leaderboard shows all 5 metrics (gated and reported)', async () => { + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_aaa', { recall: 0.9, mrr: 0.8 })) + await writeRun(null, fakeReport('2026-07-30T11:00:00Z_bbb', { recall: 0.5, mrr: 0.4 })) + await compare({ overrides: overrides(), logger: silentLogger }) + const html = await fs.readFile(path.join(runsDir, 'compare.html'), 'utf8') + // leaderboard present with 5 metric rows (one per METRIC_KEY) + assert.ok(html.includes('class="lb-table"')) + assert.ok(html.includes('Recall@K')) + assert.ok(html.includes('MRR@K')) + assert.ok(html.includes('Precision@K')) + assert.ok(html.includes('Hit-Rate@K')) + assert.ok(html.includes('nDCG@K')) + // gated = no "reported" tag; ungated precision/ndcg have the tag + assert.ok(html.includes('Precision@K')) + const lbSection = html.slice(html.indexOf('class="lb-table"'), html.indexOf('', html.indexOf('class="lb-table"'))) + const rows = (lbSection.match(//g) || []).length - 1 // exclude header + assert.equal(rows, 5) + }) + + test('leaderboard ranks best run first (gold medal column)', async () => { + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_aaa', { recall: 0.9, mrr: 0.8, hit: 0.9 })) + await writeRun(null, fakeReport('2026-07-30T11:00:00Z_bbb', { recall: 0.3, mrr: 0.2, hit: 0.3 })) + await compare({ overrides: overrides(), logger: silentLogger }) + const html = await fs.readFile(path.join(runsDir, 'compare.html'), 'utf8') + // best run (aaa with higher scores) should appear as 🥇 column + assert.ok(html.includes('🥇')) + // the best run's label should appear before the worse run in the leaderboard header + const lbStart = html.indexOf('class="lb-table"') + const lbHeader = html.slice(lbStart, html.indexOf('', lbStart)) + const iGold = lbHeader.indexOf('🥇') + const iSilver = lbHeader.indexOf('🥈') + assert.ok(iGold < iSilver) + }) + + test('run-detail aggregate table shows rank column instead of delta/baseline', async () => { + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_aaa', { recall: 0.9 })) + await writeRun(null, fakeReport('2026-07-30T11:00:00Z_bbb', { recall: 0.5 })) + await compare({ overrides: overrides(), logger: silentLogger }) + const html = await fs.readFile(path.join(runsDir, 'compare.html'), 'utf8') + // rank column header present, old delta/baseline headers absent + assert.ok(html.includes('>rank<')) + assert.ok(!html.includes('Δ vs base')) + assert.ok(!html.includes('>baseline<')) + // rank values (#1, #2) present + assert.ok(html.includes('>#1<') || html.includes('>#2<')) + }) + + test('x-axis labels: one per dot (no skipping), short with ellipsis', async () => { + const longLabel = 'a'.repeat(40) + ' / model-name-here' + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_aaa', { label: longLabel })) + await writeRun(null, fakeReport('2026-07-30T11:00:00Z_bbb')) + await compare({ overrides: overrides(), logger: silentLogger }) + const html = await fs.readFile(path.join(runsDir, 'compare.html'), 'utf8') + // each chart has 2 xtick labels (one per run, no skipping) + const tickCount = (html.match(/class="axis xtick"/g) || []).length + assert.equal(tickCount, 5 * 2) // 5 charts × 2 runs + // long label is truncated with ellipsis in the xtick text + assert.ok(html.includes('…')) + // the xtick element itself uses the short (truncated) form, not the full label + const tickMatch = html.match(/class="axis xtick"[^>]*>([^<]+)<\/text>/) + assert.ok(tickMatch && tickMatch[1].length <= 29) // ≤28 chars + ellipsis + }) + + test('grid uses 2 columns', async () => { + await writeRun(null, fakeReport('2026-07-30T10:00:00Z_aaa')) + await compare({ overrides: overrides(), logger: silentLogger }) + const html = await fs.readFile(path.join(runsDir, 'compare.html'), 'utf8') + assert.ok(html.includes('repeat(2,1fr)')) + }) +}) diff --git a/evals/tests/unit/config.test.js b/evals/tests/unit/config.test.js new file mode 100644 index 0000000..7351e54 --- /dev/null +++ b/evals/tests/unit/config.test.js @@ -0,0 +1,100 @@ +import { test, describe, afterEach } from 'node:test' +import assert from 'node:assert/strict' +import { loadConfig, METRIC_KEYS } from '../../lib/config.js' + +// Snapshot & restore the two honoured env vars between tests so overrides don't leak. +const EVAL_ENV = ['EVAL_LABEL', 'EVAL_RUNS_DIR'] +function clearEnv() { + for (const k of EVAL_ENV) delete process.env[k] +} + +describe('config tests', () => { + afterEach(clearEnv) + + test('loads defaults from config.json', async () => { + clearEnv() + const cfg = await loadConfig() + assert.equal(cfg.k, 5) + assert.equal(cfg.gates.recall_at_k, 0.8) + assert.equal(cfg.gates.precision_at_k, null) + assert.ok(cfg.paths.goldenSet.endsWith('data/golden-set.json')) + assert.ok(cfg.paths.runsDir.endsWith('runs')) + // all metric keys present in gates + for (const key of METRIC_KEYS) assert.ok(key in cfg.gates) + }) + + test('label is set via EVAL_LABEL / override (empty by default in code)', async () => { + clearEnv() + // Code default is '' (config.json may set its own value, so don't assume ''). + assert.equal((await loadConfig({ configPath: '/no/such/config.json' })).label, '') + process.env.EVAL_LABEL = 'tuned chunker' + assert.equal((await loadConfig()).label, 'tuned chunker') // env + const cfg = await loadConfig({ overrides: { label: 'baseline v1' } }) + assert.equal(cfg.label, 'baseline v1') // override wins over env + }) + + test('EVAL_RUNS_DIR points at another corpus\' results (absolute respected)', async () => { + clearEnv() + process.env.EVAL_RUNS_DIR = '/tmp/eval-runs-abs' + assert.equal((await loadConfig()).paths.runsDir, '/tmp/eval-runs-abs') + }) + + test('programmatic overrides win last', async () => { + clearEnv() + const cfg = await loadConfig({ overrides: { k: 3, gates: { recall_at_k: 0.99 }, output: { keepRuns: 3, compareFormat: 'md' } } }) + assert.equal(cfg.k, 3) + assert.equal(cfg.gates.recall_at_k, 0.99) + assert.equal(cfg.output.keepRuns, 3) + assert.equal(cfg.output.compareFormat, 'md') + }) + + test('compareFormat defaults to html', async () => { + clearEnv() + assert.equal((await loadConfig()).output.compareFormat, 'html') + }) + + test('rejects invalid compareFormat', async () => { + clearEnv() + await assert.rejects( + () => loadConfig({ overrides: { output: { compareFormat: 'pdf' } } }), + /compareFormat must be "html" or "md"/ + ) + }) + + test('rejects invalid k', async () => { + clearEnv() + await assert.rejects(() => loadConfig({ overrides: { k: 0 } }), /k must be a positive integer/) + }) + + test('rejects out-of-range gate', async () => { + clearEnv() + await assert.rejects( + () => loadConfig({ overrides: { gates: { recall_at_k: 1.5 } } }), + /must be null or a number in \[0,1\]/ + ) + }) + + test('validates gates on non-default metrics too (precision_at_k)', async () => { + clearEnv() + await assert.rejects( + () => loadConfig({ overrides: { gates: { precision_at_k: 1.5 } } }), + /gate precision_at_k must be null or a number in \[0,1\]/ + ) + }) + + test('rejects keepRuns = 0 (would wipe the just-appended run)', async () => { + clearEnv() + await assert.rejects(() => loadConfig({ overrides: { output: { keepRuns: 0 } } }), /keepRuns must be -1 .* or a positive integer/) + }) + + test('rejects fractional keepRuns', async () => { + clearEnv() + await assert.rejects(() => loadConfig({ overrides: { output: { keepRuns: 1.5 } } }), /keepRuns must be -1/) + }) + + test('accepts keepRuns = -1 (keep all)', async () => { + clearEnv() + const cfg = await loadConfig({ overrides: { output: { keepRuns: -1 } } }) + assert.equal(cfg.output.keepRuns, -1) + }) +}) diff --git a/evals/tests/unit/evaluate.test.js b/evals/tests/unit/evaluate.test.js new file mode 100644 index 0000000..1c6f3ea --- /dev/null +++ b/evals/tests/unit/evaluate.test.js @@ -0,0 +1,302 @@ +import { test, describe, beforeEach, afterEach } from 'node:test' +import assert from 'node:assert/strict' +import fs from 'fs/promises' +import path from 'path' +import os from 'os' +import { evaluate, evaluateAndCompare } from '../../lib/evaluate.js' + +// ---- fixtures ------------------------------------------------------------- +// A tiny in-memory index + retriever so no ONNX model / network is touched. +const CHUNK_IDS = ['doc-a#0001', 'doc-b#0002', 'doc-c#0003', 'doc-d#0004', 'doc-e#0005'] + +function fakeLoadIndex() { + return async () => ({ + idSet: new Set(CHUNK_IDS), + count: CHUNK_IDS.length + }) +} + +// Retriever that returns a fixed ranking (best-first) for every question. +function fakeRetriever(ranking) { + return async () => async () => ranking +} + +const silentLogger = { log() {}, error() {} } + +let tmpDir +let goldenPath +let runsDir + +async function writeGolden(questions, name = 'test-golden') { + await fs.writeFile(goldenPath, JSON.stringify({ golden_set: name, questions })) +} + +function baseOverrides(extra = {}) { + return { + k: 5, + paths: { goldenSet: goldenPath, runsDir }, + capire_version: '2026.5.0', + ...extra + } +} + +// Read result.jsonl as an array of parsed run reports. +async function readResults() { + const text = await fs.readFile(path.join(runsDir, 'result.jsonl'), 'utf8') + return text.split('\n').filter(Boolean).map(l => JSON.parse(l)) +} + +describe('evaluate tests', () => { + beforeEach(async () => { + tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'evals-cli-')) + goldenPath = path.join(tmpDir, 'golden.json') + runsDir = path.join(tmpDir, 'runs') + }) + afterEach(async () => { + await fs.rm(tmpDir, { recursive: true, force: true }) + }) + + test('happy path: exit 0, appends one line to result.jsonl (no folders, no md)', async () => { + await writeGolden([ + { id: 'q-001', question: 'q1', relevant_doc_ids: ['doc-a#0001'] }, + { id: 'q-002', question: 'q2', relevant_doc_ids: ['doc-b#0002'] } + ]) + const res = await evaluate({ + overrides: baseOverrides(), + logger: silentLogger, + deps: { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } + }) + assert.equal(res.code, 0) + assert.equal(res.report.overall_status, 'pass') + + const rows = await readResults() + assert.equal(rows.length, 1) + assert.equal(rows[0].run_id, res.report.run_id) + assert.equal(rows[0].config.capire_version, '2026.5.0') + assert.equal(rows[0].config.golden_set, 'test-golden') + + // no per-run folders, no report.md, only result.jsonl in runsDir + const entries = await fs.readdir(runsDir) + assert.deepEqual(entries.sort(), ['result.jsonl']) + }) + + test('label is recorded in the report config', async () => { + await writeGolden([{ id: 'q-001', question: 'q1', relevant_doc_ids: ['doc-a#0001'] }]) + const res = await evaluate({ + overrides: baseOverrides({ label: 'tuned chunker' }), + logger: silentLogger, + deps: { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } + }) + assert.ok(res.report.config.label.includes('tuned chunker')) + const rows = await readResults() + assert.ok(rows[0].config.label.includes('tuned chunker')) + }) + + test('a multi-section chunk is credited for every section it covers (its own Source lines)', async () => { + // A chunk spans two headings, each with its own `> Source:` line in the body. + // The golden label is the SECOND section — the chunk should still count as a + // hit because it structurally covers that section (not by substring-matching + // the golden URL against body text). + const secondUrl = 'https://x/docs/get-started/#nodejs-and-cds-dk' + const chunkText = [ + 'Getting Started > Initial Setup > Source: https://x/docs/get-started/#initial-setup', + 'setup body', + '### Node.js and _cds-dk_', + `> Source: ${secondUrl}`, + 'node body' + ].join('\n') + const fakeRetrieverWithText = async () => { + const fn = async () => ['https://x/docs/get-started/#initial-setup'] + fn.lastTexts = [chunkText] + return fn + } + await writeGolden([{ id: 'q-001', question: 'installing cds-dk', relevant_doc_ids: [secondUrl] }]) + const idxWith = async () => ({ idSet: new Set(['https://x/docs/get-started/#initial-setup', secondUrl]), count: 2 }) + const res = await evaluate({ + overrides: baseOverrides(), + logger: silentLogger, + deps: { loadIndex: idxWith, makeRetriever: fakeRetrieverWithText } + }) + // The second section's own Source line → covered → hit → recall=1, not 0. + assert.equal(res.report.per_question[0].metrics.hit_rate_at_k, 1) + assert.equal(res.report.per_question[0].metrics.recall_at_k, 1) + }) + + test('evaluate() does not touch CDS_MCP_OFFLINE (entry point owns the flag)', async () => { + await writeGolden([{ id: 'q-001', question: 'q1', relevant_doc_ids: ['doc-a#0001'] }]) + const deps = { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } + delete process.env.CDS_MCP_OFFLINE + await evaluate({ overrides: baseOverrides(), logger: silentLogger, deps }) + // evaluate() must not set/mutate the env — bin/eval.js sets it once before import. + assert.equal('CDS_MCP_OFFLINE' in process.env, false) + }) + + test('gated failure → exit 1', async () => { + // Relevant docs not retrieved at all → recall/hit-rate 0, below gate. + await writeGolden([{ id: 'q-001', question: 'q1', relevant_doc_ids: ['not-retrieved#9999'] }]) + // 'not-retrieved#9999' isn't in the index → would trip pre-flight; add it to the index. + const idxWithMissing = async () => ({ + idSet: new Set([...CHUNK_IDS, 'not-retrieved#9999']), + count: CHUNK_IDS.length + 1 + }) + const res = await evaluate({ + overrides: baseOverrides(), + logger: silentLogger, + deps: { loadIndex: idxWithMissing, makeRetriever: fakeRetriever(CHUNK_IDS) } + }) + assert.equal(res.code, 1) + assert.equal(res.report.overall_status, 'fail') + assert.ok(res.report.gated_failures.includes('recall_at_k')) + }) + + test('stale golden id → warns, proceeds, scores as a miss (run still written)', async () => { + await writeGolden([{ id: 'q-001', question: 'q1', relevant_doc_ids: ['ghost#dead'] }]) + const res = await evaluate({ + overrides: baseOverrides(), + logger: silentLogger, + deps: { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } + }) + // No abort: the stale id is never retrieved → recall/hit-rate 0 → gated fail (exit 1), + // and the run is still recorded. + assert.equal(res.code, 1) + assert.equal(res.report.per_question[0].metrics.recall_at_k, 0) + await fs.access(path.join(runsDir, 'result.jsonl')) // written, not skipped + }) + + test('missing golden set → exit 3', async () => { + // goldenPath does not exist + const res = await evaluate({ + overrides: baseOverrides(), + logger: silentLogger, + deps: { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } + }) + assert.equal(res.code, 3) + }) + + test('a non-ENOENT error reading the golden set propagates', async () => { + // goldenPath is a directory → readFile fails with EISDIR (not ENOENT) → rethrown. + await fs.mkdir(goldenPath) + await assert.rejects( + () => evaluate({ overrides: baseOverrides(), logger: silentLogger, deps: { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } }), + err => err.code !== 'ENOENT' + ) + }) + + test('malformed golden question → exit 3, no crash, no run written', async () => { + // question missing relevant_doc_ids would previously TypeError in preflight + await writeGolden([{ id: 'q-001', question: 'q1' }]) + const res = await evaluate({ + overrides: baseOverrides(), + logger: silentLogger, + deps: { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } + }) + assert.equal(res.code, 3) + await assert.rejects(() => fs.access(path.join(runsDir, 'result.jsonl'))) + }) + + test('baseline = oldest run: second run diffs against the first', async () => { + await writeGolden([{ id: 'q-001', question: 'q1', relevant_doc_ids: ['doc-a#0001'] }]) + // First run is the baseline (oldest); it has no baseline itself. + const first = await evaluate({ + overrides: baseOverrides(), + logger: silentLogger, + deps: { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } + }) + assert.equal(first.report.baseline_run_id, null) + // Second run with a WORSE ranking (relevant doc pushed to rank 3). + const worse = ['doc-x#000x', 'doc-y#000y', 'doc-a#0001', 'doc-b#0002', 'doc-c#0003'] + const second = await evaluate({ + overrides: baseOverrides(), + logger: silentLogger, + deps: { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(worse) } + }) + assert.equal(second.report.baseline_run_id, first.report.run_id) // diffed vs oldest + assert.ok(second.report.aggregate.mrr.delta < 0) // mrr dropped vs baseline + assert.match(second.report.diagnosis, /ranking\/scoring regression/) + }) + + test('pinned baselineRunId: diffs against the pinned run, not the oldest', async () => { + await writeGolden([{ id: 'q-001', question: 'q1', relevant_doc_ids: ['doc-a#0001'] }]) + const deps = { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } + const r1 = await evaluate({ overrides: baseOverrides(), logger: silentLogger, deps }) + const r2 = await evaluate({ overrides: baseOverrides(), logger: silentLogger, deps }) + // Pin the SECOND run as baseline for a third run — not the oldest (r1). + const r3 = await evaluate({ + overrides: baseOverrides({ baselineRunId: r2.report.run_id }), + logger: silentLogger, + deps + }) + assert.equal(r3.report.baseline_run_id, r2.report.run_id) + assert.notEqual(r3.report.baseline_run_id, r1.report.run_id) + }) + + test('pinned baselineRunId not found → no baseline, no crash', async () => { + await writeGolden([{ id: 'q-001', question: 'q1', relevant_doc_ids: ['doc-a#0001'] }]) + const deps = { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } + await evaluate({ overrides: baseOverrides(), logger: silentLogger, deps }) + const r = await evaluate({ + overrides: baseOverrides({ baselineRunId: 'no-such-run' }), + logger: silentLogger, + deps + }) + assert.equal(r.report.baseline_run_id, null) + }) + + test('result.jsonl accumulates one line per run, chronologically', async () => { + await writeGolden([{ id: 'q-001', question: 'q1', relevant_doc_ids: ['doc-a#0001'] }]) + const deps = { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } + for (let i = 0; i < 3; i++) { + await evaluate({ overrides: baseOverrides({ output: { keepRuns: 20 } }), logger: silentLogger, deps }) + } + const rows = await readResults() + assert.equal(rows.length, 3) + // sorted by run_id ascending + const ids = rows.map(r => r.run_id) + assert.deepEqual(ids, [...ids].sort()) + }) + + test('run hygiene: caps result.jsonl to the most recent keepRuns lines', async () => { + await writeGolden([{ id: 'q-001', question: 'q1', relevant_doc_ids: ['doc-a#0001'] }]) + const deps = { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } + for (let i = 0; i < 5; i++) { + await evaluate({ overrides: baseOverrides({ output: { keepRuns: 2 } }), logger: silentLogger, deps }) + } + const rows = await readResults() + assert.equal(rows.length, 2) // capped to keepRuns, newest kept + }) + + test('custom resultsName is honored', async () => { + await writeGolden([{ id: 'q-001', question: 'q1', relevant_doc_ids: ['doc-a#0001'] }]) + await evaluate({ + overrides: baseOverrides({ output: { keepRuns: 20, resultsName: 'runs.jsonl' } }), + logger: silentLogger, + deps: { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } + }) + const entries = await fs.readdir(runsDir) + assert.deepEqual(entries.sort(), ['runs.jsonl']) + }) + + test('evaluateAndCompare runs the eval once and writes compare', async () => { + await writeGolden([{ id: 'q-001', question: 'q1', relevant_doc_ids: ['doc-a#0001'] }]) + const res = await evaluateAndCompare({ + overrides: baseOverrides(), + logger: silentLogger, + deps: { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } + }) + assert.equal(res.code, 0) + const rows = await readResults() + assert.equal(rows.length, 1) + await fs.access(path.join(runsDir, 'compare.html')) + }) + + test('evaluateAndCompare stops on a hard error (missing golden set) and still compares', async () => { + // No golden set written → run() returns exit 3 (hard error). + const res = await evaluateAndCompare({ + overrides: baseOverrides(), + logger: silentLogger, + deps: { loadIndex: fakeLoadIndex(), makeRetriever: fakeRetriever(CHUNK_IDS) } + }) + assert.equal(res.code, 3) + await assert.rejects(() => fs.access(path.join(runsDir, 'result.jsonl'))) + }) +}) diff --git a/evals/tests/unit/ids.test.js b/evals/tests/unit/ids.test.js new file mode 100644 index 0000000..918b963 --- /dev/null +++ b/evals/tests/unit/ids.test.js @@ -0,0 +1,167 @@ +import { test, describe } from 'node:test' +import assert from 'node:assert/strict' +import { parseId, resolveChunkIds, buildIdMap, buildTextMap, resolveIds } from '../../lib/ids.js' + +describe('ids tests', () => { + test('parseId is the Source: URL from the first line (with its #section anchor)', () => { + const text = + 'Getting Started > Initial Setup > Source: https://cap.cloud.sap/docs/get-started/#initial-setup\nbody text here' + assert.equal(parseId(text), 'https://cap.cloud.sap/docs/get-started/#initial-setup') + }) + + test('parseId is deterministic (pure string parse)', () => { + const text = 'Getting Started > Setup > Source: https://x/y#setup\nmore' + assert.equal(parseId(text), parseId(text)) + assert.equal(parseId(text), 'https://x/y#setup') + }) + + test('parseId ignores the body and breadcrumb — same URL → same id', () => { + const a = parseId('Heading A > Source: https://x/p#s\nbody one') + const b = parseId('Different Heading > Source: https://x/p#s\nbody two') + assert.equal(a, b) + assert.equal(a, 'https://x/p#s') + }) + + test('parseId finds the chunk\'s own Source: line even when it is not line 1', () => { + // Real corpus chunks put the Source: a couple of lines below the heading: + // "## Initial Setup\n\n> Source: /docs/get-started/#initial-setup" + const chunk = '## Initial Setup\n\n> Source: /docs/get-started/#initial-setup\nbody' + assert.equal(parseId(chunk), '/docs/get-started/#initial-setup') + }) + + test('parseId returns null when there is no Source: line at all (no synthetic id)', () => { + // Malformed chunks are not scored — the eval does not invent ids for them. + assert.equal(parseId('Getting Started > Initial Setup\nsome text'), null) + assert.equal(parseId(''), null) + }) + + test('resolveChunkIds: single-section chunk → just its first-line Source', () => { + const text = 'Domain > Primary Keys\n> Source: /docs/guides/domain#primary-keys\nbody' + assert.deepEqual(resolveChunkIds(text), ['/docs/guides/domain#primary-keys']) + }) + + test('resolveChunkIds: multi-section chunk collects every in-body Source line', () => { + const text = [ + '# Getting Started', + '> Source: /docs/get-started/', + 'intro', + '## Initial Setup', + '> Source: /docs/get-started/#initial-setup', + 'setup body', + '### Node.js and _cds-dk_', + '> Source: /docs/get-started/#nodejs-and-cds-dk', + 'node body' + ].join('\n') + assert.deepEqual(resolveChunkIds(text), [ + '/docs/get-started/', + '/docs/get-started/#initial-setup', + '/docs/get-started/#nodejs-and-cds-dk' + ]) + }) + + test('resolveChunkIds: a heading whose Source line was split off is resolved via the tree', () => { + // The chunk ends right after the heading — its Source line got cut at the + // chunk boundary. The page-scoped source tree recovers it. + const text = [ + '## Initial Setup', + '> Source: /docs/get-started/#initial-setup', + 'setup body', + '### Node.js and _cds-dk_' // no Source line follows + ].join('\n') + const sourceIndex = { + byHeadingInPage: { + '/docs/get-started/': { 'node.js and _cds-dk_': '/docs/get-started/#nodejs-and-cds-dk' } + } + } + assert.deepEqual(resolveChunkIds(text, sourceIndex), [ + '/docs/get-started/#initial-setup', + '/docs/get-started/#nodejs-and-cds-dk' + ]) + }) + + test('resolveChunkIds: without a tree, a split-off heading is simply not credited', () => { + const text = '## Initial Setup\n> Source: /docs/get-started/#initial-setup\nbody\n### Node.js and _cds-dk_' + assert.deepEqual(resolveChunkIds(text, null), ['/docs/get-started/#initial-setup']) + }) + + test('resolveChunkIds: no first-line Source → empty', () => { + assert.deepEqual(resolveChunkIds('breadcrumb only\nbody'), []) + }) + + test('resolveChunkIds dedups repeated sections', () => { + const text = '## A\n> Source: /docs/p#a\nbody\n## A again\n> Source: /docs/p#a\nmore' + assert.deepEqual(resolveChunkIds(text), ['/docs/p#a']) + }) + + test('buildIdMap returns distinct ids + a Set, collapsing same-url chunks', () => { + const chunks = [ + 'Alpha > Source: https://x/a#alpha\nbody 1', + 'Beta > Source: https://x/b#beta\nbody 2', + 'Alpha again > Source: https://x/a#alpha\nbody 3' // same URL → same id + ] + const { ids, idSet } = buildIdMap(chunks) + assert.equal(ids.length, 2) // duplicate id collapsed + assert.equal(idSet.size, 2) + assert.ok(idSet.has('https://x/a#alpha')) + assert.ok(idSet.has('https://x/b#beta')) + }) + + test('buildIdMap drops chunks with no first-line Source: URL', () => { + const chunks = [ + 'Setup > Source: https://x/setup#brew\ninstall homebrew', + 'more brew install steps', // no Source → dropped + 'Next > Source: https://x/next#go\ndifferent page' + ] + const { ids } = buildIdMap(chunks) + assert.deepEqual(ids, ['https://x/setup#brew', 'https://x/next#go']) + }) + + test('buildIdMap keeps distinct sections of the same page as distinct ids', () => { + const chunks = [ + 'Auth > Role-Based > Source: https://x/auth#role-based\nbody', + 'Auth > Instance-Based > Source: https://x/auth#instance-based\nbody' + ] + const { ids } = buildIdMap(chunks) + assert.equal(ids.length, 2) // same page, different #anchor → different id + assert.ok(ids.every(id => id.startsWith('https://x/auth#'))) + }) + + test('buildTextMap maps every id back to its chunk text', () => { + const chunks = [ + 'Setup > Source: https://x/setup#brew\ninstall homebrew', + 'Next > Source: https://x/next#go\nbody' + ] + const map = buildTextMap(chunks) + assert.equal(map.get('https://x/setup#brew'), chunks[0]) + assert.equal(map.get('https://x/next#go'), chunks[1]) + }) + + test('resolveIds drops URL-less slots, keeps aligned text for the rest', () => { + const retrieved = [ + 'A > Source: https://x/a#s\nbody', + 'B > Source: https://x/b#s\nbody', + 'breadcrumb only', // no Source → dropped + 'C > Source: https://x/c#s\nbody' + ] + const { ids, texts } = resolveIds(retrieved, []) + assert.deepEqual(ids, ['https://x/a#s', 'https://x/b#s', 'https://x/c#s']) + // texts aligned with the kept ids + assert.deepEqual(texts, [retrieved[0], retrieved[1], retrieved[3]]) + }) + + test('resolveIds keeps distinct per-slot text for two slots sharing an id', () => { + const retrieved = [ + 'Domain > Primary Keys > Source: https://x/d#pk\nbody one', + 'Domain > Primary Keys > Source: https://x/d#pk\nbody two' + ] + const { ids, texts } = resolveIds(retrieved, []) + assert.deepEqual(ids, ['https://x/d#pk', 'https://x/d#pk']) // same id, both slots + assert.notEqual(texts[0], texts[1]) // but distinct text per slot + assert.deepEqual(texts, retrieved) + }) + + test('resolveIds does not dedup — repeated page fills repeated slots', () => { + const retrieved = ['P > Source: https://x/p#s\n1', 'P again > Source: https://x/p#s\n2'] + assert.deepEqual(resolveIds(retrieved, []).ids, ['https://x/p#s', 'https://x/p#s']) + }) +}) diff --git a/evals/tests/unit/metrics.test.js b/evals/tests/unit/metrics.test.js new file mode 100644 index 0000000..5b71aac --- /dev/null +++ b/evals/tests/unit/metrics.test.js @@ -0,0 +1,140 @@ +import { test, describe } from 'node:test' +import assert from 'node:assert/strict' +import { + recallAtK, + precisionAtK, + mrr, + hitRateAtK, + ndcgAtK, + metricsFor, + relevantHitsAtRank, + mean, + round +} from '../../lib/metrics.js' + +// Worked example from the spec (cap-001): +// relevant = [compositions, managed-compositions] +// retrieved = [associations, compositions, domain-modeling, managed-compositions, entities] +// k = 5 → relevant hits at ranks 2 and 4 +// expected: recall 1.0, precision 0.4, mrr 0.5, hit_rate 1, ndcg ≈ 0.651 +const REL = ['compositions', 'managed-compositions'] +const RET = ['associations', 'compositions', 'domain-modeling', 'managed-compositions', 'entities'] + +describe('metrics tests', () => { + test('worked example: relevant hit ranks', () => { + assert.deepEqual(relevantHitsAtRank(REL, RET, 5), [2, 4]) + }) + + test('worked example: recall@5 = 1.0', () => { + assert.equal(recallAtK(REL, RET, 5), 1.0) + }) + + test('worked example: precision@5 = 0.4', () => { + assert.equal(precisionAtK(REL, RET, 5), 0.4) + }) + + test('worked example: mrr = 0.5 (first hit at rank 2)', () => { + assert.equal(mrr(REL, RET, 5), 0.5) + }) + + test('worked example: hit_rate@5 = 1', () => { + assert.equal(hitRateAtK(REL, RET, 5), 1) + }) + + test('worked example: ndcg@5 ≈ 0.651', () => { + assert.equal(round(ndcgAtK(REL, RET, 5), 3), 0.651) + }) + + test('worked example: metricsFor bundles all five', () => { + const m = metricsFor(REL, RET, 5) + assert.equal(m.recall_at_k, 1.0) + assert.equal(m.precision_at_k, 0.4) + assert.equal(m.mrr, 0.5) + assert.equal(m.hit_rate_at_k, 1) + assert.equal(round(m.ndcg_at_k, 3), 0.651) + }) + + test('no relevant hits in top-k → zeros', () => { + const rel = ['x'] + const ret = ['a', 'b', 'c'] + assert.equal(recallAtK(rel, ret, 3), 0) + assert.equal(precisionAtK(rel, ret, 3), 0) + assert.equal(mrr(rel, ret, 3), 0) + assert.equal(hitRateAtK(rel, ret, 3), 0) + assert.equal(ndcgAtK(rel, ret, 3), 0) + }) + + test('perfect ranking → all ones (ndcg=1)', () => { + const rel = ['a', 'b'] + const ret = ['a', 'b', 'c', 'd'] + assert.equal(recallAtK(rel, ret, 2), 1) + assert.equal(mrr(rel, ret, 2), 1) + assert.equal(hitRateAtK(rel, ret, 2), 1) + assert.equal(ndcgAtK(rel, ret, 2), 1) + }) + + test('first hit at rank 1 → mrr 1.0', () => { + assert.equal(mrr(['a'], ['a', 'b', 'c'], 5), 1.0) + }) + + test('first hit at rank 4 → mrr 0.25', () => { + assert.equal(mrr(['d'], ['a', 'b', 'c', 'd', 'e'], 5), 0.25) + }) + + test('duplicate relevant slots: recall counts distinct, precision counts slots', () => { + // 'a' fills two of the three slots (a page returned twice). + const rel = ['a', 'b'] + const ret = ['a', 'a', 'b'] + assert.equal(recallAtK(rel, ret, 3), 1) // distinct {a,b} found → 2/2 + assert.equal(precisionAtK(rel, ret, 3), 1) // 3 relevant slots / 3 = 1 (dupes count) + assert.equal(mrr(rel, ret, 3), 1) // first relevant slot at rank 1 + assert.equal(hitRateAtK(rel, ret, 3), 1) + }) + + test('recall never exceeds 1 when a relevant page fills every slot', () => { + assert.equal(recallAtK(['a'], ['a', 'a', 'a'], 3), 1) // one distinct relevant doc + assert.equal(precisionAtK(['a'], ['a', 'a', 'a'], 3), 1) // 3/3 slots relevant + }) + + test('ndcg dedups relevant gain → duplicates cannot mask a bad rank', () => { + // 'a' relevant, first appears at rank 2 then repeats at rank 3. + // Old raw-slot sum: 1/log2(3)+1/log2(4)=1.131 → clamped to 1.0 (masked!). + // Deduped: credit 'a' once at its best rank (2) → 1/log2(3)/1 ≈ 0.631. + assert.equal(round(ndcgAtK(['a'], ['x', 'a', 'a'], 3), 3), 0.631) + // a genuinely perfect ranking (rank 1) still scores 1.0 + assert.equal(ndcgAtK(['a'], ['a', 'a', 'b'], 3), 1) + }) + + test('precision divides by k even when fewer results returned', () => { + // Only 2 retrieved, 1 relevant, k=5 → precision 1/5, not 1/2. + assert.equal(precisionAtK(['a'], ['a', 'b'], 5), 0.2) + }) + + test('recall is fraction of relevant set, capped by what fits in k', () => { + // 3 relevant, only 2 fit in top-2 → recall 2/3. + const rel = ['a', 'b', 'c'] + const ret = ['a', 'b', 'x', 'c'] + assert.equal(recallAtK(rel, ret, 2), 2 / 3) + }) + + test('round() is deterministic on exact halves', () => { + assert.equal(round(0.005, 2), 0.01) + assert.equal(round(0.125, 2), 0.13) + assert.equal(round(0.6510, 3), 0.651) + }) + + test('round() is symmetric for negatives (no -0, magnitude preserved)', () => { + assert.equal(round(-0.005, 2), -0.01) // mirrors +0.005 → 0.01, not -0 + assert.equal(round(-0.015, 2), -0.02) + assert.equal(round(-0.025, 2), -0.03) + assert.ok(!Object.is(round(-0.004, 2), -0)) // rounds to a clean 0, not -0 + assert.equal(round(-0.004, 2), 0) + }) + + test('edge guards: empty relevant set and k<=0 → 0', () => { + assert.equal(recallAtK([], ['a'], 5), 0) // no relevant docs + assert.equal(ndcgAtK([], ['a'], 5), 0) + assert.equal(precisionAtK(['a'], ['a'], 0), 0) // k = 0 + assert.equal(mean([]), 0) // empty mean + }) +}) \ No newline at end of file diff --git a/evals/tests/unit/report.test.js b/evals/tests/unit/report.test.js new file mode 100644 index 0000000..db806f6 --- /dev/null +++ b/evals/tests/unit/report.test.js @@ -0,0 +1,244 @@ +import { test, describe } from 'node:test' +import assert from 'node:assert/strict' +import { + buildReport, + diagnose, + preflight, + validateGolden, + makeRunId +} from '../../lib/report.js' + +const GATES = { + recall_at_k: 0.8, + mrr: 0.5, + hit_rate_at_k: 0.8, + precision_at_k: null, + ndcg_at_k: null +} + +const CONFIG = { + capire_version: '2026.5.0', + golden_set: 'test-set', + golden_set_size: 2, + k: 5 +} + +// Two-question fixture. Deterministic — no retriever, no ONNX. +function fixtureRaw(ret1, ret2) { + return [ + { id: 'q-002', question: 'second', relevant_doc_ids: ['b'], retrieved_ids: ret2 }, + { id: 'q-001', question: 'first', relevant_doc_ids: ['a'], retrieved_ids: ret1 } + ] +} + +describe('eval tests', () => { + test('pre-flight detects stale relevant ids', () => { + const idSet = new Set(['a', 'c']) + const stale = preflight([{ id: 'q1', relevant_doc_ids: ['a', 'b'] }], idSet) + assert.deepEqual(stale, [{ question: 'q1', doc_id: 'b' }]) + }) + + test('validateGolden: accepts a well-formed set', () => { + const ok = [ + { id: 'q-001', question: 'a?', relevant_doc_ids: ['x'] }, + { id: 'q-002', question: 'b?', relevant_doc_ids: ['y', 'z'] } + ] + assert.deepEqual(validateGolden(ok), []) + }) + + test('validateGolden: catches missing/empty/duplicate/null cases', () => { + const bad = [ + { id: 'q-001', question: 'a?', relevant_doc_ids: ['x'] }, + { id: 'q-001', question: 'dup id', relevant_doc_ids: ['y'] }, // duplicate id + { id: 'q-002', question: 'no rel' }, // missing relevant_doc_ids + { id: 'q-003', question: 'empty', relevant_doc_ids: [] }, // empty ground truth + { id: 'q-004', question: 'nullid', relevant_doc_ids: [null] }, // null entry + { id: 'q-005', relevant_doc_ids: ['z'] }, // missing question + null // not an object at all + ] + const problems = validateGolden(bad) + assert.ok(problems.some(p => /duplicate id/.test(p))) + assert.ok(problems.some(p => /q-002.*must be an array/.test(p))) + assert.ok(problems.some(p => /q-003.*empty/.test(p))) + assert.ok(problems.some(p => /q-004.*non-string\/empty/.test(p))) + assert.ok(problems.some(p => /q-005.*missing non-empty "question"/.test(p))) + assert.ok(problems.some(p => /not an object/.test(p))) + }) + + test('per_question is sorted by id ascending', () => { + const r = buildReport({ + config: CONFIG, + perQuestionRaw: fixtureRaw(['a'], ['b']), + baseline: null, + gates: GATES + }) + assert.deepEqual(r.per_question.map(q => q.id), ['q-001', 'q-002']) + }) + + test('healthy run → overall pass, exit-worthy status pass', () => { + const r = buildReport({ + config: CONFIG, + perQuestionRaw: fixtureRaw(['a', 'x', 'y'], ['b', 'x', 'y']), + baseline: null, + gates: GATES + }) + assert.equal(r.overall_status, 'pass') + assert.deepEqual(r.gated_failures, []) + assert.equal(r.aggregate.recall_at_k.status, 'pass') + assert.equal(r.aggregate.precision_at_k.status, 'info') // gate null + }) + + test('MRR regression → gated failure + ranking diagnosis', () => { + // baseline: both relevant at rank 1 (mrr 1.0) + const baseReport = buildReport({ + config: CONFIG, + perQuestionRaw: fixtureRaw(['a'], ['b']), + baseline: null, + gates: GATES + }) + const baseline = { run_id: 'base_1', ...baseReport } + // now: relevant docs pushed to rank 4 → mrr 0.25 each, recall still 1 + const r = buildReport({ + config: CONFIG, + perQuestionRaw: fixtureRaw(['x', 'y', 'z', 'a'], ['x', 'y', 'z', 'b']), + baseline, + gates: GATES + }) + assert.equal(r.aggregate.recall_at_k.value, 1) // recall stable + assert.ok(r.aggregate.mrr.value < 0.5) // mrr below gate + assert.equal(r.aggregate.mrr.status, 'fail') + assert.deepEqual(r.gated_failures, ['mrr']) + assert.equal(r.overall_status, 'fail') + assert.match(r.diagnosis, /ranking\/scoring regression/) + assert.equal(r.baseline_run_id, 'base_1') + }) + + test('zero baseline value yields a real delta, not null', () => { + // baseline: relevant docs absent → recall/mrr aggregate 0.00 + const baseReport = buildReport({ + config: CONFIG, + perQuestionRaw: fixtureRaw(['x', 'y'], ['x', 'y']), + baseline: null, + gates: GATES + }) + assert.equal(baseReport.aggregate.mrr.value, 0) // precondition: baseline is 0 + const baseline = { run_id: 'base_0', ...baseReport } + // now: both relevant at rank 1 → mrr 1.0, a genuine improvement from 0 + const r = buildReport({ + config: CONFIG, + perQuestionRaw: fixtureRaw(['a'], ['b']), + baseline, + gates: GATES + }) + assert.equal(r.aggregate.mrr.baseline, 0) + assert.equal(r.aggregate.mrr.delta, 1) // was masked to null before the fix + }) + + test('recall regression → chunking/embedding diagnosis (first match wins)', () => { + const baseReport = buildReport({ + config: CONFIG, + perQuestionRaw: fixtureRaw(['a'], ['b']), + baseline: null, + gates: GATES + }) + const baseline = { run_id: 'base_2', ...baseReport } + // relevant docs fall out of top-5 entirely → recall down (and mrr down, but recall wins) + const r = buildReport({ + config: CONFIG, + perQuestionRaw: fixtureRaw(['x', 'y', 'z', 'p', 'q'], ['x', 'y', 'z', 'p', 'q']), + baseline, + gates: GATES + }) + assert.ok(r.aggregate.recall_at_k.delta < 0) + assert.match(r.diagnosis, /chunking\/embedding regression/) + assert.deepEqual(r.gated_failures.sort(), ['hit_rate_at_k', 'mrr', 'recall_at_k']) + assert.equal(r.overall_status, 'fail') + }) + + test('diagnose(): precision-only drop', () => { + const agg = { + recall_at_k: { delta: 0 }, + mrr: { delta: 0 }, + ndcg_at_k: { delta: 0 }, + precision_at_k: { delta: -0.1 } + } + assert.match(diagnose(agg), /top-K padded with noise/) + }) + + test('diagnose(): no regression', () => { + const agg = { + recall_at_k: { delta: 0.02 }, + mrr: { delta: 0.01 }, + ndcg_at_k: { delta: 0 }, + precision_at_k: { delta: 0 } + } + assert.equal(diagnose(agg), 'no_regression') + }) + + test('diagnose(): a within-dead-band drop is NOT a regression', () => { + // -0.02 is exactly the dead-band; must not trip a cause (noise on n=10). + const agg = { + recall_at_k: { delta: -0.02 }, + mrr: { delta: -0.01 }, + ndcg_at_k: { delta: -0.02 }, + precision_at_k: { delta: -0.02 } + } + assert.equal(diagnose(agg), 'no_regression') + }) + + test('diagnose(): reports ALL causes above dead-band, not first-match-wins', () => { + // recall AND precision both drop meaningfully → both reported. + const agg = { + recall_at_k: { delta: -0.1 }, + mrr: { delta: 0 }, + ndcg_at_k: { delta: 0 }, + precision_at_k: { delta: -0.1 } + } + const d = diagnose(agg) + assert.match(d, /chunking\/embedding regression/) + assert.match(d, /top-K padded with noise/) + assert.ok(d.includes(';')) // multiple causes joined + }) + + test('delta rounding: aggregate 2dp, per-question 3dp', () => { + const baseReport = buildReport({ + config: CONFIG, + perQuestionRaw: fixtureRaw(['a'], ['b']), + baseline: null, + gates: GATES + }) + const baseline = { run_id: 'base_3', ...baseReport } + const r = buildReport({ + config: CONFIG, + perQuestionRaw: fixtureRaw(['x', 'a'], ['b']), // q-001 mrr 0.5, q-002 mrr 1.0 → avg 0.75 + baseline, + gates: GATES + }) + // aggregate value rounded to 2dp + assert.equal(r.aggregate.mrr.value, 0.75) + assert.equal(r.aggregate.mrr.delta, -0.25) // 0.75 - 1.00 + // per-question metric 3dp precision + const q1 = r.per_question.find(q => q.id === 'q-001') + assert.equal(q1.metrics.mrr, 0.5) + }) + + test('DETERMINISM: same inputs → byte-identical report (ignoring run_id)', () => { + const r1 = buildReport({ config: CONFIG, perQuestionRaw: fixtureRaw(['a', 'x'], ['x', 'b']), baseline: null, gates: GATES }) + const r2 = buildReport({ config: CONFIG, perQuestionRaw: fixtureRaw(['a', 'x'], ['x', 'b']), baseline: null, gates: GATES }) + assert.equal(JSON.stringify(r1), JSON.stringify(r2)) + }) + + test('makeRunId is deterministic when now + rand are fixed', () => { + const id = makeRunId(new Date('2026-07-30T07:11:55.123Z'), 'abc123') + assert.equal(id, '2026-07-30T07:11:55.123Z_abc123') + }) + + test('makeRunId keeps millisecond precision → same-second runs sort by time', () => { + // Two runs 4ms apart in the same wall-clock second must order by time, + // not by the random suffix. + const early = makeRunId(new Date('2026-07-30T07:11:55.001Z'), 'zzzzzz') + const late = makeRunId(new Date('2026-07-30T07:11:55.005Z'), 'aaaaaa') + assert.ok(early < late) // string sort respects the ms component + }) + +}) \ No newline at end of file diff --git a/evals/tests/unit/search-docs.test.js b/evals/tests/unit/search-docs.test.js new file mode 100644 index 0000000..4f06c9c --- /dev/null +++ b/evals/tests/unit/search-docs.test.js @@ -0,0 +1,93 @@ +import { test, describe, beforeEach, afterEach } from 'node:test' +import assert from 'node:assert/strict' +import fs from 'fs/promises' +import path from 'path' +import os from 'os' +import { loadIndex, loadChunkText, makeSearchDocsRunner } from '../../lib/search-docs.js' + +// A fixture corpus: two real sections + a chunk with no first-line Source: URL. +// A URL-less chunk has no id and is dropped (not scored) — the eval scores the +// tool's real output against correctly-formatted input, it does not invent ids. +const CORPUS = { + dim: 3, + count: 3, + chunks: [ + 'Getting Started > Setup > Source: https://x/setup#a\nsetup body', + 'CDS > CDL > Source: https://x/cdl#b\ncdl body', + 'more cdl detail' // no Source: URL → dropped + ] +} + +describe('search-docs tests', () => { + let tmpDir, corpusPath + beforeEach(async () => { + tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'evals-retriever-')) + corpusPath = path.join(tmpDir, 'code-chunks.json') + await fs.writeFile(corpusPath, JSON.stringify(CORPUS)) + }) + afterEach(async () => { + await fs.rm(tmpDir, { recursive: true, force: true }) + }) + + test('loadIndex parses distinct ids + count from the corpus (URL-less dropped)', async () => { + const idx = await loadIndex(corpusPath) + assert.deepEqual(idx.ids, ['https://x/setup#a', 'https://x/cdl#b']) + assert.equal(idx.count, 2) + assert.ok(idx.idSet.has('https://x/setup#a')) + }) + + test('loadChunkText maps every id back to its chunk text', async () => { + const map = await loadChunkText(corpusPath) + assert.equal(map.get('https://x/setup#a'), CORPUS.chunks[0]) + assert.equal(map.get('https://x/cdl#b'), CORPUS.chunks[1]) + }) + + test('loadIndex throws on a corrupt/missing corpus', async () => { + await fs.writeFile(corpusPath, '{"not":"chunks"}') + await assert.rejects(() => loadIndex(corpusPath), /Corrupt or missing corpus/) + await assert.rejects(() => loadIndex(path.join(tmpDir, 'nope.json')), /ENOENT|Corrupt/) + }) + + test('makeSearchDocsRunner resolves search_docs output to corpus-consistent ids', async () => { + const searchDocs = { + handler: async ({ maxResults }) => { + assert.equal(maxResults, 5) + return `${CORPUS.chunks[0]}\n---\n${CORPUS.chunks[1]}` + } + } + const retrieve = await makeSearchDocsRunner(5, { searchDocs, corpusPath }) + assert.deepEqual(await retrieve('q'), ['https://x/setup#a', 'https://x/cdl#b']) + assert.deepEqual(retrieve.lastTexts, [CORPUS.chunks[0], CORPUS.chunks[1]]) + }) + + test('a retrieved chunk with no Source: URL is dropped from the id list', async () => { + const searchDocs = { handler: async () => `${CORPUS.chunks[0]}\n---\nmore cdl detail` } + const retrieve = await makeSearchDocsRunner(5, { searchDocs, corpusPath }) + assert.deepEqual(await retrieve('q'), ['https://x/setup#a']) + }) + + test('retriever returns [] when search_docs returns nothing', async () => { + const searchDocs = { handler: async () => '' } + const retrieve = await makeSearchDocsRunner(5, { searchDocs, corpusPath }) + assert.deepEqual(await retrieve('q'), []) + }) + + test('switching corpusPath (model change) uses the new model corpus for id resolution', async () => { + // Two corpora with different Source URLs; each retriever resolves against its own. + const corpusA = { chunks: ['A > Page > Source: https://x/page-a#s\nbody'] } + const corpusB = { chunks: ['B > Page > Source: https://x/page-b#s\nbody'] } + const pathA = path.join(tmpDir, 'corpus-a.json') + const pathB = path.join(tmpDir, 'corpus-b.json') + await fs.writeFile(pathA, JSON.stringify(corpusA)) + await fs.writeFile(pathB, JSON.stringify(corpusB)) + + const sdA = { handler: async () => corpusA.chunks[0] } + const sdB = { handler: async () => corpusB.chunks[0] } + + const retrieveA = await makeSearchDocsRunner(5, { searchDocs: sdA, corpusPath: pathA }) + const retrieveB = await makeSearchDocsRunner(5, { searchDocs: sdB, corpusPath: pathB }) + + assert.deepEqual(await retrieveA('q'), ['https://x/page-a#s']) + assert.deepEqual(await retrieveB('q'), ['https://x/page-b#s']) + }) +}) diff --git a/evals/tests/unit/store.test.js b/evals/tests/unit/store.test.js new file mode 100644 index 0000000..a79ff51 --- /dev/null +++ b/evals/tests/unit/store.test.js @@ -0,0 +1,28 @@ +import { test, describe, beforeEach, afterEach } from 'node:test' +import assert from 'node:assert/strict' +import fs from 'fs/promises' +import path from 'path' +import os from 'os' +import { readRuns, resultsPath } from '../../lib/store.js' + +const cfg = dir => ({ paths: { runsDir: dir }, output: { resultsName: 'result.jsonl' } }) + +describe('store tests', () => { + let tmpDir + beforeEach(async () => { + tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'evals-store-')) + }) + afterEach(async () => { + await fs.rm(tmpDir, { recursive: true, force: true }) + }) + + test('readRuns returns [] when the results file does not exist (ENOENT)', async () => { + assert.deepEqual(await readRuns(cfg(tmpDir)), []) + }) + + test('readRuns rethrows a non-ENOENT read error', async () => { + // Make resultsPath a directory → reading it fails with EISDIR, not ENOENT. + await fs.mkdir(resultsPath(cfg(tmpDir))) + await assert.rejects(() => readRuns(cfg(tmpDir)), err => err.code !== 'ENOENT') + }) +}) diff --git a/lib/embeddings.js b/lib/embeddings.js index dda0f98..9fc05b2 100644 --- a/lib/embeddings.js +++ b/lib/embeddings.js @@ -115,13 +115,29 @@ export async function searchEmbeddings(query, chunks) { return scoredChunks } -// Only to be used in scripts, not in production +// Only to be used in scripts, not in production. +// Progress is logged every LOG_EVERY chunks (override with EMBEDDINGS_LOG_EVERY=N, +// set to 0 to suppress all progress output). export async function createEmbeddings(id, chunks, dir = path.join(__dirname, '..', 'embeddings')) { + const logEvery = Number(process.env.EMBEDDINGS_LOG_EVERY ?? 50) + const startTime = Date.now() const embeddings = [] for (let i = 0; i < chunks.length; i++) { const embedding = await getEmbeddings(chunks[i]) embeddings.push(embedding) + + if (logEvery > 0 && ((i + 1) % logEvery === 0 || i + 1 === chunks.length)) { + const elapsed = (Date.now() - startTime) / 1000 + const rate = (i + 1) / elapsed + const remaining = Math.round((chunks.length - i - 1) / rate) + const pct = Math.round((i + 1) / chunks.length * 100) + // eslint-disable-next-line no-console + console.log( + `Embedding ${i + 1}/${chunks.length} (${pct}%) — ` + + `${elapsed.toFixed(0)}s elapsed, ~${remaining}s remaining` + ) + } } await saveEmbeddings(id, chunks, embeddings, dir) diff --git a/package.json b/package.json index 1213439..aecddf8 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,11 @@ }, "scripts": { "test": "node --test --test-concurrency=1", - "lint": "npx eslint ." + "lint": "npx eslint .", + "evals": "node evals/bin/eval.js", + "evals:compare": "node evals/bin/compare.js", + "evals:build-source-map": "node evals/lib/buildSourceMap.js", + "evals:test": "node --test --test-concurrency=1 evals/tests/unit/*.test.js" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.29.0", diff --git a/tests/embedding-progress.test.js b/tests/embedding-progress.test.js new file mode 100644 index 0000000..a6914d7 --- /dev/null +++ b/tests/embedding-progress.test.js @@ -0,0 +1,66 @@ +// Tests for progress logging in createEmbeddings (lib/embeddings.js). +// +// Run: node --test tests/embedding-progress.test.js + +import { test } from 'node:test' +import assert from 'node:assert/strict' +import fs from 'node:fs/promises' +import os from 'node:os' +import path from 'node:path' + +import { createEmbeddings } from '../lib/embeddings.js' + +const CHUNKS = ['alpha', 'beta', 'gamma', 'delta', 'epsilon', + 'zeta', 'eta', 'theta', 'iota', 'kappa'] // 10 chunks + +async function tmpDir() { + return fs.mkdtemp(path.join(os.tmpdir(), 'emb-progress-')) +} + +async function captureLog(fn) { + const lines = [] + const orig = console.log + console.log = (...args) => lines.push(args.join(' ')) + try { await fn() } finally { console.log = orig } + return lines +} + +test('progress fires every LOG_EVERY chunks and always on the last chunk', async () => { + const dir = await tmpDir() + process.env.EMBEDDINGS_LOG_EVERY = '3' // fires at 3, 6, 9, 10 + + const lines = await captureLog(() => createEmbeddings('code-chunks', CHUNKS, dir)) + + delete process.env.EMBEDDINGS_LOG_EVERY + await fs.rm(dir, { recursive: true, force: true }) + + const progress = lines.filter(l => /^Embedding \d+\/\d+/.test(l)) + assert.ok(progress.length >= 3, `expected at least 3 progress lines, got ${progress.length}`) + const last = progress[progress.length - 1] + assert.ok(last.includes('10/10'), `last line should be 10/10, got: ${last}`) + assert.ok(last.includes('100%'), `last line should show 100%, got: ${last}`) +}) + +test('progress line contains elapsed time and ETA', async () => { + const dir = await tmpDir() + process.env.EMBEDDINGS_LOG_EVERY = '5' + const lines = await captureLog(() => createEmbeddings('code-chunks', CHUNKS, dir)) + delete process.env.EMBEDDINGS_LOG_EVERY + await fs.rm(dir, { recursive: true, force: true }) + + const progress = lines.filter(l => /^Embedding \d+\/\d+/.test(l)) + assert.ok(progress.length > 0, 'expected at least one progress line') + assert.ok(progress[0].includes('elapsed'), `should contain "elapsed": ${progress[0]}`) + assert.ok(progress[0].includes('remaining'), `should contain "remaining": ${progress[0]}`) +}) + +test('EMBEDDINGS_LOG_EVERY=0 suppresses all progress output', async () => { + const dir = await tmpDir() + process.env.EMBEDDINGS_LOG_EVERY = '0' + const lines = await captureLog(() => createEmbeddings('code-chunks', CHUNKS, dir)) + delete process.env.EMBEDDINGS_LOG_EVERY + await fs.rm(dir, { recursive: true, force: true }) + + const progress = lines.filter(l => /^Embedding \d+\/\d+/.test(l)) + assert.equal(progress.length, 0, `expected no progress lines, got: ${progress.join(' | ')}`) +})