From f1d211759e34493457d29e94970bc85557bac9c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Wed, 22 Apr 2026 15:48:43 +0200 Subject: [PATCH 1/7] Java date/time functions --- java/working-with-cql/query-api.md | 318 ++++++++++++++--------------- 1 file changed, 148 insertions(+), 170 deletions(-) diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index 9765123f2..c7f4b463e 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -1467,15 +1467,15 @@ Entity references specify entity sets. They can be used to define the target ent ```java import com.sap.cds.ql.CQL; -// bookshop.Books[year = 2020].author // [!code focus] -Authors_ authors = CQL.entity(Books_.class).filter(b -> b.year().eq(2020)).author(); // [!code focus] +// bookshop.Books[year = 2020].author // [!code highlight] +Authors_ authors = CQL.entity(Books_.class).filter(b -> b.year().eq(2020)).author(); // [!code highlight] // or as untyped entity ref StructuredType authors = CQL.entity("bookshop.Books").filter(b -> b.get("year").eq(2020)).to("author"); -// SELECT from bookshop.Books[year = 2020]:author { name } // [!code focus] -Select.from(authors).columns("name"); // [!code focus] +// SELECT from bookshop.Books[year = 2020]:author { name } // [!code highlight] +Select.from(authors).columns("name"); // [!code highlight] ``` You can also get [entity references](query-execution#entity-refs) from the result of a CDS QL statement to address an entity via its key values in other statements. @@ -1576,69 +1576,105 @@ When using named parameters, `Update` and `Delete` statements can be executed as Scalar functions are values that are calculated from other values. This calculation can be executing a function on the underlying data store or applying an operation, like an addition, to its parameters. The Query Builder API supports the generic `func` function, as well as a number of build-in functions. -* Generic Scalar Function +##### Generic Scalar Function - The generic function `func`, creates a scalar function call that is executed by the underlying data store. The first argument, being the native query language function name, and the remaining arguments are passed on as arguments of the specified function. In the following example, the native query language `count` function is called on the `name` element. This function returns the count of number of elements with name `Monika`. +The method `func`, creates a scalar function call that is executed by the underlying data store. The first argument, being the native query language function name, and the remaining arguments are passed on as arguments of the specified function. In the following example, the native query language `count` function is called on the `name` element. This function returns the count of number of elements with name `Monika`. - ```java - import static com.sap.cds.ql.CQL.func; - Select.from(EMPLOYEE) - .columns(e -> e.name(), e -> func("COUNT", e.name()).as("count")) - .where(e -> e.name().eq("Monika")); - ``` +```java +import static com.sap.cds.ql.CQL.func; +Select.from(EMPLOYEE) + .columns(e -> e.name(), e -> func("COUNT", e.name()).as("count")) + .where(e -> e.name().eq("Monika")); +``` + +##### `toLower` + +The `toLower` function is a built-in string function for converting a given string value to lower case using the rules of the underlying data store. + +```java +import static com.sap.cds.ql.CQL.toLower; +Select.from(EMPLOYEE).columns(e -> e.name()) + .where(e -> e.name().endsWith(toLower("IKA"))); +``` + +In the following example, the `toLower` function is applied on the `name` element before applying the equals predicate. + +```java +Select.from(EMPLOYEE).columns(e -> e.name()) + .where(e -> e.name().toLower().eq("monika")); +``` + +##### `toUpper` + +The `toUpper` function is a built-in string function for converting a given string value to upper case using the rules of the underlying data store. + +```java +import static com.sap.cds.ql.CQL.toUpper; +Select.from(EMPLOYEE).columns(e -> e.name()) + .where(e -> e.name().endsWith(toUpper("ika"))); +``` + +In the following example, the `toUpper` function is applied on the `name` element before applying the equals predicate. + +```java +Select.from(EMPLOYEE).columns(e -> e.name()) + .where(e -> e.name().toUpper().eq("MONIKA")); +``` -* To Lower +##### `substring` - The `toLower` function is a built-in string function for converting a given string value to lower case using the rules of the underlying data store. +The `substring` method creates an expression for substring extraction from a string value. Extract a substring from a specified starting position of either a given length or to the end of the string. The first position is zero! - ```java - import static com.sap.cds.ql.CQL.toLower; - Select.from(EMPLOYEE).columns(e -> e.name()) - .where(e -> e.name().endsWith(toLower("IKA"))); - ``` +```java +Select.from("bookshop.Authors") + .columns(a -> a.get("name").substring(0,2).as("shortname")) +``` +In the following example, the `substring` function is applied as part of a predicate to test whether a subset of characters matches a given string. - In the following example, the `toLower` function is applied on the `name` element before applying the equals predicate. +```java +Select.from("bookshop.Authors") + .where(e -> e.get("name").substring(2).eq("ter")); +``` - ```java - Select.from(EMPLOYEE).columns(e -> e.name()) - .where(e -> e.name().toLower().eq("monika")); - ``` +##### `concat` -* To Upper +See [`Concat`](#string-expressions) String Expression - The `toUpper` function is a built-in string function for converting a given string value to upper case using the rules of the underlying data store. +#### Date/Time functions - ```java - import static com.sap.cds.ql.CQL.toUpper; - Select.from(EMPLOYEE).columns(e -> e.name()) - .where(e -> e.name().endsWith(toUpper("ika"))); - ``` +You can use date/time functions to extract components from date/time values and to compute the difference between timestamps: - In the following example, the `toUpper` function is applied on the `name` element before applying the equals predicate. +##### Extraction Functions - ```java - Select.from(EMPLOYEE).columns(e -> e.name()) - .where(e -> e.name().toUpper().eq("MONIKA")); - ``` +| method | return CDS | return Java | example | +| --- | --- | --- | --- | +| year | Int32 | Integer | `date.year()` | +| month | Int32 | Integer | `CQL.year(date)` | +| day | Int32 | Integer | `date.year()` | +| hour | Int32 | Integer | `CQL.hour(time)` | +| minute | Int32 | Integer | `time.minute()` | +| second | Int32 | Integer | `time.second()` | +| date | Date | LocalDate | `timestamp.date()` | +| time | Time | LocalTime | `timestamp.time()` | -* Substring +You can also use the `extract` function to extract a _given_ chrono field (date/time component): - The `substring` method creates an expression for substring extraction from a string value. Extract a substring from a specified starting position of either a given length or to the end of the string. The first position is zero. +```java +date.extract(ChronoField.DAY_OF_MONTH) +``` - ```java - Select.from("bookshop.Authors") - .columns(a -> a.get("name").substring(0,2).as("shortname")) - ``` - In the following example, the `substring` function is applied as part of a predicate to test whether a subset of characters matches a given string. +##### Difference Computation Functions - ```java - Select.from("bookshop.Authors") - .where(e -> e.get("name").substring(2).eq("ter")); - ``` +These methods allow to compute the difference between timestamps: -* Concat +| method | return CDS | return Java | example | +| --- | --- | --- | --- | +| yearsBetween | Int32 | Integer | `start.yearsBetween(end)` | +| monthsBetween | Int32 | Integer | `start.monthsBetween(end)` | +| daysBetween | Int32 | Integer | `CQL.daysBetween(start, end)` | +| secondsBetween | Int64 | Integer | `start.secondsBetween(end)` | +| nano100Between | Int64 | Integer | `start.nano100Between(end)` | - See [`Concat`](#string-expressions) String Expression #### Case-When-Then Expressions @@ -1651,59 +1687,58 @@ Select.from(BOOKS).columns( .when(b.stock().gt(100)).then("high") .orElse("medium").as("stockLevel").type(CdsBaseType.STRING)); ``` -#### String Expressions - -* Concat +#### Concat - Function `concat` creates a string expression to concatenate a specified value to this value. +The function `concat` creates a string expression to concatenate a specified value to this value. - ```java - // SELECT from Author {name || ' - the Author' as author_name : String} - Select.from(AUTHOR) - .columns(a -> a.name().concat(" - the Author").as("author_name")); - ``` +```java +// SELECT from Author {name || ' - the Author' as author_name : String} +Select.from(AUTHOR) + .columns(a -> a.name().concat(" - the Author").as("author_name")); +``` #### Arithmetic Expressions Arithmetic Expressions are captured by scalar functions as well: -* Plus +##### `plus` - Function `plus` creates an arithmetic expression to add a specified value to this value. +The function `plus` creates an arithmetic expression to add a specified value to this value. - ```java - // SELECT from Author {id + 2 as x : Integer} - Select.from(AUTHOR) - .columns(a -> a.id().plus(2).as("x")); - ``` +```java +// SELECT from Author {id + 2 as x : Integer} +Select.from(AUTHOR) + .columns(a -> a.id().plus(2).as("x")); +``` -* Minus - Function `minus` creates an arithmetic expression to subtract a specified value with this value. +##### `minus` - ```java - Select.from("bookshop.Authors") - .columns("name") - .limit(a -> literal(3).minus(1)); - ``` +The function `minus` creates an arithmetic expression to subtract a specified value with this value. -* Times +```java +Select.from("bookshop.Authors") + .columns("name") + .limit(a -> literal(3).minus(1)); +``` - Function `times` creates an arithmetic expression to multiply a specified value with this value. In the following example, `p` is an Integer parameter value passed when executing the query. +##### `times` - ```java - Parameter p = param("p"); - Select.from(AUTHOR) - .where(a -> a.id().between(10, p.times(30))); - ``` +The function `times` creates an arithmetic expression to multiply a specified value with this value. In the following example, `p` is an Integer parameter value passed when executing the query. -* Divided By +```java +Parameter p = param("p"); +Select.from(AUTHOR) + .where(a -> a.id().between(10, p.times(30))); +``` - Function `dividedBy` creates an arithmetic expression to divide this value with the specified value. +##### `dividedBy` - ```java - Select.from(AUTHOR) - .where(a -> a.id().between(10, literal(30).dividedBy(2))); - ``` +The function `dividedBy` creates an arithmetic expression to divide this value with the specified value. + +```java +Select.from(AUTHOR) + .where(a -> a.id().between(10, literal(30).dividedBy(2))); +``` ### Predicates @@ -1725,7 +1760,7 @@ These comparison operators are supported: CDL - + Description @@ -1741,9 +1776,7 @@ These comparison operators are supported: Test if this value equals a given value. NULL values might be treated as unknown resulting in a three-valued logic as in SQL. -Select.from("bookshop.Books") - .where(b -> b.get("stock") - .eq(15)); +CQL.get("stock").eq(15) @@ -1753,10 +1786,7 @@ These comparison operators are supported: Test if this value is NOT equal to a given value. NULL values might be treated as unknown resulting in a three-valued logic as in SQL. -Select.from("bookshop.Books") - .where(b -> b.get("stock") - .ne(25)); - +CQL.get("stock").ne(15) @@ -1765,12 +1795,7 @@ These comparison operators are supported: Test if this value equals a given value. NULL values are treated as any other value (Boolean logic). - -Select.from("bookshop.Books") - .where(b -> b.get("stock") - .is(15)); - - +CQL.get("stock").is(15) @@ -1779,12 +1804,7 @@ These comparison operators are supported: Test if this value is NOT equal to a given value. NULL values are treated as any other value (Boolean logic). - -Select.from("bookshop.Books") - .where(b -> b.get("stock") - .isNot(25)); - - +CQL.get("stock").isNot(15) @@ -1793,12 +1813,7 @@ These comparison operators are supported: Test if this value is greater than a given value. - -Select.from("bookshop.Books") - .where(b -> b.get("stock") - .gt(5)); - - +CQL.get("stock").gt(15) @@ -1807,12 +1822,7 @@ These comparison operators are supported: Test if this value is greater than or equal to a given value. - -Select.from("bookshop.Books") - .where(b -> b.get("stock") - .ge(5)); - - +CQL.get("stock").ge(15) @@ -1821,12 +1831,7 @@ These comparison operators are supported: Test if this value is less than a given value. - -Select.from("bookshop.Books") - .where(b -> b.get("stock") - .lt(5)); - - +CQL.get("stock").lt(15) @@ -1835,12 +1840,7 @@ These comparison operators are supported: Test if this value is less than or equal to a given value. - -Select.from("bookshop.Books") - .where(b -> b.get("stock") - .le(5)); - - +CQL.get("stock").le(15) @@ -1851,12 +1851,7 @@ BETWEEN Test if this value is between1 a range of values. - -Select.from("bookshop.Books") - .where(b -> b.get("stock") - .between(5, 10)); - - +CQL.get("stock").between(5, 10) @@ -1965,12 +1960,7 @@ AND Returns a predicate that represents a logical AND of this predicate and another. - -Select.from("bookshop.Authors") -.where(a -> - a.get("name").eq("Peter) - .and(a.get("Id").eq(1))); - +name().eq("Peter).and(ID().eq(1)); @@ -1983,11 +1973,7 @@ OR -Select.from("bookshop.Authors") -.where(a -> - a.get("name").eq("Peter) - .or(a.get("Id").eq(1))); - +name().eq("Peter).or(ID().eq(1)); @@ -1999,11 +1985,8 @@ NOT Returns a predicate that represents the logical negation of this predicate. - -Select.from("bookshop.Authors") -.where(a -> - not(a.get("Id").eq(3))); - +name().eq("Peter).not()
or
+CQL.not(name().eq("Peter)) @@ -2013,6 +1996,8 @@ NOT These boolean-valued functions can be used in filters: +##### Containment Test + @@ -2037,11 +2022,7 @@ CONTAINS Test if this string value contains a given substring. @@ -2053,11 +2034,7 @@ STARTS WITH Test if this string value starts with a given prefix. @@ -2069,17 +2046,13 @@ ENDS WITH Test if this string value ends with a given suffix.
- -Select.from(EMPLOYEE) - .where(e -> e.name() - .contains("oni")); - +name().contains("oni")
- -Select.from("bookshop.Books") - .where(b -> b.get("title") - .startsWith("The")); - +title().startsWith("The")
- -Select.from("bookshop.Books") - .where(b -> b.get("title") - .endsWith("Raven")); - +title().endsWith("Raven")
-#### `matchesPattern` Predicate {#matches-pattern} +##### Regular Exressions (`matchesPattern`) {#matches-pattern} The `matchesPattern` predicate is applied to a String value and tests if it matches a given regular expression. @@ -2106,10 +2079,15 @@ The behavior of the regular expression can be customized with the options that c For example, the following code matches that the title of the book begins with the word "CAP" while ignoring the case of the letters: ```java -Select.from("bookshop.Books").where(t -> t.get("title").matchesPattern(CQL.val("^CAP.+$"), CQL.val("i"))); +Select.from("bookshop.Books") + .where(t -> t.get("title") + .matchesPattern(CQL.val("^CAP.+$"), CQL.val("i"))); ``` +#### Filter by Associated Data + +These function allow to filter data based on a condition on associated entities: -#### `anyMatch/allMatch` Predicate {#any-match} +##### Using `anyMatch/allMatch` {#any-match} The `anyMatch` and `allMatch` predicates are applied to an association and test if _any_ instance/_all_ instances of the associated entity set match a given filter condition. They are supported in filter conditions of [Select](#select), [Update](#update) and [Delete](#delete) statements. @@ -2149,7 +2127,7 @@ Select.from(AUTHORS).where(a -> a.books().anyMatch( p.text().contains("unicorn")))); ``` -#### `EXISTS` Subquery {#exists-subquery} +##### Using an `EXISTS` Subquery {#exists-subquery} An `exists` subquery is used to test if a subquery returns any records. Typically a subquery is correlated with the enclosing _outer_ query. You construct an `exists` subquery with the [`exists`](https://javadoc.io/doc/com.sap.cds/cds4j-api/latest/com/sap/cds/ql/StructuredType.html#exists-java.util.function.Function-) method, which takes a [function](#lambda-expressions) that creates the subquery from a reference to the _outer_ query. To access elements of the outer query from within the subquery, this _outer_ reference must be used: From 95d8c5961f3f4afac4b0423b7c93c463a0256207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C3=B6rler?= Date: Wed, 22 Apr 2026 15:54:44 +0200 Subject: [PATCH 2/7] 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 c7f4b463e..ff0aff8fe 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -1650,7 +1650,7 @@ You can use date/time functions to extract components from date/time values and | --- | --- | --- | --- | | year | Int32 | Integer | `date.year()` | | month | Int32 | Integer | `CQL.year(date)` | -| day | Int32 | Integer | `date.year()` | +| day | Int32 | Integer | `date.day()` | | hour | Int32 | Integer | `CQL.hour(time)` | | minute | Int32 | Integer | `time.minute()` | | second | Int32 | Integer | `time.second()` | From b105dfcc6597afafb29ba41707eaf11603e563a0 Mon Sep 17 00:00:00 2001 From: Mahati Shankar <93712176+smahati@users.noreply.github.com> Date: Wed, 29 Apr 2026 00:23:58 +0200 Subject: [PATCH 3/7] cosmetics --- java/working-with-cql/query-api.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index ff0aff8fe..a804de488 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -1578,7 +1578,7 @@ Scalar functions are values that are calculated from other values. This calculat ##### Generic Scalar Function -The method `func`, creates a scalar function call that is executed by the underlying data store. The first argument, being the native query language function name, and the remaining arguments are passed on as arguments of the specified function. In the following example, the native query language `count` function is called on the `name` element. This function returns the count of number of elements with name `Monika`. +The method `func` creates a scalar function call that is executed by the underlying data store. The first argument being the native query language's (CQN) function name, and the remaining arguments are passed on as arguments of the specified function. In the following example, the CQN `count` function is called on the `name` element. This function returns the count of number of elements with name `Monika`. ```java import static com.sap.cds.ql.CQL.func; @@ -1623,7 +1623,7 @@ Select.from(EMPLOYEE).columns(e -> e.name()) ##### `substring` -The `substring` method creates an expression for substring extraction from a string value. Extract a substring from a specified starting position of either a given length or to the end of the string. The first position is zero! +The `substring` method creates an expression for substring extraction from a string value. Specify the starting position and optionally the length of the substring. Specifying only the starting position gets you all the characters to the end of the string. Note, the first character's position is zero! ```java Select.from("bookshop.Authors") @@ -1638,7 +1638,7 @@ Select.from("bookshop.Authors") ##### `concat` -See [`Concat`](#string-expressions) String Expression +See [`Concat`](#string-expressions) String Expression. #### Date/Time functions @@ -1665,7 +1665,7 @@ date.extract(ChronoField.DAY_OF_MONTH) ##### Difference Computation Functions -These methods allow to compute the difference between timestamps: +These methods allow you to compute the difference between timestamps: | method | return CDS | return Java | example | | --- | --- | --- | --- | @@ -2052,7 +2052,7 @@ ENDS WITH -##### Regular Exressions (`matchesPattern`) {#matches-pattern} +##### Regular Expressions (`matchesPattern`) {#matches-pattern} The `matchesPattern` predicate is applied to a String value and tests if it matches a given regular expression. From d7f4e848ac124924ce41dda6af7dd228ebfc12b3 Mon Sep 17 00:00:00 2001 From: Mahati Shankar <93712176+smahati@users.noreply.github.com> Date: Wed, 29 Apr 2026 00:46:32 +0200 Subject: [PATCH 4/7] update link --- 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 d521aab96..cf8b2a63b 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -1638,7 +1638,7 @@ Select.from("bookshop.Authors") ##### `concat` -See [`Concat`](#string-expressions) String Expression. +See [`Concat`](#concat) String Expression. #### Date/Time functions From a8d2915e79dfb3d1e7904b817da8c52046870777 Mon Sep 17 00:00:00 2001 From: Mahati Shankar <93712176+smahati@users.noreply.github.com> Date: Wed, 29 Apr 2026 00:55:14 +0200 Subject: [PATCH 5/7] rename duplicate header --- 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 cf8b2a63b..fec8f4149 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -1638,7 +1638,7 @@ Select.from("bookshop.Authors") ##### `concat` -See [`Concat`](#concat) String Expression. +See [`Concat Expression`](#concat-expression). #### Date/Time functions @@ -1753,7 +1753,7 @@ Select.from(BOOKS).columns( .when(b.stock().gt(100)).then("high") .orElse("medium").as("stockLevel").type(CdsBaseType.STRING)); ``` -#### Concat +#### Concat Expression The function `concat` creates a string expression to concatenate a specified value to this value. From f7b17879b18ecec9f1ae7273c7c8351ca6108464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Jeglinsky?= Date: Wed, 29 Apr 2026 08:39:37 +0200 Subject: [PATCH 6/7] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: René Jeglinsky --- java/working-with-cql/query-api.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index fec8f4149..556d0e7d0 100644 --- a/java/working-with-cql/query-api.md +++ b/java/working-with-cql/query-api.md @@ -1627,7 +1627,7 @@ The `substring` method creates an expression for substring extraction from a str ```java Select.from("bookshop.Authors") - .columns(a -> a.get("name").substring(0,2).as("shortname")) + .columns(a -> a.get("name").substring(0,2).as("shortname")); ``` In the following example, the `substring` function is applied as part of a predicate to test whether a subset of characters matches a given string. @@ -1754,6 +1754,7 @@ Select.from(BOOKS).columns( .orElse("medium").as("stockLevel").type(CdsBaseType.STRING)); ``` #### Concat Expression +###### String Expressions The function `concat` creates a string expression to concatenate a specified value to this value. From cd7ca80cd81e29beff372ee591ed7ac18a134dac Mon Sep 17 00:00:00 2001 From: Mahati Shankar <93712176+smahati@users.noreply.github.com> Date: Tue, 5 May 2026 14:25:16 +0200 Subject: [PATCH 7/7] add check as expression to snippet checker --- .github/java-snippet-checker/check-java-snippets.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/java-snippet-checker/check-java-snippets.js b/.github/java-snippet-checker/check-java-snippets.js index fe5e4172e..013461f42 100755 --- a/.github/java-snippet-checker/check-java-snippets.js +++ b/.github/java-snippet-checker/check-java-snippets.js @@ -81,6 +81,7 @@ for (const dir of baseDirs) { const variations = [ { content: snippet.content, error: null }, { content: snippetAsMethod(snippet.content), error: null }, + { content: snippetAsExpression(snippet.content), error: null }, { content: snippetAsCode(snippet.content), error: null }, ]; @@ -206,6 +207,17 @@ ${ indentLines(content.trim(), 4) } `; } +/** + * @param {string} content + */ +function snippetAsExpression(content) { + return `// Snippet Checker +Object o = +${ indentLines(content.trim(), 4) } +; +`; +} + /** * @param {string[]} files */