Referenz
NQL, die Abfragesprache für Kennzahlen
Jede Kennzahl-Kachel in nexxus ist eine NQL-Abfrage. Diese Seite erklärt, wie eine Abfrage aufgebaut ist, welche Quellen und Felder es gibt, wie sich Werte beschriften lassen, was die Antwort enthält und warum niemand über NQL mehr sieht, als er sehen darf.
NQL ist die Abfragesprache hinter jeder Kennzahl in nexxus: hinter den Kacheln auf dem Dashboard, auf den Auswertungen und hinter den Antworten des KI-Assistenten auf Fragen nach Zahlen. Eine Abfrage ist ein kleines JSON-Objekt. Sie nennt eine Quelle, die Kennzahlen, die gezählt oder summiert werden, die Merkmale, nach denen gegliedert wird, und Filter.
{ "source": "project", "measures": [{"name": "anzahl", "as": "anzahl"}], "dimensions": [{"field": "dateCreated", "grain": "month", "as": "monat"}], "filters": [{"field": "isActive", "op": "eq", "value": true}], "orderBy": [{"as": "monat", "dir": "asc"}]}Diese Abfrage zählt die aktiven Projekte je Monat, in dem sie angelegt wurden, und sortiert nach Monat.
NQL ist absichtlich kein SQL. Eine Abfrage kann keine Tabelle und keine Spalte frei benennen. Sie wählt aus, was eine Quelle veröffentlicht hat: Merkmale, Kennzahlen, Operatoren und Zeitraster aus festen Listen. Alles andere lehnt nexxus ab und nennt dabei, was gültig gewesen wäre.
Sie müssen NQL nicht schreiben können. Der KI-Assistent und der Kachel-Dialog schreiben die Abfrage für Sie. Diese Seite hilft, wenn Sie eine Abfrage lesen, von Hand anpassen oder aus einem Skript stellen wollen.
Wo Sie NQL begegnen
Abschnitt betitelt „Wo Sie NQL begegnen“- Im Kachel-Dialog. Unter der Vorschau öffnet Für Fortgeschrittene: Abfrage als JSON die NQL-Abfrage der Kachel. Sie bearbeiten sie dort direkt; sie Wird erst mit „Vorschau“ ausgeführt.
- Beim KI-Assistenten. Fragen Sie ihn nach Zahlen, schreibt er eine Abfrage und führt sie mit Ihren Rechten aus. Auf einer Auswertung baut er daraus Kacheln, die Sie bestätigen.
- In den Werkzeugen des KI-Assistenten und in KI-Clients. Der eingebaute Connector „Auswertungen“ bringt die Werkzeuge
nx_nql_schema(was Sie auswerten dürfen) undnx_nql_query(eine Abfrage ausführen) mit. Sie laufen mit den Rechten der Person der Unterhaltung, auch in einem verbundenen Client wie Claude Code. - Über die API.
GET /api/nql/schemaliefert die Quellen, die Sie lesen dürfen, mit ihren Merkmalen und Kennzahlen;POST /api/nql/queryführt eine Abfrage aus. Ein Skript authentifiziert sich dafür mit einem persönlichen API-Token und hat dann dieselben Rechte wie Sie.
Wer was sieht
Abschnitt betitelt „Wer was sieht“Die tragende Regel von NQL: Niemand bekommt Zahlen, die er sich nicht auch einzeln hätte holen dürfen. Eine Kachel läuft bei der Person, die sie ansieht, nicht bei der, die sie angelegt hat. Deshalb zeigt dieselbe Kachel verschiedenen Personen verschiedene Zahlen, und eine geteilte Auswertung gibt niemandem Daten.
Recht „Auswertungen“
Ohne core.analytics.read gibt es keine Abfrage.
Leserecht der Quelle
Etwa project.project.read für Projekte. Manche Quellen nur für Administratoren.
Veröffentlichte Felder
Nur Merkmale und Kennzahlen, die die Quelle anbietet.
Mandant und Zeilen
Nur Ihr Mandant, nur nicht gelöschte Datensätze, dazu der Zeilenfilter der Quelle.
| Stufe | Was bei Nein passiert |
|---|---|
Recht Auswertungen (core.analytics.read) | Die Abfrage scheitert mit 403. Ohne das Recht gibt es weder Kennzahl-Kacheln noch Auswertungen. |
| Leserecht der Quelle | Die Quelle fehlt im Schema der Person, eine Abfrage darauf scheitert mit 403. Die Kachel zeigt Keine Berechtigung für diese Daten. |
| Veröffentlichte Felder | Die Abfrage scheitert mit 400 und nennt die gültigen Felder. |
| Mandant und Löschstatus | Zeilen anderer Mandanten und gelöschte Datensätze sind schlicht nicht da. |
| Zeilenfilter der Quelle | Zeilen, die die Person nicht sehen darf, sind schlicht nicht da (bei KI-Sitzungen die fremden, bei Abwesenheiten alle außer den eigenen und denen der geleiteten Teams). |
Administratoren bestehen jede Rechteprüfung, aber nicht die Trennung der Mandanten. Ein .write-Recht für Auswertungen gibt es nicht, denn NQL schreibt nichts.
Der Aufbau einer Abfrage
Abschnitt betitelt „Der Aufbau einer Abfrage“| Feld | Pflicht | Form | Wirkung |
|---|---|---|---|
source | ja | Schlüssel, z. B. "project" | Die Quelle. Die Liste steht unter Die Quellen. |
measures | eins von beiden | [{name, as?, percentOfTotal?}] oder ["anzahl"] | Die Kennzahlen. name ist eine Kennzahl der Quelle, as der Spaltenname in der Antwort (Vorgabe: name). |
dimensions | eins von beiden | [{field, grain?, as?, format?, template?, labels?, ids?}] oder ["status"] | Die Merkmale, nach denen gegliedert wird. grain nur bei Datumsmerkmalen, die Beschriftung unter Werte beschriften. |
filters | nein | [{field, op, value}], auch mit Gruppen | Zeilen einschränken, bevor gezählt wird. op ist ohne Angabe eq. Gruppen mit ODER und NICHT unter Filter. |
having | nein | [{as, op, value}], auch mit Gruppen | Gruppen einschränken, nachdem gezählt wurde. as ist eine Kennzahl-Spalte. |
orderBy | nein | [{as, dir}] | Sortieren nach einer Spalte der Antwort, dir ist asc (Vorgabe) oder desc. |
limit | nein | Zahl | Höchstens so viele Zeilen. Vorgabe 1000, höchstens 5000. |
topN | nein | {by?, limit?, otherLabel?, within?} | Die stärksten Gruppen behalten, den Rest zusammenfassen. |
totals | nein | true oder {grandTotal, subtotalsFor} | Gesamt- und Zwischensummen neben den Zeilen. |
split | nein | Spaltenname eines Merkmals | Die Werte dieses Merkmals werden zu Spalten. |
Kennzahlen
Abschnitt betitelt „Kennzahlen“Eine Kennzahl ist eine fest definierte Rechnung der Quelle: anzahl zählt Projekte, stunden summiert die Stunden von Zeiteinträgen, kunden zählt verschiedene Kunden. Die Abfrage wählt die Kennzahl beim Namen, nicht die Rechnung. Deshalb kann keine Abfrage eine Spalte summieren, die die Quelle nicht dafür freigegeben hat.
Jede Kennzahl hat eine Funktion: count, count_distinct, sum, avg, min oder max. Das Schema nennt sie unter fn, dazu "additive": true oder false. Additiv sind count und sum: zwei Teilsummen ergeben zusammen die Gesamtsumme. Durchschnitte, Höchstwerte und das Zählen verschiedener Werte lassen sich nicht so zusammenfassen. nexxus lehnt sie deshalb dort ab, wo zusammengefasst werden müsste: neben einem Verweis-Merkmal, bei topN mit Rest, bei totals und bei percentOfTotal. Eine Absage ist besser als eine plausible falsche Zahl.
percentOfTotal: true an einer Kennzahl ergänzt eine Spalte <as>Anteil mit ihrem Anteil am Ganzen in Prozent, mit einer Nachkommastelle. Die Anteile ergeben zusammen genau 100.
Merkmale und Zeitraster
Abschnitt betitelt „Merkmale und Zeitraster“Ein Merkmal ist ein Feld, nach dem gegliedert und gefiltert werden kann: der Status, die Abrechnungsart, das Anlagedatum. Sein Typ steht im Schema:
| Typ | Beispiel | Besonderheit |
|---|---|---|
string | Projektname, Modell | Text |
number | Auswirkung eines Risikos (1 bis 4) | Zahl |
boolean | Aktiv, Abrechenbar | Die Kacheln zeigen Ja und Nein |
enum | Status, Art | Feste Werte; das Schema nennt sie unter values und ihre Namen unter valueLabels (OPEN heißt „Offen“) |
date | Angelegt am, Datum | Nimmt ein Zeitraster grain: day, week, month, quarter oder year. Ein Wert ist der Beginn des Zeitraums |
Verweis ("reference": true) | Kunde eines Projekts, Projekt eines Zeiteintrags | Zeigt auf ein anderes Modul. Die Antwort enthält den Namen, ein Filter braucht die Nummer |
Ein Verweis-Merkmal wird erst nach dem Zählen in Namen übersetzt. Haben zwei Kunden denselben Namen, landen sie in einer Zeile; die Antwort sagt das mit meta.merged. Deshalb verträgt ein Verweis nur additive Kennzahlen.
Mit "ids": true am Merkmal behält die Antwort stattdessen die Nummer: In den Zeilen stehen Nummern, in valueLabels der Spalte ihre Namen, und zwei Kunden gleichen Namens bleiben zwei Zeilen. In dieser Form braucht es ein Filter, denn ein Filter nennt die Nummer.
Ein Filter ist eine Liste von Bedingungen, und eine Liste heißt: alle ihre Einträge.
"filters": [ {"field": "status", "op": "in", "value": ["OPEN", "MITIGATED"]}, {"field": "dateCreated", "op": "gte", "value": "2026-01-01"}, {"field": "reviewDate", "op": "isNull"}]Ein Eintrag kann auch eine Gruppe sein. So kommen ODER und NICHT in eine Abfrage:
| Gruppe | Trifft zu, wenn |
|---|---|
{"all": [...]} | alle Einträge zutreffen |
{"any": [...]} | mindestens ein Eintrag zutrifft |
{"not": ...} | der Eintrag darin nicht zutrifft |
Offene Risiken, die schwer wiegen oder noch keinen Prüftermin haben:
"filters": [ {"field": "status", "op": "eq", "value": "OPEN"}, {"any": [ {"field": "impact", "op": "gte", "value": 3}, {"not": {"field": "reviewDate", "op": "isNotNull"}} ]}]Gruppen lassen sich bis zu 5 Ebenen tief schachteln (ein not zählt nicht als eigene Ebene). Eine Bedingung fasst höchstens 50 Vergleiche, ein Vergleich höchstens 500 Werte. having nimmt dieselben Gruppen, mit as statt field.
| Operator | Bedeutung | value |
|---|---|---|
eq, ne | gleich, ungleich | ein Wert |
gt, gte, lt, lte | größer, größer gleich, kleiner, kleiner gleich | ein Wert |
between, notBetween | innerhalb oder außerhalb zweier Grenzen; die Grenzen zählen als innerhalb | genau zwei Werte |
in, notIn | einer der Werte, keiner der Werte | eine Liste |
contains, notContains | enthält, enthält nicht | ein Text |
startsWith, endsWith | beginnt mit, endet mit | ein Text |
isNull, isNotNull | leer, nicht leer | keiner |
inLast, inNext | von so lange her bis heute, von heute bis so weit voraus | {"amount": 7, "unit": "day"}; unit ist day, week, month, quarter oder year |
inPeriod | in einem Kalenderzeitraum um heute; Wochen beginnen am Montag | eins von yesterday, today, tomorrow, oder last, this bzw. next mit Week, Month, Quarter, Year, z. B. lastMonth |
Nicht jeder Operator passt zu jedem Merkmal. Jedes Merkmal nimmt eq, ne, in, notIn, isNull und isNotNull. Zahlen, Daten und Text nehmen außerdem gt, gte, lt, lte, between und notBetween. contains, notContains, startsWith und endsWith gehören zu Text, inLast, inNext und inPeriod zu Daten. Alles andere endet mit 400.
Werte schreiben Sie so, wie der Typ des Merkmals es verlangt:
| Typ | Wert |
|---|---|
date | "2026-01-01" (dieser ganze Tag), ein Zeitpunkt wie "2026-01-01T00:00:00Z", Millisekunden seit 1970 oder {"relativeDays": -7} |
number | eine Zahl; Nachkommastellen bleiben erhalten |
boolean | true oder false |
enum | einer der Rohwerte aus values, z. B. "OPEN" |
string | ein Text |
| Verweis | die Nummer des Datensatzes, nicht sein Name; bei einem Merkmal, das auf eine Person zeigt, auch {"currentUser": true}, die fragende Person |
Ein Tag meint den ganzen Tag, in der Zeitzone des Mandanten, auch bei einem Zeitpunkt: eq "2026-03-31" nimmt den ganzen Tag, lte "2026-03-31" schließt seinen Abend ein, gt "2026-03-31" beginnt am 1. April.
Relative Werte werden bei jedem Lauf neu berechnet, eine gespeicherte Abfrage stimmt also auch morgen noch. {"relativeDays": 0} ist heute, -7 vor einer Woche, 3 in drei Tagen. inLast, inNext und inPeriod gelten genauso, und immer in ganzen Tagen.
Ein anderes Merkmal als Wert: {"field": "dateCreated"} vergleicht zwei Merkmale einer Zeile, mit eq, ne, gt, gte, lt und lte. Beide müssen denselben Typ haben, und zwei Verweise müssen auf dieselbe Art von Datensatz zeigen. Trifft ein Tag auf einen Zeitpunkt, zählt der Tag dieses Zeitpunkts in der Zeitzone des Mandanten.
{"field": "followUpDate", "op": "lt", "value": {"field": "dateCreated"}}Leere Felder: ne, notIn, notContains, notBetween und alles unter not finden auch Zeilen, in denen das Merkmal leer ist, denn ein leeres Feld ist nicht „Offen“. Die Größenvergleiche finden keine, deshalb ist {"not": {"field": "impact", "op": "gt", "value": 3}} nicht dasselbe wie lte 3: Es findet auch Risiken ohne Auswirkung.
Text ignoriert Groß- und Kleinschreibung bei eq, ne, in, notIn und den Operatoren für Teile eines Textes.
filters oder having? „Projekte aus 2026“ ist ein Filter: Er schränkt die Zeilen ein, bevor gezählt wird. „Kunden mit mehr als fünf Projekten“ ist ein having: Es schränkt die Gruppen ein, nachdem gezählt wurde.
{"source": "project", "measures": ["anzahl"], "dimensions": ["kunde"], "having": [{"as": "anzahl", "op": "gt", "value": 5}]}Die stärksten N und der Rest
Abschnitt betitelt „Die stärksten N und der Rest“topN behält die stärksten Gruppen und fasst den Rest in einer Zeile zusammen. Ein Diagramm mit vierzig Reihen liest niemand; so bleibt der Rest als ehrlicher Block sichtbar, statt stillschweigend zu fehlen.
"topN": {"by": "stunden", "limit": 5, "otherLabel": "Sonstige", "within": ["monat"]}by: die Kennzahl, nach der gerankt wird. Vorgabe ist die erste Kennzahl.limit: wie viele Gruppen bleiben. Vorgabe 5.otherLabel: der Name der Rest-Zeile, Vorgabe „Sonstige“.nulllässt den Rest weg.within: Merkmale, innerhalb derer gerankt wird.["monat"]heißt „die stärksten fünf je Monat“. Hat die Abfrage außer dem gerankten Merkmal noch ein Datum, muss es hier stehen; sonst würde „Sonstige“ in die Datumsspalte geschrieben, und nexxus lehnt die Abfrage ab.
Summen und Spalten
Abschnitt betitelt „Summen und Spalten“totals liefert Summen neben den Zeilen, nicht zwischen ihnen. Ein Diagramm, das eine Zeile „Gesamt“ zwischen seinen Daten bekäme, würde sie als riesigen Balken zeichnen.
"totals": {"grandTotal": true, "subtotalsFor": ["kategorie"]}totals: true ist die Kurzform für die Gesamtsumme. Die Antwort enthält dann grandTotal ({spalte: summe}) und subtotals (je Merkmal die Gruppen mit ihren Summen).
split dreht ein Merkmal in Spalten. Aus monat | status | anzahl wird monat | Offen | Behandelt, die Form, die ein Diagramm mit mehreren Reihen braucht. Höchstens 50 Spalten; bei mehr Werten erst topN anwenden.
Die Reihenfolge
Abschnitt betitelt „Die Reihenfolge“Nach dem Zählen laufen die Stufen immer in dieser Reihenfolge: having, topN, percentOfTotal, totals, orderBy, Beschriftung, split. Eine Abfrage kann sie also nicht in einer anderen Reihenfolge verlangen, und das Ergebnis hängt nicht davon ab, in welcher Reihenfolge die Felder im JSON stehen.
Werte beschriften
Abschnitt betitelt „Werte beschriften“Manche Werte sehen in den Daten technisch aus: der Objekttyp im Änderungsprotokoll ist app.modules.core.migration.MigrationStatus, eine Abwesenheitsart heißt intern SPECIAL_LEAVE. Drei Angaben an einem Merkmal bestimmen, wie seine Werte in Legenden, Achsen, Tabellen, Filterlisten und im PDF dastehen. Die Daten ändern sie nicht: Die Antwort enthält weiter den Rohwert, und ein Filter nennt weiter den Rohwert.
"dimensions": [ {"field": "className", "as": "typ", "format": ["class", "words"]}, {"field": "impact", "as": "auswirkung", "template": "Stufe {value}"}, {"field": "isActive", "as": "aktiv", "labels": {"true": "Aktiv", "false": "Inaktiv"}}]| Angabe | Form | Wirkung |
|---|---|---|
format | ein Format oder eine Liste von höchstens vier, der Reihe nach angewandt | Formt den Wert um, siehe die Tabelle darunter |
template | Text mit {value}, höchstens 80 Zeichen | Setzt Text um den Wert: aus 3 wird „Stufe 3“. "" entfernt die Vorlage der Quelle |
labels | {rohwert: name}, höchstens 200, je höchstens 80 Zeichen | Feste Namen für einzelne Werte. Bei einem enum nur seine Werte, bei Ja/Nein nur true und false |
| Format | Aus | Wird |
|---|---|---|
raw | der Wert, wie er gespeichert ist; schaltet die Vorgabe der Quelle ab | |
class | app.modules.core.migration.MigrationStatus | MigrationStatus (der Teil nach dem letzten Punkt) |
words | MigrationStatus, SPECIAL_LEAVE | Migration Status, Special leave |
upper | Offen | OFFEN |
lower | Offen | offen |
capitalize | offen | Offen (nur der erste Buchstabe) |
So entsteht ein Name: Steht der Wert in labels, gilt dieser Name. Sonst beginnt nexxus mit dem Namen, den die Quelle für den Wert kennt (bei einem enum etwa „Offen“ für OPEN), oder mit dem Wert selbst, wendet die Formate der Reihe nach an und setzt das Ergebnis in die Vorlage.
Eine Quelle kann ein Format vorgeben. Der Objekttyp des Änderungsprotokolls steht deshalb ohne jede Angabe als MigrationStatus da; das Schema zeigt die Vorgabe unter format. Die Abfrage ersetzt format und template der Quelle, "format": "raw" schaltet die Vorgabe ab.
Grenzen:
- Datumsmerkmale nehmen keine der drei Angaben. Eine Kachel schreibt ein Datum nach seinem Zeitraster („Okt. 26“, „Q3 2026“).
- Ja/Nein-Merkmale nehmen nur
labels. - Den Rest von
topN(„Sonstige“) beschriftet nichts. splitbenennt seine Spalten nach den Namen, der Schlüssel jeder Spalte (as) bleibt der Rohwert. Eine Sortierung oder eine Reihenfarbe wandert also nicht mit, wenn Sie die Beschriftung ändern.- Ergeben zwei Rohwerte denselben Namen, stehen sie in Diagrammen zusammen.
Die Antwort
Abschnitt betitelt „Die Antwort“{ "columns": [ {"as": "typ", "role": "dimension", "type": "string", "label": "Objekttyp", "grain": null, "valueLabels": {"app.modules.communication.email.Email": "Email"}}, {"as": "anzahl", "role": "measure", "type": "number", "label": "Anzahl Änderungen", "grain": null, "valueLabels": null} ], "rows": [["app.modules.communication.email.Email", 1800], ["Sonstige", 412]], "meta": {"rowCount": 2, "truncated": false, "durationMs": 37, "merged": false, "foldedIntoOther": true, "split": null}, "subtotals": [], "grandTotal": null}columnsbeschreibt die Spalten einmal,rowsenthält die Zeilen als Listen in dieser Reihenfolge. Ein Diagramm braucht die Spalten nur einmal, nicht in jeder Zeile.labelist der Name der Spalte,valueLabelsdie Beschriftung der Werte eines Merkmals: Rohwert und Name, für die Werte in den Zeilen, deren Name vom Rohwert abweicht. Ganze Zahlen stehen als Text ohne Nachkommastellen ("3"). Mitidsan einem Verweis steht dort der Name jeder Nummer in den Zeilen. Sonstnull.- Datumswerte kommen immer als Millisekunden seit 1970, gleich welches Zeitraster.
meta.truncated: trueheißt: Die Zeilengrenze hat Zeilen abgeschnitten. Wer die Antwort weiterverwendet, sollte das sagen, statt Vollständigkeit vorzutäuschen.meta.merged: Verweis-Werte gleichen Namens wurden zusammengefasst.meta.foldedIntoOther:topNhat einen Rest gebildet.meta.split: das Merkmal, das zu Spalten wurde.
| Status | Wann | Beispiel |
|---|---|---|
| 403 | Das Recht für Auswertungen fehlt, oder das Leserecht der Quelle; die Meldung nennt den Schlüssel | |
| 400 | Unbekannte Quelle, unbekanntes Merkmal, unbekannte Kennzahl, unbekannter Operator oder unbekanntes Zeitraster, ein ungültiger Enum-Wert, ein Verweis auf eine Spalte, die die Abfrage nicht hat, eine nicht additive Kennzahl, wo addiert werden müsste, eine Beschriftung, die das Merkmal nicht nimmt | „Unknown dimension ‘kundenname’ for source ‘project’. Available: billable, billingModel, dateCreated, designation, isActive, isInternal, kunde, projektgruppe“ |
Die Meldungen sind für Menschen und für Sprachmodelle geschrieben: Sie zählen auf, was gültig gewesen wäre. Der KI-Assistent korrigiert damit seine eigene Abfrage, bevor Sie eine Kachel zu sehen bekommen.
Die Quellen
Abschnitt betitelt „Die Quellen“Welche Quellen eine Person sieht, hängt von ihren Rechten ab. Die folgende Liste ist die eines Administrators. Merkmale vom Typ date bieten alle Zeitraster an. Die Namen der Quellen, Merkmale und Kennzahlen stehen in jeder Sprache auf Deutsch, so wie der Server sie liefert. Die erste Spalte nennt den Schlüssel der Quelle, ihren Namen und das Leserecht, das sie verlangt.
| Quelle | Merkmale | Kennzahlen |
|---|---|---|
projectProjekte project.project.read | designation Projekt, kunde Kunde (Verweis), projektgruppe Projektgruppe (Verweis), isActive Aktiv, isInternal Intern, billable Abrechenbar, billingModel Abrechnungsart, dateCreated Angelegt am | anzahl Anzahl Projekte, kunden Kunden (verschiedene) |
timesheetEntryZeiteinträge timesheet.entry.read | entryDate Datum, projekt Projekt (Verweis), source Herkunft, billable Abrechenbar | eintraege Einträge, stunden Stunden |
vacationRequestAbwesenheiten absence.vacationRequest.read | mitarbeiter Mitarbeiter (Verweis), type Art, status Status, fromDate Beginn | antraege Anträge, tage Tage |
partnerKunden und Lieferanten partner.partner.read | name Name, partnerType Art, isActive Aktiv, isInternal Intern, currency Währung, dateCreated Angelegt am | anzahl Anzahl |
ismsRiskISMS-Risiken isms.risk.read | status Status, likelihood Eintrittswahrscheinlichkeit, impact Auswirkung, reviewDate Nächste Prüfung, dateCreated Angelegt am | anzahl Anzahl Risiken, wahrscheinlichkeitSchnitt und auswirkungSchnitt (Durchschnitte) |
ismsMeasureISMS-Maßnahmen isms.measure.read | category TOM-Kategorie, measureType Art, status Status, recurrence Turnus, approvalRequired Freigabe nötig, reviewDate Erste Fälligkeit, dateCreated Angelegt am | anzahl Anzahl Maßnahmen |
formFormulare formBuilder.read | name Formular, type Typ, status Status, version Version, dateCreated Angelegt am | anzahl Anzahl Formulare, hoechsteVersion Höchste Version |
userBenutzerkonten authentication.user.read | username Benutzername, accountType Kontotyp, invitationPending Einladung offen, lastLoginAt Letzte Anmeldung, dateCreated Angelegt am | anzahl Anzahl Konten |
aiSessionKI-Sitzungen ai.session.use | dateCreated Begonnen am, model Modell, status Status, origin Herkunft, createdById Benutzer-ID | sitzungen Sitzungen, nachrichten Nachrichten, nachrichtenSchnitt Ø Nachrichten je Sitzung |
aiScheduleRunSchedule-Läufe aiAssistant.schedule.read | status Status, trigger Auslöser, scheduleId Schedule-ID, model Modell, scheduledFor Solltermin, dateCreated Angelegt am | laeufe Läufe, dauerMs Ø Dauer (ms), werkzeugaufrufe Werkzeugaufrufe, werkzeugfehler Werkzeugfehler |
auditLogProtokoll (Änderungen) nur Administratoren | className Objekttyp (Format class), eventName Vorgang, actor Person, dateCreated Zeitpunkt | anzahl Anzahl Änderungen, personen Aktive Personen (verschiedene) |
Einige Quellen sind bewusst knapp: Die Risiken kommen ohne Titel und Beschreibung, die Partner ohne Kontakt- und Bankdaten, das Änderungsprotokoll ohne alte und neue Werte, die Benutzerkonten ohne persönliche Angaben. Was eine Quelle nicht veröffentlicht, kann niemand auswerten.
Zwei Quellen schränken die Zeilen je Person ein: KI-Sitzungen zeigen jeder Person nur ihre eigenen, Administratoren alle; Abwesenheiten zeigen die eigenen und die der Teams, die die Person leitet, wie die Liste der Abwesenheiten.
Quellen aus Paketen
Abschnitt betitelt „Quellen aus Paketen“Jedes Fachobjekt eines aktiven Paket-Moduls ist außerdem eine eigene Quelle mit dem Schlüssel <paket>:<fachobjekt>. Die Liste oben enthält sie nicht, weil sie davon abhängen, was Ihr Mandant installiert hat. Das Paket muss dafür nichts tun.
| Was | Regel |
|---|---|
| Name | Die Mehrzahl des Fachobjekts, dahinter das Paket in Klammern, zum Beispiel Kunden (Vorgänge). |
| Recht | Das Leserecht des Fachobjekts. Ein Datensatz hat keine eigenen Rechte: Wer das Fachobjekt lesen darf, sieht alle seine Zeilen. |
| Merkmale | Die indizierten Felder des Fachobjekts und jeder Verweis, dazu id, createdAt, updatedAt und createdBy. Ein Feld ohne Index fehlt auch hier. |
| Kennzahlen | anzahl, dazu je Zahlenfeld summe_<feld>, schnitt_<feld>, min_<feld> und max_<feld>. |
Drei Dinge verhalten sich anders als bei den Quellen oben:
- Felder mit mehreren Werten: Ein Feld mit
multipleführt einen Datensatz unter jedem seiner Werte. Eine Summe über solche Gruppen zählt ihn einmal je Wert. - Text: Vergleiche achten nicht auf Groß- und Kleinschreibung und reichen bis zum 500. Zeichen eines Felds.
- Ausgeschaltet: Solange ein Paket deaktiviert ist, fehlen seine Quellen. Kacheln darauf zeigen Dieser Bereich ist ausgeschaltet, Benachrichtigungsregeln stehen auf kann nicht prüfen. Nach dem Aktivieren arbeiten beide wieder wie vorher.
Grenzen
Abschnitt betitelt „Grenzen“- Eine Quelle je Abfrage. NQL verbindet keine Quellen. Werte aus anderen Modulen kommen nur als Verweis-Merkmal, etwa der Kunde eines Projekts.
- Keine zweite Y-Achse. Zwei Größen mit verschiedener Skala sind zwei Kacheln.
- Zeilen: Vorgabe 1000, höchstens 5000; vor
topNund Zusammenfassungen höchstens 5000 Gruppen aus der Datenbank.splithöchstens 50 Spalten.
Für Entwickler: eine Quelle anbieten
Abschnitt betitelt „Für Entwickler: eine Quelle anbieten“Die Fachobjekte eines Pakets werden von selbst zu Quellen, siehe Quellen aus Paketen. Alle anderen Quellen entstehen im Code von nexxus.
Im Regelfall macht eine Annotation eine Entität auswertbar, und zwar genau so weit, wie sie es aufzählt:
@NqlExposed( label = "Protokoll (Änderungen)", permission = "core.auditLog.read", adminOnly = true, description = "Änderungen im System: Anzahl nach Objekttyp, Vorgang, Person und Zeit.", dimensions = [ NqlDim(field = "className", label = "Objekttyp", format = ["class"]), NqlDim(field = "eventName", label = "Vorgang"), NqlDim(field = "dateCreated", label = "Zeitpunkt"), ], measures = [ NqlMeas(fn = "count", name = "anzahl", label = "Anzahl Änderungen"), ])@Entityopen class AuditLog { ... }- Positivliste, absichtlich. Abgefragt werden kann nur, was unter
dimensionsundmeasuressteht. Sonst wäre jedes neue Feld sofort auswertbar, beim nächstenpasswordHashein Datenleck durch Unterlassung. permissionist Pflicht und derselbe Schlüssel, den die Liste des Moduls verlangt.adminOnly = trueöffnet eine Quelle nur Administratoren.- Der Typ wird aus dem Feld gelesen. Werte bekommen Namen über
valueLabels = ["OPEN=Offen", ...]; trägt ein Enum eine Eigenschaftlabel, gilt diese.formatundtemplategeben die Beschriftung vor, solange eine Abfrage nichts anderes sagt. - Der Schlüssel der Quelle ist der Klassenname mit kleinem Anfangsbuchstaben, solange
keynichts anderes sagt.
Braucht eine Quelle einen Zeilenfilter unterhalb des Mandanten (nur eigene Datensätze) oder ein Merkmal aus einem anderen Modul, schreiben Sie sie als @Component, das NqlSource implementiert. Merkmale aus anderen Modulen gruppieren nach der Nummer und werden danach über einen NqlRefResolver beschriftet, mit einer Abfrage je Ergebnis:
NqlDimension.ref("kunde", "partnerId", NqlEntityLabelResolver(entityManager, Partner::class.java) { it.name }).label("Kunde")Mandant und Löschstatus verantwortet keine Quelle selbst. Die setzt nexxus für jede Abfrage. Fehlt der Mandant, liefert eine Abfrage nichts statt alles.