diff --git a/java/working-with-cql/query-api.md b/java/working-with-cql/query-api.md index 9765123f2..ff0aff8fe 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.day()` | +| 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: