Worum es geht
Mit * bekommst du eine Referenz nur als { "id": … }. Willst du mehr von dem referenzierten Objekt sehen, baust du die Referenz aus: Du schreibst statt eines Feldnamens ein Objekt in die response.
{ "field": "company", "response": ["companyname", "city"] }
field nennt die Referenz, response sagt, welche Felder des referenzierten Objekts du willst. Das funktioniert für einzelne Referenzen und für Listen.
Das Beispielmodell
| Modell | Felder | Referenzen |
|---|---|---|
company | companyname, city | employees (Liste von employee) |
employee | firstname, lastname | company (einzeln), department (einzeln) |
department | name | – |
employee.company und company.employees sind die zwei Seiten derselben Beziehung. company ist die Rückreferenz von employees.
Vom Request zur Antwort
flowchart LR
subgraph R["response"]
direction TB
r1["companyname"]
r2["{ field: employees }"]
r3["firstname"]
r4["{ field: department }"]
r5["name"]
r2 --> r3
r2 --> r4
r4 --> r5
end
subgraph A["Antwort"]
direction TB
a1["company<br/>companyname: Codamic AG"]
a2["employees[0]<br/>firstname: Daniel"]
a3["department<br/>name: Entwicklung"]
a4["employees[1]<br/>firstname: Anna"]
a5["department<br/>name: Vertrieb"]
a1 --> a2
a1 --> a4
a2 --> a3
a4 --> a5
end
R ==> A
POST /api/rest/hr/company/read/a1…
{
"response": [
"companyname",
{
"field": "employees",
"response": [
"firstname",
{ "field": "department", "response": ["name"] }
]
}
]
}{
"data": {
"id": "a1…",
"companyname": "Codamic AG",
"employees": [
{ "id": "7f3…", "firstname": "Daniel",
"department": { "id": "d4…", "name": "Entwicklung" } },
{ "id": "8b1…", "firstname": "Anna",
"department": { "id": "d7…", "name": "Vertrieb" } }
]
},
"meta": { "error": false }
}Die Antwort ist gekürzt: Nicht angeforderte Felder stehen in Wirklichkeit als null darin, und jedes Objekt hat seine Systemfelder _createdOn und _updatedOn.
Einzelreferenz und Liste laufen verschieden
Wann: Die Referenz zeigt auf ein Objekt, z. B. employee.company.
-
1CDMS→Datenbankliest den
employeeund hängtcompanyper LEFT JOIN an, dabei nur dieidund den Typ -
2CDMSprüft die Leserolle von
company -
3CDMS→Datenbankliest das Objekt
companymit dieseridwie ein eigenes Lesen: mit seinen Zeilenfiltern und nur mit den Feldern aus der innerenresponse -
4CDMSsetzt das Ergebnis in das Feld
companyein
Ergebnis: company ist ein Objekt mit den angeforderten Feldern.
Wann: Der employee hat keine company, oder der Benutzer darf diese company nicht sehen (eigene Daten, Attributfilter).
Im ersten Fall liefert der LEFT JOIN keine id, im zweiten findet das Lesen der company wegen der Zeilenfilter nichts. In beiden Fällen bleibt das Feld leer. Der employee selbst kommt trotzdem.
Ergebnis: 200 mit "company": null.
Wann: Die Referenz ist eine Liste, z. B. company.employees.
-
1CDMS→Datenbankliest die
company -
2CDMSprüft die Leserolle von
employee -
3CDMSbaut eine eigene Suche auf
employeemit dem Filtercompany.id = <id der company>Diesen Filter über die Rückreferenz ergänzt CDMS selbst. Du schreibst ihn nicht. -
4CDMS→Datenbanksucht die passenden
employee, mit deren Zeilenfiltern und nur mit den Feldern aus der innerenresponse -
5CDMSsetzt die Treffer als Liste in
employeesein
Ergebnis: employees ist eine Liste. Ohne Treffer ist sie leer.
Wann: Du willst nur einen Teil der Liste, sortiert.
Im Objekt darf ein parameter stehen, genau wie bei einer Suche: query, order, limit, page. CDMS hängt deinen Filter mit UND an den Filter auf die Rückreferenz. Ohne limit kommen alle Einträge.
Ergebnis: Die Liste enthält nur die Einträge, die zu deinem Filter passen, in deiner Sortierung und höchstens limit Stück.
Wann: In der inneren response steht wieder ein Objekt.
Jede Ebene läuft genauso ab wie die erste. Es gibt keine feste Grenze für die Tiefe, die Grenze ist deine response: CDMS geht nie tiefer, als du es hinschreibst.
Ergebnis: Ein Baum, der genau die Form deiner response hat.
Wann: Die response reicht in employees, dem Benutzer fehlt employee-read.
-
1CDMSprüft die Leserolle von
employee -
2CDMS→Client403
missing-permission|employee-read
Ergebnis: Die ganze Anfrage scheitert. Dieselbe Anfrage ohne das Objekt employees klappt mit derselben Rolle.
Eine Liste mit eigenem Filter
POST /api/rest/hr/company/read/a1…
{
"response": [
"companyname",
{
"field": "employees",
"response": ["firstname", "lastname"],
"parameter": {
"limit": 5,
"order": [{ "field": "_createdOn", "order": "DESC" }],
"query": {
"type": "AND",
"filter": [{ "key": "lastname", "value": "M%", "param": "LIKE" }]
}
}
}
]
}{
"data": {
"id": "a1…",
"companyname": "Codamic AG",
"employees": [
{ "id": "7f3…", "firstname": "Daniel", "lastname": "Mertins" },
{ "id": "2c9…", "firstname": "Eva", "lastname": "Meier" }
]
},
"meta": { "error": false }
}Was CDMS daraus als Suche auf employee macht:
flowchart TB
subgraph W["WHERE für employee"]
direction TB
w1["company.id = 'a1…'<br/>(von CDMS ergänzt)"]
w2["UND lastname LIKE 'M%'<br/>(dein Filter)"]
w3["UND Zeilenfilter von employee<br/>(eigene Daten, Attributfilter)"]
end
W --> O["ORDER BY _createdOn DESC"]
O --> L["höchstens 5"]
Wie Filter, Sortierung und Blättern im Einzelnen funktionieren, steht im Kapitel Suchen, unter anderem bei Filter in verschachtelten Listen und Blättern und Trefferzahl.
Bei einer Suche: pro Treffer eine Unterabfrage
Baust du eine Liste in einer Suche aus, macht CDMS die Unterabfrage für jeden Treffer einzeln:
sequenceDiagram
participant C as Client
participant D as CDMS
participant DB as Datenbank
C->>D: POST /hr/company/query (limit 25) mit { field: employees }
D->>DB: Suche auf company
DB-->>D: 25 Firmen
loop für jede der 25 Firmen
D->>DB: Suche auf employee mit company.id = …
DB-->>D: Mitarbeiter dieser Firma
end
D-->>C: 25 Firmen, jede mit ihrer Liste employees
Bei 25 Firmen sind das 25 zusätzliche Suchen. Hat jede Firma 500 Mitarbeiter und du setzt kein limit, kommen 12 500 Mitarbeiter zurück. Mehr dazu unter Was beim Lesen in der Datenbank passiert.
Was eine Unterliste nicht hat
dataplusmetameta.totalCountsagt, wie viele Treffer es insgesamt gibt- blättern mit
pageundlimit
- nur die Einträge
- keine Trefferzahl, die Liste ist ein einfaches JSON-Array
limitundpagewirken trotzdem, für jede Liste einzeln
Brauchst du die Gesamtzahl der Einträge einer Liste, frag das Kindmodell direkt: POST /hr/employee/query mit dem Filter company.id = … liefert meta.totalCount.
Entscheidungstabelle
| Art | Leserolle des Zielmodells | Ziel vorhanden und sichtbar | Inhalt des Feldes |
|---|---|---|---|
| einzeln | nein | – | 403 für die ganze Anfrage |
| einzeln | ja | nein | null |
| einzeln | ja | ja | Objekt mit den angeforderten Feldern |
| Liste | nein | – | 403 für die ganze Anfrage |
| Liste | ja | keiner | leere Liste [] |
| Liste | ja | einige | nur die sichtbaren Einträge |
Statt der Leserolle des Zielmodells kann auch eine Rolle auf der Beziehung selbst reichen. Siehe Rechte auf Beziehungen. Die Tabelle beschreibt den Standard, den Strict Mode.
Fallen
Wie es weitergeht
- Wenn die Referenz auf ein abstraktes Modell zeigt: Lesen über abstrakte Typen
- Was das für die Datenbank bedeutet: Was beim Lesen in der Datenbank passiert