Skip to content
Open
Changes from 7 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 163 additions & 4 deletions java/working-with-cql/query-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ In the [CDS Query Language (CQL)](/cds/cql) builder, the lambda expression `o ->
To target components of a structured document, we recommend using path expressions with infix filters.
:::


### Filters {#target-entity-filters}

Besides using infix filters in path expressions, the `Select`, `Update`, and `Delete` builders support filtering the [target entity set](#target-entity-sets) via the `where` method. Using `where` is equivalent to defining an infix filter on the last segment of a path expression in the statement's `from` / `entity` clause. For statements that have both, an infix filter on the last path segment and a `where` filter, the resulting target filter is the conjunction (`and`) of the infix filter and the `where` filter.
Expand Down Expand Up @@ -1309,11 +1308,73 @@ The Query Builder API supports using expressions in many places. Expressions con

### Entity References {#entity-refs}

Entity references specify entity sets. They can be used to define the target entity set of a [CQL](../../cds/cql) statement. They can either be defined inline using lambda expressions in the Query Builder (see [Target Entity Sets](#target-entity-sets)) or via the `CQL.entity` method, which is available in an _untyped_ version as well as in a _typed_ version that uses the generated [model interfaces](../cqn-services/persistence-services#model-interfaces). The following example shows an entity reference describing the set of *authors* that have published books in the year 2020:
Entity references specify entity sets and define the target entity set of a [CQL](../../cds/cql) statement or be an argument of event handler.

A reference consists of _segments_ that define the path from the entity's root to the certain part of it. Each segment has the _identifier_ with the name of the entity or an element and an optional filter _predicate_. These predicates might include other references.

References can be represented in JSON following an [Expression](../../cds/cxn) notation.

References are either _absolute_ or _relative_. Absolute refs always have fully qualified name of the type in their first segment. Relative references need other absolute reference to which they relate.

The simplest kind of absolute reference is the reference to the entity set, for example, to all books.

```java
import com.sap.cds.ql.CQL;
Books_ books = CQL.entity(Books_.class); // {"ref":["sap.capire.bookshop.Books"]}

StructuredTypeRef ref = bookWithId.asRef(); // or CqnStructuredTypeRef which is cleaner equivalent type
```

The method `asRef()` seals the reference and makes it immutable.

Relative references typically reference elements of the entity, for example, title of the book. They are members of select list of the CQL statement and are relative to its source.

```java
CqnElementRef title = CQL.entity(Books_.class).title(); // {"ref":["title"]}
CqnElementRef dynamicTitle = CQL.get(Books.TITLE); // {"ref":["title"]}
```

New references are constructed with [model interfaces](../cqn-services/persistence-services#model-interfaces) or via API that is also used to build [CQL statements](/java/working-with-cql/query-api#concepts). For most of the application code, the model interfaces are the recommended way to do this.

References might also represent navigation within a structured entity or between different entities using its associations. For example, in the following is the reference that represents the path from the book to its chapters.

```java
CqnStructuredTypeRef ref = CQL.entity(Books_.class).filter(b -> b.ID().eq("...")).chapters(c -> c.ID().eq("...")).pages(p -> p.ID().eq("...")).asRef();
```

Each segment of this reference has an identifier (typically an association or composition) and a filter.

```json
{
"ref": [
{
"id": "sap.capire.bookshop.Books",
"where": [
{ "ref": ["ID"]}, "=", {"val": "..."}
]
},
{
"id": "chapters",
"where": [
{ "ref": ["ID"]}, "=", {"val": "..."}
]
},
{
"id": "pages",
"where": [
{ "ref": ["ID"]}, "=", {"val": "..."}
]
}
]
}
```

An existing reference can be reused as an object or a variable, or a new reference can be built on top of it. They can be introspected with [`CqnVisitor`](/java/working-with-cql/query-introspection#cqnvisitor). The references are not bound to the particular model and are not checked against it while they are being built.

Each reference can be serialized as JSON and also renders JSON in its `toString()` implementation. Do not use this JSON to process the references, for example, to extract values from them or to compare references with each other.

Absolute references can be used as sources of the CQL statements and statements generated by CAP typically have them as their [sources](/java/working-with-cql/query-api#from-reference).

```java
// bookshop.Books[year = 2020].author // [!code focus]
Authors_ authors = CQL.entity(Books_.class).filter(b -> b.year().eq(2020)).author(); // [!code focus]

Expand All @@ -1325,8 +1386,106 @@ StructuredType<?> authors =
Select.from(authors).columns("name"); // [!code focus]
```

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.
The resulting statement has two references: one absolute describing the author and one relative, describing the name of the author.

References can be analyzed with the [`CqnAnalyzer`](/java/working-with-cql/query-introspection#cqnanalyzer) to bind it back to the model, for example, to find annotations or extract filter values.

The existing references obtained from the statements or [injected in custom handlers](/java/event-handlers/#entity-reference-arguments) can be used to produce new references.

Given that there is a simple reference pointing to the book created as follows.

```java
// {"ref":[{"id":"sap.capire.bookshop.Books","where":[{"ref":["ID"]},"=",{"val":"..."}]}]}
CqnStructuredTypeRef ref = CQL.entity(Books_.class).filter(b -> b.ID().eq("...")).asRef(); // [!code focus]
```

To navigate to author of the book, use `CQL.entity(...)` to make it typed again and add one more segment to it. Note, that this does not check that original reference is indeed the reference to the book, this only lets you use required model interface.

```java
// {"ref":[{"id":"sap.capire.bookshop.Books","where":[{"ref":["ID"]},"=",{"val":"..."}]},"author"]}
CqnStructuredTypeRef refToAuthor = CQL.entity(Books_.class, ref).author().asRef(); // [!code focus]
```

With `CQL.to(...)` the same is produced dynamically.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use active voice and improve clarity. Change 'With CQL.to(...) the same is produced dynamically' to 'Use CQL.to(...) to produce the same result dynamically.'


```java
// {"ref":[{"id":"sap.capire.bookshop.Books","where":[{"ref":["ID"]},"=",{"val":"..."}]},"author"]}
CqnStructuredTypeRef toAuthor = CQL.to(ref.segments()).to("author").asRef(); // [!code focus]
```

This new reference might be used as the root of its own statement to read or change the author.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use active voice and improve clarity. Change "This new reference might be used as the root of its own statement" to "You can use this new reference as the root of its own statement" to be more direct and actionable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use active voice and improve clarity. Change "This new reference might be used" to "You can use this new reference" to be more direct and actionable.


With this one, one might need to navigate to the parent again. This is done by stripping the segments from the back of the reference.

```java
// {"ref":[{"id":"sap.capire.bookshop.Books","where":[{"ref":["ID"]},"=",{"val":"..."}]}]}
CqnStructuredTypeRef toParent = CQL.to(
refToAuthor.segments().subList(0, refToAuthor.segments().size() - 1)).asRef(); // [!code focus]
```

There is a method `rootSegment()` that can be used to construct the reference starting with the same root as the existing reference. This is useful when you require navigation to different parts of the same structured type, for example, from the book's author to the pages of it.

```java
CqnStructuredTypeRef toPagesOfTheBook = CQL.to(List.of(refToAuthor.rootSegment())).to("chapters").to("pages").asRef();
```

You can use the `CQL.entity(...)` to use convenience of model interfaces for this with additional cast.

```java
CqnStructuredTypeRef toPagesOfTheBook = CQL.entity(Books_.class,
CQL.to(List.of(refToAuthor.rootSegment())).asRef()).chapters().pages().asRef();
```

The same reference is produced as the result of last two statements. You, of course, have to provide the filters, if to-many associations are used in them.

References can be copied to produce other refs with in-place modification available for its segments as follows.

```java
StructuredTypeRef ref = CQL.entity(Books_.class).filter(b -> b.ID().eq("...")).chapters(c -> c.ID().eq("...")).pages(p -> p.ID().eq("...")).asRef();
RefBuilder<StructuredTypeRef> builder = CQL.copy(ref);
builder.segments().forEach(s -> {
// add new predicate to each segment
s.filter(CQL.and(CQL.get(Books.GENRE).eq("Fiction"), s.filter().orElse(null)));
});

StructuredTypeRef copy = builder.build(); // new reference is ready
```

The code that manipulates the references must be tested thoroughly and you always need to ensure that you do not lose the filters or change the reference so that it becomes inconsistent.

:::warning Limitation
The references are not comparable between each other. They cannot be used as keys of maps or values in a set.
:::

### Elements References {#element-refs}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spelling error: 'Elements References' should be 'Element References' (singular form is more appropriate for a heading about the concept).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grammar error: The heading should be 'Element References' (not 'Elements References') to maintain consistency with the singular form used elsewhere in the document.


An element reference points to an element of the entity. Such references are usually _relative_, they do not have the name of the entity in their root. They also can include filters in their segments except _the last one_.
Most of the time, they exist as members of the [select list](#projections) of a statement or part of the statement, for example, of an expand predicate.

The following example illustrates the difference:

```java
CqnSelect statement = Select.from(Books_.class, b -> b.filter(f -> f.ID().eq("...")))
.columns(b -> b.author().placeOfBirth());

CqnStructuredTypeRef absoluteRef = statement.ref(); // Books(ID=...)

CqnElementRef relativeRef = statement.items().getFirst().asRef(); // author/placeOfBirth
```

Element references can be extended with the same APIs as the entity references.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Element references can be extended with the same APIs as the entity references.
Element references can be extended with the same APIs as the [entity references](#entity-refs).


```java
CqnElementRef extendedRef = CQL.to(relativeRef.segments()).get("name"); // author/placeOfBirth/name
```

You can create an _absolute_ element reference, but they are rarely used in practice.

```java
CqnElementRef nameOfAuthor = CQL.entity(Books_.class).filter(f -> f.ID().eq("...")).author().name();
```

They also share the same features and limitations as the entity references and they cannot be used with [`CqnAnalyzer`](/java/working-with-cql/query-introspection#cqnanalyzer).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use active voice and improve clarity. Change 'They also share the same features and limitations as the entity references and they cannot be used with' to 'Element references share the same features and limitations as entity references and cannot be used with' to avoid repetition and make it more concise.


### Values

Expand Down