From 7de16c7699d741454c82b1a20038a37ff68c32ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Mon, 10 Nov 2025 18:15:21 +0100 Subject: [PATCH 01/10] Java: cds.Map --- java/cds-data.md | 25 ++++++++++++++-- java/working-with-cql/query-api.md | 48 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/java/cds-data.md b/java/cds-data.md index 068f3d1bd..9bcee75a1 100644 --- a/java/cds-data.md +++ b/java/cds-data.md @@ -43,7 +43,7 @@ The [predefined CDS types](../cds/types) are mapped to Java types and as follows | `cds.Binary` | `byte[]` | | | `cds.LargeBinary` | `byte[]` | `java.io.InputStream` (1) if annotated with `@Core.MediaType` | | `cds.Vector` | `com.sap.cds.CdsVector` | for [vector embeddings](#vector-embeddings) | -| `cds.Map` | `java.util.Map` | for arbitrary [structured data](#structured-data)(2) | +| `cds.Map` | `java.util.Map` | for schemaless [structured data](#cds-map) | ### SAP HANA-Specific Data Types @@ -63,7 +63,6 @@ To facilitate using legacy CDS models, the following [SAP HANA-specific data typ > (1) Although the API to handle large objects is the same for every database, the streaming feature, however, is supported (and tested) in **SAP HANA**, **PostgreSQL**, and **H2**. See section [Database Support in Java](./cqn-services/persistence-services#database-support) for more details on database support and limitations. -> (2) Serialized as JSON to a CLOB column or JSONB column (on Postgres) ::: warning The framework isn't responsible for closing the stream when writing to the database. You decide when the stream is to be closed. If you forget to close the stream, the open stream can lead to a memory leak. @@ -74,7 +73,7 @@ These types are used for the values of CDS elements with primitive type. In the ## Structured Data In CDS, structured data is used as payload of *Insert*, *Update*, and *Upsert* statements. Also the query result of *Select* may be structured. -CAP Java represents data of entities and structured types as `Map` and provides the `CdsData` interface as an extension of `Map` with additional convenience methods. +CAP Java represents data of entities, structured types and elements of type [cds.Map](#cds-map) as `java.util.Map` and provides the `CdsData` interface as an extension of `Map` with additional convenience methods. In the following we use this CDS model: @@ -279,6 +278,26 @@ Avoid cyclic relationships between CdsData objects when using toJson.
+## Map Data { #cds-map } + +Elements of type `cds.Map` can be used to store arbitrary _schemaless_ [stuctured data](#structured-data). CAP Java represents data of elemens of type `cds.Map` as Map. + +On the database, this data is serialized to JSON(2). Only data types that are compatible with [JSON](https://www.json.org/) can be stored and retrieved: + +| Java Type | JSON Type | +| ---------------------------------| --------------- | +| `java.lang.String` | `string` | +| `java.lang.Number`(2) | `number` | +| `java.lang.Boolean` | `true`, `false` | +| `java.util.Map` | `object` | +| `java.util.List` | `array` | +| `null` | `null` | + +Map data can be nested. + +> (1) Serialized as JSON to a CLOB column or JSONB column (on Postgres) + +> (2) The actual subclass of a `Number` is not preserved upon serialization and might change upon deserialization. ## Vector Embeddings { #vector-embeddings } diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index b78376698..aed7d7c07 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -187,6 +187,30 @@ Select.from(BOOKS) Select.from(BOOKS).byParams("title", "author.name"); ``` +#### Filtering Map Data + +You can also filter by _content_ of [map data](../cds-data.md#map-data) (i.e. elements of type `cds.Map`). Considering this model + +```cds +entity Product : cuid { + name : String; + category : String; + details : Map; +} +``` + +this query selects all products of the category "Tech" and where type Map element `details` contains a sub-element `brand` with the value "ACME". + +```java +Select.from(PRODUCTS) + .where(p -> p.category().eq("Tech").and( + p.to("details").get("brand").eq("ACME"))); +``` + +::: warning +Depending on the data database filtering by content of Map data can be an expensive operation. A filter by a content of map data should always be a narrowing restriction. +::: + ### Parameters The [CQL](../../cds/cql) builders support [parameters](#expr-param) in the `where` clause and in infix filters for [parameterized execution](query-execution#parameterized-execution): @@ -817,6 +841,30 @@ In this example, it's assumed that the total number of books is more or equal to The pagination isn't stateful. If rows are inserted or removed before a subsequent page is requested, the next page could contain rows that were already contained in a previous page or rows could be skipped. ::: +#### Sorting by Map Data + +You can also sort by _content_ of [map data](../cds-data.md#map-data) (i.e. elements of type `cds.Map`). Considering this model + +```cds +entity Product : cuid { + name : String; + category : String; + details : Map; +} +``` + +this query sorts products by category and additionally by the sub-element `brand` of the map element `details`. + +```java +Select.from(PRODUCTS) + .orderBy(p -> p.category().asc(), + p.to("details").get("brand").asc()); +``` + +::: warning +Depending on the data database sorting by content of Map data can be an expensive operation. A sort specificytion that sorts by content of map data should always be an additional sort specification. +::: + ### Pessimistic Locking { #write-lock} Use the `lock()` method to enforce [Pessimistic Locking](../../guides/providing-services#select-for-update). From 244a8d3ced3169d386b6d745f03dd5a0692d587e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Tue, 11 Nov 2025 15:11:29 +0100 Subject: [PATCH 02/10] edits --- java/working-with-cql/query-api.md | 35 ++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index aed7d7c07..2ab49a3c5 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -189,7 +189,7 @@ Select.from(BOOKS).byParams("title", "author.name"); #### Filtering Map Data -You can also filter by _content_ of [map data](../cds-data.md#map-data) (i.e. elements of type `cds.Map`). Considering this model +You can also filter by _content_ of [map data](../cds-data.md#cds-map) (i.e. elements of type `cds.Map`). Considering this model ```cds entity Product : cuid { @@ -199,11 +199,11 @@ entity Product : cuid { } ``` -this query selects all products of the category "Tech" and where type Map element `details` contains a sub-element `brand` with the value "ACME". +this query selects all products of the category "tech" and where type Map element `details` contains a sub-element `brand` with the value "ACME". ```java Select.from(PRODUCTS) - .where(p -> p.category().eq("Tech").and( + .where(p -> p.category().eq("tech").and( p.to("details").get("brand").eq("ACME"))); ``` @@ -560,6 +560,33 @@ Object authorId = book.get("author.Id"); // path access Only to-one associations that are mapped via the primary key elements of the target entity are supported on the select list. The execution is optimized and gives no guarantee that the target entity exists, if this is required use expand or enable [integrity constraints](../../guides/databases#database-constraints) on the database. ::: +#### Selecting Map Data + +You can also use elements of type [cds.Map](../cds-data.md#cds-map) on the select list. Considering this model + +```cds +entity Product : cuid { + name : String; + category : String; + details : Map; +} +``` + +the query + +```java +Select.from(PRODUCTS).columns(p -> p.ID(), p.details()); +``` + +will select the product's ID along with the details, which are returned as a `Map`. You can also select sub-elements of elements of type `cds.Map`: + +```java +Select.from(PRODUCTS) + .columns(p-> p.ID(), + p-> p.to("details").get("brand")) + .where(p -> p.category().eq("tech")); + +This query selects the sub-element `brand` of the element `details` for all products of category "tech". ### Filtering and Searching { #filtering} @@ -843,7 +870,7 @@ The pagination isn't stateful. If rows are inserted or removed before a subseque #### Sorting by Map Data -You can also sort by _content_ of [map data](../cds-data.md#map-data) (i.e. elements of type `cds.Map`). Considering this model +You can also sort by _content_ of [map data](../cds-data.md#cds-map) (i.e. elements of type `cds.Map`). Considering this model ```cds entity Product : cuid { From f035b0d92ce3808c5bd66b6ff46418887b602efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Tue, 11 Nov 2025 15:20:20 +0100 Subject: [PATCH 03/10] typo --- java/working-with-cql/query-api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index 2ab49a3c5..ee16a1d3d 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -560,7 +560,7 @@ Object authorId = book.get("author.Id"); // path access Only to-one associations that are mapped via the primary key elements of the target entity are supported on the select list. The execution is optimized and gives no guarantee that the target entity exists, if this is required use expand or enable [integrity constraints](../../guides/databases#database-constraints) on the database. ::: -#### Selecting Map Data +#### Selecting Map Data You can also use elements of type [cds.Map](../cds-data.md#cds-map) on the select list. Considering this model @@ -889,7 +889,7 @@ Select.from(PRODUCTS) ``` ::: warning -Depending on the data database sorting by content of Map data can be an expensive operation. A sort specificytion that sorts by content of map data should always be an additional sort specification. +Depending on the data database sorting by content of Map data can be an expensive operation. A sort specification that sorts by content of map data should always be an additional sort specification. ::: ### Pessimistic Locking { #write-lock} From d66cd9fe65decd22adf0113b0663b223932e189b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Tue, 11 Nov 2025 15:24:42 +0100 Subject: [PATCH 04/10] typos --- java/cds-data.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/java/cds-data.md b/java/cds-data.md index 9bcee75a1..20c29df9c 100644 --- a/java/cds-data.md +++ b/java/cds-data.md @@ -280,9 +280,9 @@ Avoid cyclic relationships between CdsData objects when using toJson. ## Map Data { #cds-map } -Elements of type `cds.Map` can be used to store arbitrary _schemaless_ [stuctured data](#structured-data). CAP Java represents data of elemens of type `cds.Map` as Map. +Elements of type `cds.Map` can be used to store arbitrary _schemaless_ [stuctured data](#structured-data). CAP Java represents data of elements of type `cds.Map` as `Map`. -On the database, this data is serialized to JSON(2). Only data types that are compatible with [JSON](https://www.json.org/) can be stored and retrieved: +On the database, this data is serialized to [JSON](https://www.json.org/)(2). Only data types that are compatible with JSON can be stored and retrieved: | Java Type | JSON Type | | ---------------------------------| --------------- | @@ -293,11 +293,11 @@ On the database, this data is serialized to JSON(2). Only data types | `java.util.List` | `array` | | `null` | `null` | -Map data can be nested. - > (1) Serialized as JSON to a CLOB column or JSONB column (on Postgres) -> (2) The actual subclass of a `Number` is not preserved upon serialization and might change upon deserialization. +> (2) The actual subclass of a `Number` is not preserved upon serialization and might change upon deserialization. + +Map data can be nested and may contain `List`s, which are serialized to JSON arrays. ## Vector Embeddings { #vector-embeddings } From 941cb5423c1d05e3925fe00454665f9f693757f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Tue, 11 Nov 2025 15:31:39 +0100 Subject: [PATCH 05/10] reword --- java/working-with-cql/query-api.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index ee16a1d3d..0883340b5 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -199,7 +199,7 @@ entity Product : cuid { } ``` -this query selects all products of the category "tech" and where type Map element `details` contains a sub-element `brand` with the value "ACME". +the following query selects all products of the category "tech" and where type Map element `details` contains a sub-element `brand` with the value "ACME": ```java Select.from(PRODUCTS) @@ -208,7 +208,7 @@ Select.from(PRODUCTS) ``` ::: warning -Depending on the data database filtering by content of Map data can be an expensive operation. A filter by a content of map data should always be a narrowing restriction. +Depending on the data database, filtering by content of map data can be an expensive operation. A filter by a content of map data should only be used to make a filter that uses column data more restrictive. ::: ### Parameters @@ -884,12 +884,13 @@ this query sorts products by category and additionally by the sub-element `brand ```java Select.from(PRODUCTS) + .where(p -> p.category().eq("tech")) .orderBy(p -> p.category().asc(), p.to("details").get("brand").asc()); ``` ::: warning -Depending on the data database sorting by content of Map data can be an expensive operation. A sort specification that sorts by content of map data should always be an additional sort specification. +Depending on the data database, sorting by content of map data can be an expensive operation and should only be applied on a small result set. ::: ### Pessimistic Locking { #write-lock} From 345168f33e83ba91435b0e17edd40ef4d36e58d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Tue, 11 Nov 2025 15:47:16 +0100 Subject: [PATCH 06/10] broken link --- java/working-with-cql/query-api.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index 0883340b5..601e88d7b 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -585,6 +585,7 @@ Select.from(PRODUCTS) .columns(p-> p.ID(), p-> p.to("details").get("brand")) .where(p -> p.category().eq("tech")); +``` This query selects the sub-element `brand` of the element `details` for all products of category "tech". @@ -607,6 +608,7 @@ entity Book { title : String; } ``` + In the following example, element `title` is included in `@cds.search`. Only this particular element is searchable then. ```cds From e8a73687af444b57f8e935635b3f7960980b882d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Fri, 14 Nov 2025 16:18:45 +0100 Subject: [PATCH 07/10] Apply suggestions from code review Co-authored-by: Matthias Schur <107557548+MattSchur@users.noreply.github.com> --- java/cds-data.md | 4 ++-- java/working-with-cql/query-api.md | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/java/cds-data.md b/java/cds-data.md index 20c29df9c..672ec6407 100644 --- a/java/cds-data.md +++ b/java/cds-data.md @@ -282,7 +282,7 @@ Avoid cyclic relationships between CdsData objects when using toJson. Elements of type `cds.Map` can be used to store arbitrary _schemaless_ [stuctured data](#structured-data). CAP Java represents data of elements of type `cds.Map` as `Map`. -On the database, this data is serialized to [JSON](https://www.json.org/)(2). Only data types that are compatible with JSON can be stored and retrieved: +On the database, this data is serialized to [JSON](https://www.json.org/)(1). Only data types that are compatible with JSON can be stored and retrieved: | Java Type | JSON Type | | ---------------------------------| --------------- | @@ -297,7 +297,7 @@ On the database, this data is serialized to [JSON](https://www.json.org/)(2 > (2) The actual subclass of a `Number` is not preserved upon serialization and might change upon deserialization. -Map data can be nested and may contain `List`s, which are serialized to JSON arrays. +Map data can be nested and may contain nested maps and lists, which are serialized to JSON objects and arrays, respectively. ## Vector Embeddings { #vector-embeddings } diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index 601e88d7b..b8cbf6f88 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -187,7 +187,7 @@ Select.from(BOOKS) Select.from(BOOKS).byParams("title", "author.name"); ``` -#### Filtering Map Data +#### Filtering Map Data You can also filter by _content_ of [map data](../cds-data.md#cds-map) (i.e. elements of type `cds.Map`). Considering this model @@ -199,16 +199,16 @@ entity Product : cuid { } ``` -the following query selects all products of the category "tech" and where type Map element `details` contains a sub-element `brand` with the value "ACME": +The following query selects all products of category "tech" where the `details` map contains a sub-element `brand` with value "ACME": ```java Select.from(PRODUCTS) .where(p -> p.category().eq("tech").and( - p.to("details").get("brand").eq("ACME"))); + p.details().get("brand").eq("ACME"))); ``` ::: warning -Depending on the data database, filtering by content of map data can be an expensive operation. A filter by a content of map data should only be used to make a filter that uses column data more restrictive. +Depending on the database, filtering by content of a map element can be expensive when applied on large datasets. Use additional filters on non-map elements to reduce the dataset. ::: ### Parameters @@ -578,16 +578,16 @@ the query Select.from(PRODUCTS).columns(p -> p.ID(), p.details()); ``` -will select the product's ID along with the details, which are returned as a `Map`. You can also select sub-elements of elements of type `cds.Map`: +selects the product's ID along with the details, which are returned as a `CdsData` map. You can also select sub-elements of a `cds.Map` via path expressions: ```java Select.from(PRODUCTS) .columns(p-> p.ID(), - p-> p.to("details").get("brand")) + p-> p.details().get("brand")) .where(p -> p.category().eq("tech")); ``` -This query selects the sub-element `brand` of the element `details` for all products of category "tech". +This query selects the sub-element `brand` of the `details` map element for all products of category "tech". ### Filtering and Searching { #filtering} @@ -870,7 +870,7 @@ In this example, it's assumed that the total number of books is more or equal to The pagination isn't stateful. If rows are inserted or removed before a subsequent page is requested, the next page could contain rows that were already contained in a previous page or rows could be skipped. ::: -#### Sorting by Map Data +#### Sorting by Map Data You can also sort by _content_ of [map data](../cds-data.md#cds-map) (i.e. elements of type `cds.Map`). Considering this model @@ -882,7 +882,7 @@ entity Product : cuid { } ``` -this query sorts products by category and additionally by the sub-element `brand` of the map element `details`. +This following query sorts products by category and additionally by the sub-element `brand` of the map element `details`. ```java Select.from(PRODUCTS) @@ -892,7 +892,7 @@ Select.from(PRODUCTS) ``` ::: warning -Depending on the data database, sorting by content of map data can be an expensive operation and should only be applied on a small result set. +Depending on the data database, sorting by content of map data is expensive and can lead to poor performance when applied to large result sets. ::: ### Pessimistic Locking { #write-lock} From 07b3aa7db50499b15e877acb4273686b24578b5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Mon, 17 Nov 2025 16:47:17 +0100 Subject: [PATCH 08/10] Apply suggestions from code review Co-authored-by: Matthias Schur <107557548+MattSchur@users.noreply.github.com> --- java/cds-data.md | 2 +- java/working-with-cql/query-api.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java/cds-data.md b/java/cds-data.md index 672ec6407..2f898b1aa 100644 --- a/java/cds-data.md +++ b/java/cds-data.md @@ -278,7 +278,7 @@ Avoid cyclic relationships between CdsData objects when using toJson.
-## Map Data { #cds-map } +## Map Data { #cds-map } Elements of type `cds.Map` can be used to store arbitrary _schemaless_ [stuctured data](#structured-data). CAP Java represents data of elements of type `cds.Map` as `Map`. diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index b8cbf6f88..1c5085df5 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -560,7 +560,7 @@ Object authorId = book.get("author.Id"); // path access Only to-one associations that are mapped via the primary key elements of the target entity are supported on the select list. The execution is optimized and gives no guarantee that the target entity exists, if this is required use expand or enable [integrity constraints](../../guides/databases#database-constraints) on the database. ::: -#### Selecting Map Data +#### Selecting Map Data You can also use elements of type [cds.Map](../cds-data.md#cds-map) on the select list. Considering this model @@ -892,7 +892,7 @@ Select.from(PRODUCTS) ``` ::: warning -Depending on the data database, sorting by content of map data is expensive and can lead to poor performance when applied to large result sets. +Depending on the database, sorting by content of map data is expensive and can lead to poor performance when applied to large result sets. ::: ### Pessimistic Locking { #write-lock} From d912d04ff12771b731d4dc5c3573d83affddc7d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Mon, 17 Nov 2025 16:49:18 +0100 Subject: [PATCH 09/10] Update java/working-with-cql/query-api.md --- java/working-with-cql/query-api.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index 1c5085df5..23b0f1081 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -578,7 +578,11 @@ the query Select.from(PRODUCTS).columns(p -> p.ID(), p.details()); ``` -selects the product's ID along with the details, which are returned as a `CdsData` map. You can also select sub-elements of a `cds.Map` via path expressions: +selects the product's ID along with the details, which are returned as a `CdsData` map. + +##### Selecting Subelements of Map Data + +You can also select sub-elements of a `cds.Map` via path expressions: ```java Select.from(PRODUCTS) From a594e002aa48606e17186d0c28c8f106c1a55914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Mon, 17 Nov 2025 17:00:48 +0100 Subject: [PATCH 10/10] Update java/working-with-cql/query-api.md --- java/working-with-cql/query-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index 23b0f1081..b5097e11f 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -580,7 +580,7 @@ Select.from(PRODUCTS).columns(p -> p.ID(), p.details()); selects the product's ID along with the details, which are returned as a `CdsData` map. -##### Selecting Subelements of Map Data +##### Selecting Sub-Elements of Map Data You can also select sub-elements of a `cds.Map` via path expressions: