-
Notifications
You must be signed in to change notification settings - Fork 138
[Java] Document references #2153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 7 commits
e0c19c6
3cfdad1
36fbf31
801631e
caa3892
b1a24dd
92a0ac3
f295189
0f14609
fb7591f
7a5c3c3
5d740ce
4a67d72
2e7d155
51b74cd
91b2f00
5002237
1b1f731
881693f
278e74a
46ecab7
b47d480
761a620
483e0a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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. | ||||||
|
|
@@ -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. | ||||||
renejeglinsky marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| 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. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| References can be represented in JSON following an [Expression](../../cds/cxn) notation. | ||||||
renejeglinsky marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| 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. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| The simplest kind of absolute reference is the reference to the entity set, for example, to all books. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ```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 | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| ``` | ||||||
|
|
||||||
| 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. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ```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. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| 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. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ```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. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| 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] | ||||||
|
|
||||||
|
|
@@ -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. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| 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. | ||||||
renejeglinsky marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| Given that there is a simple reference pointing to the book created as follows. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ```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. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
renejeglinsky marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ```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. | ||||||
|
||||||
|
|
||||||
| ```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. | ||||||
|
||||||
|
|
||||||
| 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. | ||||||
renejeglinsky marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ```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. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ```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. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ```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. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| References can be copied to produce other refs with in-place modification available for its segments as follows. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ```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. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| :::warning Limitation | ||||||
| The references are not comparable between each other. They cannot be used as keys of maps or values in a set. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| ::: | ||||||
|
|
||||||
| ### Elements References {#element-refs} | ||||||
|
||||||
|
|
||||||
| 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_. | ||||||
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| 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. | ||||||
renejeglinsky marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| 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). |
vmikhailenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
There was a problem hiding this comment.
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.
Uh oh!
There was an error while loading. Please reload this page.