CodamAIDocs
Themafertig

Referenzen und Listen ausbauen

Wie eine Referenz mit { field, response } vollständig geladen wird, wie Listen eigene Filter, Sortierung und Seitengröße bekommen und wie tief das gehen darf.

Ausprägungen
EinzelreferenzEinzelreferenz nicht gesetzt oder unsichtbar → nullListeListe mit parametermehrere EbenenRückreferenz wird automatisch ergänztkeine Trefferzahl für UnterlistenReferenz ohne Leserolle → 403

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

ModellFelderReferenzen
companycompanyname, cityemployees (Liste von employee)
employeefirstname, lastnamecompany (einzeln), department (einzeln)
departmentname–

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
Zwei Ebenen in einer Anfrage
Anfrage
POST /api/rest/hr/company/read/a1…
{
  "response": [
    "companyname",
    {
      "field": "employees",
      "response": [
        "firstname",
        { "field": "department", "response": ["name"] }
      ]
    }
  ]
}
Antwort
{
  "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

Wie CDMS eine Referenz ausbaut

Wann: Die Referenz zeigt auf ein Objekt, z. B. employee.company.

  1. 1
    CDMS→Datenbank
    liest den employee und hängt company per LEFT JOIN an, dabei nur die id und den Typ
  2. 2
    CDMS
    prüft die Leserolle von company
  3. 3
    CDMS→Datenbank
    liest das Objekt company mit dieser id wie ein eigenes Lesen: mit seinen Zeilenfiltern und nur mit den Feldern aus der inneren response
  4. 4
    CDMS
    setzt das Ergebnis in das Feld company ein

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.

  1. 1
    CDMS→Datenbank
    liest die company
  2. 2
    CDMS
    prüft die Leserolle von employee
  3. 3
    CDMS
    baut eine eigene Suche auf employee mit dem Filter company.id = <id der company>
    Diesen Filter über die Rückreferenz ergänzt CDMS selbst. Du schreibst ihn nicht.
  4. 4
    CDMS→Datenbank
    sucht die passenden employee, mit deren Zeilenfiltern und nur mit den Feldern aus der inneren response
  5. 5
    CDMS
    setzt die Treffer als Liste in employees ein

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.

  1. 1
    CDMS
    prüft die Leserolle von employee
  2. 2
    CDMS→Client
    403 missing-permission|employee-read

Ergebnis: Die ganze Anfrage scheitert. Dieselbe Anfrage ohne das Objekt employees klappt mit derselben Rolle.

Eine Liste mit eigenem Filter

Die fünf zuletzt angelegten Mitarbeiter mit Nachnamen auf „M“
Anfrage
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" }]
        }
      }
    }
  ]
}
Antwort
{
  "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

Suche oben und Liste innen
POST /query
die Suche selbst
  • data plus meta
  • meta.totalCount sagt, wie viele Treffer es insgesamt gibt
  • blättern mit page und limit
{ field, parameter }
Liste in einem Objekt
  • nur die Einträge
  • keine Trefferzahl, die Liste ist ein einfaches JSON-Array
  • limit und page wirken 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

Was steht im Feld einer ausgebauten Referenz?
ArtLeserolle des ZielmodellsZiel vorhanden und sichtbarInhalt des Feldes
einzelnnein–403 für die ganze Anfrage
einzelnjaneinnull
einzelnjajaObjekt mit den angeforderten Feldern
Listenein–403 für die ganze Anfrage
Listejakeinerleere Liste []
Listejaeinigenur 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

Quellen im Code und in der Wissensdatenbank
  • CDMS/cdms-rest-api – Expander.expandModelResponse, ResponseDeserializer
  • CDMS/cdms-system-layer – AbstractLayer.recursiveRead, recursiveQuery, fetchAndSetModel, fetchAndSetList, enterField
  • CDMS/cdms-integrationtest – AbstractRecursiveRead, AbstractRoleDenialTest.readingARelationNeedsTheRelationRole, AbstractManyToManyTest
  • documentation/05-api-guide/04-lesen.md
Suchen