You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »






1. Einführung

1.1. Was ist GraphQL?

GraphQL ist eine neue, einfache und flexible Möglichkeit Daten von microtech büro+ über eine Schnittstelle abfragen und auswerten zu können. Frei übersetzt von der Seite der "GraphQL Foundation": "GraphQL ist eine Abfragesprache für APIs und eine Laufzeitumgebung für die Erfüllung dieser Abfragen mit Ihren vorhandenen Daten. GraphQL bietet eine vollständige und verständliche Beschreibung der Daten in Ihrer API, gibt Anwendern die Möglichkeit, genau das abzufragen, was sie benötigen, und nicht mehr, erleichtert die Weiterentwicklung von APIs im Laufe der Zeit und ermöglicht leistungsstarke Entwicklerwerkzeuge."

Der große Vorteil von GraphQL ist die Einfachheit der Abfragen und die Passgenauigkeit der Antworten. GraphQL liefert auf eine Abfrage die exakte Antwort, nicht benötigte Informationen werden nicht geladen oder ausgegeben. Aus diesem Grund ist GraphQL sehr performant bei Abfragen und Antworten, auch bei der Abfrage großer Datenbanken. Nutzer von GraphQL können sich gut in dieses sehr verständliche Schema einlesen.

Eine FAQ zu GraphQL ist hier zu finden: https://graphql.org/faq/

Wie GraphQL funktioniert und wie es generell angewendet werden kann, ist auf dieser Seite der Foundation dokumentiert: https://graphql.org/learn/

Mit dem Ziel, alle Vorteile von GraphQL verständlich mit Beispielen zu vermitteln, führt diese Dokumentation Schritt für Schritt in die Funktionalität der Schnittstelle ein und beschäftigt sich auch mit den spezifischen Aspekte des microtech GraphQL-Schemas und der Pagination. Wir empfehlen diese Dokumentation von Anfang bis Ende durchzuspielen, um möglichst zielgerichtet die Besonderheiten der Schnittstelle und den Einsatz in Zusammenhang mit der microtech Software zu erlernen.

1.2. Wofür verwendet microtech GraphQL?

Der Zugriff via GraphQL soll unseren Partnern dazu dienen die COM-Aktiv-Schnittstelle abzulösen; Aktuell bietet GraphQL eine rein lesende Abfrage in Zusammenspiel mit der microtech Software, in späteren Iterationen ist geplant, auch schreibende Abfragen zu ermöglichen. Der Datenbankzugriff über die GraphQL-Schnittstelle ist derzeit nicht für Endanwender gedacht.

1.3. Voraussetzungen

Um den Datenbankzugriff per GraphQL nutzen zu können, ist eine installierte microtech BETA Version (Gen. 24 Enterprise) erforderlich. Vor Nutzung der GraphQL-Schnittstelle ist zu prüfen, ob der Datenserver bereits über das Protokoll HTTPS/2 läuft und ein entsprechendes Zertifikat in der "microtech Serverkonfiguration" (BpConfig.exe) hinterlegt wurde - weitere Informationen finden sich aktualisiert in der microtech Hilfe: https://hilfe.microtech.de/x/AwC8HQ.

1.4. Aufruf des GraphQL-Playgrounds

Sind die in 1.3. genannten Voraussetzungen erfüllt, kann ein Aufgaben-Dienst der Art GraphQL-Server angelegt & gestartet werden und die URL zur Weboberfläche aus dem Ereignisprotokoll heraus kopiert werden. Die URL ist zunächst zu Kopieren.

Um den Playground aufrufen zu können, diese URL in einem Webbrowser einfügen. Die nun im Browser geöffnete Spielwiese erfüllt den Zweck, dass der Gesamtumfang der GraphQL-Schnittstelle mit allen zur microtech Software möglichen Abfragen getestet werden kann. Durch das Token ist ein Zugriff auf alle Datenbanktabellen des Servers (die durch die Berechtigung im Aufgaben-Dienst freigegeben sind) gewährleistet. Im folgenden Verlauf haben wir praktische Beispiele zusammengestellt, welche als Inspiration und Anleitung für weitere, eigene Abfragen dienen können.

2. Grundlagen

2.1 Grundlegende Abfragestruktur

Das umfassende GraphQL-Schema in der microtech Software stellt spezifische Felder für jede nach außen geführte Datenbanktabelle bereit. In der einfachsten Form der Anwendung können diese Felder in einer Tabellen-Abfrage ohne zusätzliche Parameter genutzt werden.

2.1.1 Rückgabe von Datensätzen

Mehrere Datensätze (Pluralform):

Wenn der Tabellenname in seiner Pluralform im Schema aufgeführt wird, gibt die Abfrage standardmäßig alle Datensätze dieser Tabelle zurück. Der Rückgabetyp ist in diesem Fall eine Liste von zur Tabelle passenden Row-Objekten.

Beispiel:
query {
  products {
    artNr
    suchBeg
    artikelArt
  }
}

Zusätzliche Parameter können verwendet werden, um die Sortierfolge und den Umfang der zurückgegebenen Datensätze zu steuern. Diese Funktionalität wird in späteren Abschnitten detailliert beschrieben.

Einzelne Datensätze (Singularform):

Manche Tabellen sind so gestaltet, dass sie immer nur genau einen Datensatz enthalten. Für solche Tabellen wird der Tabellenname in der Singularform im Schema verwendet, und die Abfrage gibt nur diesen einen Datensatz zurück.

Beispiel:
query {
  client {
    mandNr
    mandTyp
  }
}

Diese Art von Abfragen akzeptiert keine Parameter.

2.1.2 Felder der Tabelle

Die Felder, die innerhalb einer Abfrage spezifiziert werden können, entsprechen den Feldern der jeweiligen Datenbanktabelle. Diese Felder sind ebenso in der Benutzeroberfläche der microtech Software sichtbar. Weitere Details zu den Feldern werden in späteren Kapiteln behandelt.

2.1.3 Mehrere Tabellenabfragen

Es ist möglich, in einer einzigen Abfrage auf mehrere Tabellen zuzugreifen, indem man sie nacheinander innerhalb der query angibt. Obwohl sich dieses Handbuch in den Beispielen meistens auf die Abfrage einer einzigen Tabelle konzentrieren wird, können diese Abfragen beliebig kombiniert werden.

Beispiel:
query {
  products {
    artNr
    suchBeg
    artikelArt
  }
  client {
    mandNr
    mandTyp
  }
}

3. Abfrageoptionen für die Datensatzauswahl

3.1 Einführung

Das GraphQL-Schema bietet spezielle Parameter zur präzisen Steuerung der zurückgegebenen Datensätze. Die wichtigsten Parameter für diese Aufgabe sind exactMatch, nearestMatch und allBetween. Es ist wichtig zu beachten, dass stets nur einer dieser Parameter in einer einzelnen Abfrage verwendet werden kann.

3.2 Gemeinsame Merkmale

  • Rückgabetyp: Unabhängig vom verwendeten Parameter ist der Rückgabetyp stets eine Liste von Row-Objekten, die zur jeweiligen Tabelle passen. Der Typ der Rückgabe ändert sich nicht aufgrund der verwendeten Parameter.

  • Sortierfolgenauswahl: Für alle drei Parameter besteht die Möglichkeit, den für die Abfrage verwendete Sortierfolge anzugeben. Diese entscheidet auch über welche Felder die Datenauswahl weiter eingeschränkt werden kann. Darauf wird später im Detail eingegangen.

3.3 Abfrageoptionen

3.3.1 exactMatch

Mit exactMatch können Sie Datensätze abfragen, die genau den angegebenen Bedingungen entsprechen. Der Rückgabetyp ist eine Liste, allerdings enthält diese Liste höchstens einen Datensatz.

Beispiel:
query {
  products(
    exactMatch: {
      ...
    }
  ) {
    artNr
    suchBeg
    artikelArt
  }
}

3.3.2 nearestMatch

nearestMatch ähnelt exactMatch, gibt jedoch den nächstfolgenden Datensatz in der Sortierreihenfolge zurück, falls keine exakte Übereinstimmung gefunden wird. Auch hier ist der Rückgabetyp eine Liste, die jedoch höchstens einen Datensatz enthält.

Beispiel:
query {
  products(
    nearestMatch: {
      ...
    }
  ) {
    artNr
    suchBeg
    artikelArt
  }
}

3.3.3 allBetween

Mit allBetween können Sie eine Reihe von Datensätzen abrufen, die zwischen den angegebenen Werten liegen. Die Rückgabe ist eine Liste von Row-Objekten in der ausgewählten Sortierreihenfolge, die mehrere Datensätze enthalten kann.

Beispiel:
query {
  products(
    allBetween: {
      ...
    }
  ) {
    artNr
    suchBeg
    artikelArt
  }
}

3.4 Auswahl der Sortierfolge

3.4.1 Grundlagen der Sortierfolge

Jede Tabelle in der microtech Software hat eine oder mehrere vordefinierte Sortierfolgen. Diese wird durch das by...-Feld innerhalb der Parameter exactMatch, nearestMatch oder allBetween ausgewählt. Der Name der Sortierfolge muss dabei nicht zwangsläufig alle Namen der darin enthaltenen Felder beinhalten.

Was ist eine Sortierfolge?

Eine Sortierfolge ist eine Anordnung der Datensätze einer Tabelle basierend auf den Werten eines oder mehrerer Felder. Bei Sortierfolgen mit mehreren Feldern wird zuerst das erste Feld für die Sortierung verwendet. Wenn zwei Datensätze im ersten Feld identische Werte haben, wird das nächste Feld in der Sortierfolge herangezogen, um die Reihenfolge zu bestimmen, und so weiter.

Beispiel mit exactMatch:
query {
  products(
    exactMatch: {
      byArtNr: {
        ...
      }
    }
  ) {
    artNr
    suchBeg
    artikelArt
  }
}

In diesem Beispiel wird die Sortierfolge byArtNr genutzt, um die Reihenfolge der zurückgegebenen Datensätze zu bestimmen.

3.4.2 Beschränkungen und Hinweise

  • Es ist immer nur möglich, eine Sortierfolge für eine Abfrage auszuwählen.
  • Die gewählte Sortierfolge beeinflusst, welcher Datensatz bei exactMatch und nearestMatch als "erster" oder "nächster" betrachtet wird.
  • Bei der Verwendung von allBetween bestimmt die Sortierfolge nicht nur die Reihenfolge der zurückgegebenen Daten, sondern auch deren Umfang, da die Auswahl der Datensätze durch die Festlegung eines Anfangs- und Endpunkts eingeschränkt ist.

3.5 Angabe von Feldwerten in der Sortierfolge

Nach der Festlegung der Sortierfolge über den by...-Parameter können Sie die Feldwerte spezifizieren, um die Ergebnisse präziser einzuschränken. Dies ermöglicht die gezielte Auswahl der zurückgegebenen Datensätze innerhalb der definierten Sortierfolge.

3.5.1 Einzelne Feldwerte

Im einfachsten Szenario können Sie einen einzelnen Feldwert innerhalb der gewählten Sortierfolge angeben. Dies verfeinert die Datensatzauswahl und begrenzt die Resultate auf Datensätze, die den spezifizierten Kriterien entsprechen.

Typ des Feldwerts

Jedes Feld in der Datenbank hat einen festgelegten Typ. Beim Angeben eines Feldwerts muss daher auch der entsprechende Typ spezifiziert werden. Abhängig vom Feldtyp stehen verschiedene Typen wie int, float, string und weitere zur Verfügung. Die genauen Typ-Optionen können Sie dem Schema entnehmen.

Beispiel:

Im folgenden Beispiel wird die Sortierfolge byArtNr verwendet. Dabei wird der Wert 1 für das Feld artNr angegeben, und zwar als string.

query {
  products(
    exactMatch: {
      byNr: {
        artNr: { string: "1" }
      }
    }
  ) {
    artNr
    suchBeg
    artikelArt
  }
}

Mit dieser Abfrage würden nur Artikel zurückgegeben, die genau der angegebenen artNr entsprechen.

3.5.2 Mehrere Feldwerte in der Sortierfolge

In vielen Fällen umfasst die Sortierfolge mehr als ein Feld. In solchen Szenarien können Sie mehrere Feldwerte spezifizieren, um die Auswahl der zurückgegebenen Datensätze noch präziser zu gestalten.

Verschachtelte Angabe und Reihenfolge

Die Feldwerte werden in der Reihenfolge angegeben, in der sie in der Sortierfolge erscheinen. Die Angabe erfolgt verschachtelt, beginnend mit dem ersten Feld der Sortierfolge. Es ist nicht erforderlich, alle Felder der Sortierfolge anzugeben. Wenn jedoch Felder spezifiziert werden, muss dies in der Reihenfolge der Sortierfolge geschehen, was durch die Verschachtelung erzwungen wird

Beispiel

Im folgenden Beispiel wird die Sortierfolge byArtNrLagNr für die Tabelle warehouseSlotInventory (LagerplatzBestand) verwendet. Es werden mehrere Felder spezifiziert: artNr, lagNr, lpRegal und lpSaeule.

query  {
  warehouseSlotInventory(
    allBetween: {
      byArtNrLagNr: {       
        artNr: { string: "8"         
          lagNr: { string: "1"
            lpRegal: { string: "4"
              lpSaeule: { string: "S1" }
            }
          }
        }
      }
    }
  ) {
    artNr
    lagNr
    lpRegal
    lpSaeule
    lpEbene
  }
}

Mit dieser Abfrage würden nur Datensätze zurückgegeben, die genau den angegebenen Kriterien für artNr, lagNr, lpRegal und lpSaeule entsprechen.

3.5.3 Von/Bis-Bereich in allBetween-Abfragen

Wenn Sie die allBetween-Option verwenden, haben Sie zusätzlich die Möglichkeit, für das zuletzt angegebene Feld der Sortierfolge einen Von/Bis-Bereich festzulegen. Diese spezielle Funktionalität ermöglicht es Ihnen, eine Reihe von Datensätzen abzurufen, die innerhalb dieses Bereichs liegen.

Beispiel

Im folgenden Beispiel verwenden wir erneut die Tabelle warehouseSlotInventory (LagerplatzBestand) und die Sortierfolge byArtNrLagNr. Für das Feld lpEbene wird ein Von/Bis-Bereich mit den Werten "E" bis "E99" angegeben:

query  {
  warehouseSlotInventory(
    allBetween: {
      byArtNrLagNr: {       
        artNr: { string: "8"         
          lagNr: { string: "1"
            lpRegal: { string: "4"
              lpSaeule: { string: "S1"               
                lpEbene: {
                  from: { string: "E" },
                  to: { string: "E99" }
                }
              }
            }
          }
        }
      }
    }
  ) {
    artNr
    lagNr
    lpRegal
    lpSaeule
    lpEbene
  }
}

Mit dieser Abfrage werden alle Datensätze zurückgegeben, deren Feldwerte für lpEbene im Bereich von "E" bis "E99" liegen und für die vorherigen Felder exakt den angegeben Wert haben.

Wichtige Hinweise

  • Der Von/Bis-Bereich ist nur für das letzte in der Abfrage angegebene Feld der Sortierfolge verfügbar.
  • Die Angabe eines einzelnen Wertes in einer allBetween-Abfrage fungiert als Start- und Endwert für dieses Feld. Ein separates from und to ist in diesem Fall nicht erforderlich.

3.7.3.1 Verständnis der Sortierfolge-Beschränkungen in allBetween-Abfragen

Die allBetween-Abfrage in GraphQL erlaubt Ihnen, einen Von/Bis-Bereich nur für das zuletzt angegebene Feld der Sortierfolge festzulegen. Diese Einschränkung ist nicht willkürlich, sondern ergibt sich aus der Art und Weise, wie die Daten in der microtech Software sortiert und indiziert sind.

Beispiel: Tabelle mit Spalten A und B

Um dies zu veranschaulichen, betrachten wir ein einfaches Beispiel. Stellen Sie sich eine Tabelle vor, die zwei Spalten "A" und "B" hat. Jede Spalte enthält die Werte 1 bis 3.

Nun stellen Sie sich vor, Sie möchten einen Von/Bis-Bereich für Spalte A zwischen 1 und 3 festlegen und gleichzeitig für Spalte B den spezifischen Wert 2 auswählen. Da die Daten nach der Sortierfolge sortiert sind, würde eine solche Abfrage alle Zeilen zwischen dem Startpunkt (1,2) und dem Endpunkt (3,2) zurückgeben.

Das Ergebnis wäre wie folgt:

A | B
-----
1 | 1
1 | 2   <-- Startpunkt (1,2)
1 | 3
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2   <-- Endpunkt (3,2)
3 | 3

Das Problem hierbei ist, dass das Ergebnis auch Zeilen mit Werten für Spalte B enthalten würde, die nicht gleich 2 sind. Dies widerspricht dem Versuch, für B den spezifischen Wert 2 festzulegen.

Schlussfolgerung

Daher erlaubt die allBetween-Option nur die Angabe eines Von/Bis-Bereichs für das zuletzt angegebene Feld der Sortierfolge. Dies stellt sicher, dass die zurückgegebenen Datensätze den beabsichtigten Kriterien entsprechen und keine unerwarteten Werte in den vorherigen Feldern der Sortierfolge enthalten.

4. Abkürzungen und Effizienz

4.1 Abkürzung für Standardsortierung

Ein nützliches Feature in der microtech Software ist die Möglichkeit, eine Abkürzung für die Standardsortierung einer Tabelle zu verwenden. Anstatt den by...-Parameter explizit zu setzen, kann direkt das erste Feld der Standardsortierung der jeweiligen Tabelle angegeben werden. Dies ist besonders hilfreich, um die Abfrage effizienter und übersichtlicher zu gestalten.

Beispiel

Die Standardsortierung der products-Tabelle erfolgt nach dem Feld artNr. In diesem Fall können Sie folgende Abfrage verwenden:

query {
  products(
    allBetween: {
      artNr: {
        from: { string: "0" }
        to: { string: "12000" }
      }
    }
  ) {
    artNr
    suchBeg
    artikelArt
  }
}

Diese Abfrage ist äquivalent zu einer Abfrage, die die Standardsortierung explizit mit byArtNr angibt.

Wichtige Hinweise
  • Die Abkürzung kann nur dann verwendet werden, wenn der by...-Parameter nicht angegeben ist. Es ist nicht möglich, beide gleichzeitig zu verwenden.
  • Diese Abkürzung erleichtert die Abfrage und macht sie übersichtlicher, da sie die implizite Auswahl der Standardsortierung ermöglicht.

Das Verwenden dieser Abkürzungen kann Abfragen effizienter und einfacher lesbar machen und ist eine praktische Methode, um die Standardsortierung einer Tabelle schnell anzusprechen.

4.2 Abkürzung für exactMatch

Im GraphQL-Schema der microtech Software können Sie das erste Feld der Standardsortierung direkt auf der gleichen Ebene wie exactMatch, nearestMatch und allBetween angeben. Wenn Sie dies tun, wird implizit exactMatch für diese Abfrage ausgewählt. Das ist eine Abkürzung, die die Abfrage schneller und einfacher macht.

Beispiel

Angenommen, die Standardsortierung der products-Tabelle ist nach dem Feld artNr geordnet. Dann könnte die Abfrage wie folgt aussehen:

query {
  products(
    artNr: { string: "1" }
  ) {
    artNr
    suchBeg
    artikelArt
  }
}

Diese Abfrage wäre äquivalent zu einer, die exactMatch und byArtNr explizit angibt:

query {
  products(
    exactMatch: {
      byNr: {
        artNr: { string: "1" }
      }
    }
  ) {
    artNr
    suchBeg
    artikelArt
  }
}
Wichtige Hinweise
  • Diese Methode ist nur anwendbar, wenn exactMatch, nearestMatch und allBetween nicht auf der gleichen Ebene angegeben sind.
  • Die Abkürzung dient zur Vereinfachung der Abfrage und ermöglicht eine implizite Auswahl von exactMatch, basierend auf dem ersten Feld der Standardsortierung.

5. Filterausdrücke in GraphQL

5.1 Einleitung

Warum sind Filter wichtig?

Filter sind ein wesentliches Instrument im GraphQL-Schema, die es Entwicklern ermöglichen, spezifische Daten aus der microtech-Software abzurufen. Durch die Verwendung von Filtern können Sie die Menge der zurückgegebenen Daten einschränken und somit effizientere und zielgerichtete Abfragen durchführen.

Wo können Sie Filter anwenden?

Filter können an verschiedenen Stellen des GraphQL-Schemas angewendet werden. Ihnen stehen die Filter fastFilter (Schnelle Filter; Auswertung im Datenserver), slowFilter (Langsame Filter, Auswertung im Anwendungsserver), und keyFilter (Sehr schnelle, auf filterbare Felder der Sortierfolge beschränkt Filter, Auswertung im Datenserver) zur Verfügung.

fastFilter und slowFilter können auf der gleichen Ebene wie exactMatch, nearestMatch, und allBetween angegeben werden.

Beispiel:
query {
  products(
    allBetween: { ... }
    fastFilter: { ... }
    slowFilter: { ... }
  ) {
    artNr
    suchBeg
    artikelArt
  }
}

keyFilter können innerhalb der by... Sortierfolgenauswahl angegeben werden.

Beispiel:
query {
  products(
    allBetween: {
      bySuchBeg: {
        suchBeg: { ... }
        keyFilter: { ... }
      }
    }
  ) {
    artNr
    suchBeg
    artikelArt
  }
}

Hinweis: Nicht alle Felder der Sortierung sind immmer in keyFilter verwendbar. Ausgeschlossen sind solche die eine spezielle Sortierung (beispielsweise "Numerisch") haben.

Was erwartet Sie in den nachfolgenden Abschnitten?

In den nachfolgenden Abschnitten werden wir tiefer in die Funktionsweise dieser speziellen Filterarten eintauchen. Sie erhalten eine detaillierte Erklärung, wie jeder Filtertyp verwendet wird, unterstützt durch Beispiele und Best Practices.

5.2 Das Schema

# input objects with this directive need to have exactly one field specified
directive @exclusive on INPUT_OBJECT

# specifies an optional minimum and/or maximum length for a list argument or field
directive @length(min: Int, max: Int) on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION

# specifies a variant list argument or field must have the same type for all elements
directive @sametype on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION

input FastBooleanExpression @exclusive {
  and       : [FastBooleanExpression!] @length(min: 2)
  or        : [FastBooleanExpression!] @length(min: 2)
  not       :  FastBooleanExpression
  eq        : [FastAnyExpression!] @length(min: 2 max: 2)
  gt        : [FastAnyExpression!] @length(min: 2 max: 2)
  lt        : [FastAnyExpression!] @length(min: 2 max: 2)
  ne        : [FastAnyExpression!] @length(min: 2 max: 2)
  ge        : [FastAnyExpression!] @length(min: 2 max: 2)
  le        : [FastAnyExpression!] @length(min: 2 max: 2)
  isNull    : FastAnyExpression
  isNotNull : FastAnyExpression
  field     : FastBooleanFieldsEnum #server side available boolean fields only
  value     : Boolean
}

FastAnyExpression @exclusive {
  field : FastFieldsEnum #all filterable server side available fields
  value : Any
}

input SlowBooleanExpression @exclusive {
  and       : [SlowBooleanExpression!] @length(min: 2)
  or        : [SlowBooleanExpression!] @length(min: 2)
  not       :  SlowBooleanExpression
  eq        : [SlowAnyExpression!] @length(min: 2 max: 2)
  gt        : [SlowAnyExpression!] @length(min: 2 max: 2)
  lt        : [SlowAnyExpression!] @length(min: 2 max: 2)
  ne        : [SlowAnyExpression!] @length(min: 2 max: 2)
  ge        : [SlowAnyExpression!] @length(min: 2 max: 2)
  le        : [SlowAnyExpression!] @length(min: 2 max: 2)
  like      : [SlowAnyExpression!] @length(min: 2 max: 2)
  in        : InListExpression
  isNull    : SlowAnyExpression
  isNotNull : SlowAnyExpression
  fn...     : [SlowAnyExpression] 
  field     : SlowBooleanFieldsEnum #all boolean fields including client calculated ones
  value     : Boolean
}

InListExpression {
  left: SlowAnyExpression!
  list: [Any!]! @sametype @length(min: 1)
}

SlowAnyExpression @exclusive {
  and       : [SlowBooleanExpression!] @length(min: 2)
  or        : [SlowBooleanExpression!] @length(min: 2)
  not       :  SlowBooleanExpression
  eq        : [SlowAnyExpression!] @length(min: 2 max: 2)
  gt        : [SlowAnyExpression!] @length(min: 2 max: 2)
  lt        : [SlowAnyExpression!] @length(min: 2 max: 2)
  ne        : [SlowAnyExpression!] @length(min: 2 max: 2)
  ge        : [SlowAnyExpression!] @length(min: 2 max: 2)
  le        : [SlowAnyExpression!] @length(min: 2 max: 2)
  like      : [SlowAnyExpression!] @length(min: 2 max: 2)
  in        : InListExpression
  isNull    : SlowAnyExpression
  isNotNull : SlowAnyExpression
  neg       : SlowAnyExpression
  add       : [SlowAnyExpression!] @length(min: 2)
  sub       : [SlowAnyExpression!] @length(min: 2)
  mul       : [SlowAnyExpression!] @length(min: 2)
  div       : [SlowAnyExpression!] @length(min: 2 max: 2)
  mod       : [SlowAnyExpression!] @length(min: 2 max: 2)
  fn...     : [SlowAnyExpression] 
  field     : SlowFieldsEnum #all filterable fields including calculated ones
  value     : Any
}

5.3. Verständnis von Filterausdrücken

Einleitung

Filterausdrücke sind ein leistungsfähiges Werkzeug im GraphQL-Schema der microtech Software, das es Ihnen ermöglicht, komplexe Abfragen zu erstellen, um Daten gemäß den festgelegten Bedingungen zu filtern und zu manipulieren. Die Filterausdrücke sind in Schnelle und Langsame Ausdrücke unterteilt, wodurch Sie mehr Kontrolle und Flexibilität bei der Datenabfrage haben. Mit diesen Ausdrücken können Sie logische Beziehungen, Vergleiche und arithmetische Operationen auf Datenfeldern definieren. Dadurch haben Sie eine präzise Kontrolle über die abgerufenen Daten.

Typen von Ausdrücken

Die Filterausdrücke in diesem Schema sind in vier Haupttypen unterteilt:

  • Schnelle Boolesche Ausdrücke: Spezialisiert auf schnelle Filteroperationen mit Datenserverseitig verfügbaren booleschen Feldern. Diese Ausdrücke sind für grundlegende Vergleiche und boolesche Operationen optimiert.
  • Langsame Boolesche Ausdrücke: Bieten komplexere boolesche Funktionen, einschließlich Anwendungsserverseitig berechneter boolescher Felder, des in-Operanden, der es ermöglicht, Werte innerhalb einer Liste zu überprüfen, des like-Operators für Textvergleiche, und diverse Funktionen.
  • Schnelle Allgemeine Ausdrücke: Verwendet für schnelle Filteroperationen mit allen filterbaren Anwendungsserverseitig verfügbaren Feldern. Ideal für einfache und schnelle Abfragen.
  • Langsame Allgemeine Ausdrücke: Unterstützen komplexere Rechenoperationen und filterbare Felder, einschließlich berechneter. Diese Ausdrücke bieten mehr Flexibilität bei komplexen Anforderungen.

Struktur der Ausdrücke

Filterausdrücke in diesem Schema sind in drei Haupttypen kategorisiert, basierend auf der Anzahl der Operanden, mit denen sie arbeiten:

  • Unäre Operatoren: Diese nehmen einen einzelnen Ausdruck als Eingabe. Zum Beispiel negiert der not-Operator einen booleschen Ausdruck.
  • Binäre Operatoren: Diese benötigen genau zwei Ausdrücke als Eingabe. Vergleichsoperatoren wie eq (gleich) und gt (größer als) sind binäre Operatoren.
  • Variadische Operatoren: Auch als Multi-Operanden-Operatoren bekannt, können diese zwei oder mehr Ausdrücke als Eingabe nehmen. Logische Operatoren wie and und or sind Beispiele für variadische Operatoren.

Die Ausdrücke können verschachtelt und kombiniert werden, um komplexe logische Bedingungen zu erstellen. Sie können aus Feldern, Werten oder anderen Ausdrücken bestehen, je nach spezifischem Operator.

Direktiven und Einschränkungen

Das Schema führt bestimmte Direktiven ein, die Einschränkungen hinsichtlich der Konstruktion der Ausdrücke auferlegen:

  • @exclusive: Auf Eingabeobjekte angewendet, erfordert diese Direktive, dass genau ein Feld innerhalb des Objekts angegeben sein muss. Sie stellt sicher, dass widersprüchliche Bedingungen nicht gleichzeitig angewendet werden können.
  • @length: Diese Direktive gibt eine optionale Mindest- und/oder Höchstlänge für ein Listenargument oder -feld an. Zum Beispiel wird ein binärer Operator @length(min: 2, max: 2) haben, um sicherzustellen, dass genau zwei Ausdrücke bereitgestellt werden müssen.
  • @sametype: Auf variantenreiche Listenargumente oder -felder angewendet, erfordert diese Direktive, dass alle Elemente innerhalb der Liste denselben Typ haben sollten. Dies trägt dazu bei, die Konsistenz der verarbeiteten Daten zu wahren.

Diese Direktiven spielen eine entscheidende Rolle bei der Führung der Konstruktion von gültigen und aussagekräftigen Filterausdrücken. Die Einhaltung dieser Einschränkungen stellt sicher, dass die Ausdrücke gut geformt sind und mit der beabsichtigten Logik übereinstimmen.

5.4 Boolesche Ausdrücke (Schnelle und Langsame Boolesche Ausdrücke)

Logische Operatoren (and, or)

Diese variadischen Operatoren kombinieren mehrere boolesche Ausdrücke zu einer einzigen logischen Bedingung:

  • and: Ergibt true, wenn alle booleschen Ausdrücke darin true sind.
  • or: Ergibt true, wenn mindestens einer der booleschen Ausdrücke darin true ist.
Syntax und Interpretation:
  { and: [BooleanExpression1, BooleanExpression2, ...] }
  { or:  [BooleanExpression1, BooleanExpression2, ...] }

Diese Ausdrücke werden von links nach rechts ausgewertet: BooleanExpression1 und BooleanExpression2 und ... oder BooleanExpression1 oder BooleanExpression2 oder ....

NOT-Operator (not)

Der not-Operator negiert einen booleschen Ausdruck:

Syntax:
{ not: BooleanExpression }

Vergleichsoperatoren (eq, gt, lt, ne, ge, le)

Diese binären Operatoren vergleichen Allgemeine Ausdrücke, zu denen Felder, Werte oder andere Ausdrücke gehören können:

Syntax und Interpretation:
  { Operator: [AnyExpression1, AnyExpression2] }

Interpretiert als AnyExpression1 Operator AnyExpression2.

Operatoren:
  • eq: Gleich.
  • gt: Größer als.
  • lt: Kleiner als.
  • ne: Ungleich.
  • ge: Größer oder gleich.
  • le: Kleiner oder gleich.

LIKE-Operator (like) (nur in Langsamen Booleschen Ausdrücken)

Der like-Operator ermöglicht einen Musterabgleich mit Allgemeinen Ausdrücken:

Syntax:
  { like: [AnyExpression1, AnyExpression2] }

IN-Operator (in) (nur in Langsamen Booleschen Ausdrücken)

Der in-Operator überprüft, ob der Wert des linken Ausdrucks in einer bestimmten Liste von Werten gefunden wird. Er arbeitet mit Allgemeinen Ausdrücken, zu denen Felder, Werte oder andere Ausdrücke gehören können.

Syntax und Interpretation:
  { in: {
      left: AnyExpression,
      list: [AnyValue1, AnyValue2, ...]
    }
  }

Interpretiert als left in [AnyValue1, AnyValue2, ...].

Die konstanten Werte in der Liste müssen alle denselben Typ haben.

NULL-Prüfoperatoren (isNull, isNotNull)

Diese unären Operatoren arbeiten mit Allgemeinen Ausdrücken zusammen:

  • isNull: Überprüft, ob ein Ausdruck null ist.
    Syntax:
      { isNull: AnyExpression }
    
  • isNotNull: Überprüft, ob ein Ausdruck nicht null ist.
    Syntax:
      { isNotNull: AnyExpression }
    

Feld und Wert

Innerhalb boolescher Ausdrücke können Sie auch direkt ein Feld oder einen konstanten Wert angeben:

  • field: Bezieht sich auf ein bestimmtes boolesches Feld, das als Aufzählungswerte dargestellt ist. Nur boolesche Felder können direkt in diesem Ausdruckskontext referenziert werden.
  • value: Gibt einen konstanten booleschen Wert an.
    Syntax:
      { value: true/false }
    

5.5 Allgemeine Ausdrücke (Schnelle und Langsame Allgemeine Ausdrücke)

Allgemeine Ausdrücke stellen eine flexible und leistungsfähige Möglichkeit dar, Berechnungen und Bedingungen innerhalb von GraphQL-Abfragen zu definieren. Sie sind in zwei Haupttypen unterteilt:

  • Schnelle Allgemeine Ausdrücke: Diese bieten grundlegende Funktionalität und ermöglichen nur den direkten Zugriff auf spezifische Felder und konstante Werte.
  • Langsame Allgemeine Ausdrücke: Zusätzlich zu den Funktionen der Schnellen Allgemeinen Ausdrücke bieten die Langsamen Allgemeinen Ausdrücke eine Reihe zusätzlicher Operatoren und Funktionen, die komplexere logische und arithmetische Operationen ermöglichen.

Feld und Wert

Innerhalb von Allgemeinen Ausdrücken (sowohl Schnelle als auch Langsame) können Sie direkt ein Feld oder einen konstanten Wert angeben:

  • field: Bezieht sich auf spezifische Felder, dargestellt als Aufzählungswerte.
  • value: Gibt einen konstanten Wert an.
    Syntax:
      { value: AnyValue }
    

Die folgenden Operatoren sind nur in Langsamen Allgemeinen Ausdrücken verfügbar:

Negationsoperator (neg)

Der neg-Operator negiert einen Allgemeinen Ausdruck, indem er seinen Wert effektiv mit -1 multipliziert.

Syntax:
  { neg: SlowAnyExpression }

Arithmetische Operatoren (add, sub, mul, div, mod)

Diese Operatoren führen arithmetische Berechnungen auf Allgemeinen Ausdrücken durch:

  • Variadische Operatoren (add, sub, mul): Diese Operatoren können zwei oder mehr Ausdrücke als Eingabe nehmen.

    Syntax und Interpretation:
      { add: [SlowAnyExpression1, SlowAnyExpression2, ...] }
      { sub: [SlowAnyExpression1, SlowAnyExpression2, ...] }
      { mul: [SlowAnyExpression1, SlowAnyExpression2, ...] }
    

    Interpretiert als SlowAnyExpression1 + SlowAnyExpression2 + ..., SlowAnyExpression1 - SlowAnyExpression2 - ..., SlowAnyExpression1 * SlowAnyExpression2 * ....

  • Binäre Operatoren (div, mod): Diese Operatoren benötigen genau zwei Ausdrücke als Eingabe.

    • div: Teilt den ersten Ausdruck durch den zweiten Ausdruck.
    • mod: Berechnet den Rest der Division des ersten Ausdrucks durch den zweiten Ausdruck.
    Syntax und Interpretation:
      { div: [SlowAnyExpression1, SlowAnyExpression2] }
      { mod: [SlowAnyExpression1, SlowAnyExpression2] }
    

    Interpretiert als SlowAnyExpression1 / SlowAnyExpression2, SlowAnyExpression1 mod SlowAnyExpression2.

Logische Operatoren (and, or, not)

Diese Operatoren kombinieren mehrere Boolesche Ausdrücke zu einer einzigen logischen Bedingung im Kontext von Allgemeinen Ausdrücken:

Syntax und Interpretation:
  { and: [SlowBooleanExpression1, SlowBooleanExpression2, ...] }
  { or:  [SlowBooleanExpression1, SlowBooleanExpression2, ...] }
  { not: SlowBooleanExpression }

Die Syntax ist konsistent mit den logischen Operatoren innerhalb der booleschen Ausdrücke, interpretiert als SlowBooleanExpression1 und SlowBooleanExpression2 und ..., SlowBooleanExpression1 oder SlowBooleanExpression2 oder ..., nicht SlowBooleanExpression.

Funktionen

Funktionen bieten eine erweiterte Möglichkeit, Berechnungen und Transformationen innerhalb der Abfrage durchzuführen. Alle Funktionen beginnen mit dem Präfix fn und sind nur in Langsamen Ausdrücken verwendbar. Die Liste der verfügbaren Funktionen entspricht denen, die bereits in den Filterbedingungen innerhalb der microtech Software dokumentiert sind.

Syntax und Interpretation
  { fnFunktionsname: [SlowAnyExpression1, SlowAnyExpression2, ...] }

Funktionen nehmen eine Liste von Langsamen Allgemeinen Ausdrücken als Parameter. Die Anzahl und Art der Parameter hängen von der spezifischen Funktion ab.

Beispiel: fnDay Funktion

Die fnDay Funktion extrahiert den Tag aus einem Datumsfeld. Hier ist ein Beispiel, das die fnDay Funktion verwendet, um den Tag aus dem gspDat Feld zu extrahieren:

  {
    fnDay: [{ field: gspDat }]
  }

Dies würde den Tag des Datums im Feld gspDat zurückgeben.

Weitere Informationen

Für detaillierte Informationen zu den einzelnen Funktionen, einschließlich der Anzahl und Art der erforderlichen Parameter, verweisen wir auf die entsprechende Anwendungs-Dokumentation.

5.6 Komplexe Ausdrücke schreiben

Verschachtelung und Kombination von Ausdrücken

Komplexe Filterausdrücke ermöglichen komplexe logische und arithmetische Bedingungen durch das Verschachteln und Kombinieren von Booleschen und Allgemeinen Ausdrücken.

  • Verschachtelung: Betten Sie Ausdrücke ineinander ein, um mehrstufige logische und arithmetische Operationen zu erstellen.

    Beispiel:
      { or: [
          { and: [BooleanExpression1, BooleanExpression2] }
          { not: BooleanExpression3 }
        ]
      }
    

    Interpretiert als (BooleanExpression1 und BooleanExpression2) oder (nicht BooleanExpression3).

  • Kombination: Nutzen Sie verschiedene Ausdrücke zusammen, um differenzierte Bedingungen zu erstellen.

    Beispiel (nur in Langsamen Ausdrücken möglich):
      { eq: [
          { add: [SlowAnyExpression1, SlowAnyExpression2] }
          { mul: [SlowAnyExpression3, SlowAnyExpression4] }
        ]
      }
    

    Interpretiert als (SlowAnyExpression1 + SlowAnyExpression2) gleich (SlowAnyExpression3 \times SlowAnyExpression4).

  • Überlegungen zum Schreiben komplexer Ausdrücke:

    • Reihenfolge der Auswertung: Beachten Sie, dass die Reihenfolge der Auswertung immer von links nach rechts, innen nach außen erfolgt. Diese Reihenfolge ist entscheidend für den korrekten logischen Ablauf innerhalb komplexer Ausdrücke.
    • Einhaltung von Direktiven: Halten Sie sich an die von den Direktiven wie @exclusive, @length und @sametype auferlegten Beschränkungen, um wohlgeformte Ausdrücke sicherzustellen.
    • Verständnis für variadische Operatoren: Variadische Operatoren, wie and und or, erlauben zwei oder mehr Ausdrücke als Eingabe und werden von links nach rechts ausgewertet. Zum Beispiel wird and: [Expression1, Expression2, Expression3] als (Expression1 und Expression2) und Expression3 ausgewertet. Dieses sequenzielle Auswerten ist beim Erstellen von Ausdrücken mit mehreren Operanden unerlässlich.

5.7 Optimierung und praktische Überlegungen

Filterausdrücke können kompliziert und komplex werden und erfordern eine durchdachte Gestaltung und Optimierung. Dieser Abschnitt skizziert wichtige Überlegungen und bewährte Verfahren, um eine effiziente und klare Formulierung von Ausdrücken sicherzustellen.

Optimierung verschachtelter Ausdrücke

Tief verschachtelte Ausdrücke können komplex und schwer zu lesen oder interpretieren sein. Berücksichtigen Sie dabei Folgendes:

  • Verschachtelte logische Operatoren abflachen: Wenn möglich, sollten logische Operatoren desselben Typs (z. B. mehrere verschachtelte and- oder or-Operatoren) abgeflacht werden, um die Klarheit zu erhöhen.
  • Spezifische Vergleichsoperatoren verwenden: Wählen Sie spezifische Vergleichsoperatoren wie ne (ungleich) anstelle der Kombination von not mit eq. Ebenso verwenden Sie gt, lt, ge und le nach Bedarf. Dieser Ansatz verbessert Lesbarkeit und Prägnanz.
  • Schnelle Ausdrücke bevorzugen, wenn möglich: Verwenden Sie Schnelle Ausdrücke anstelle von Langsamen Ausdrücken, wenn die benötigte Funktionalität verfügbar ist. Dies trägt zur Verbesserung der Abfrageleistung bei.
  • Implizite Klammern durch Verschachtelung: Obwohl keine expliziten Klammern verwendet werden, impliziert die Struktur verschachtelter Ausdrücke ihre Anwesenheit. Das Verständnis der impliziten Reihenfolge der Auswertung, die durch Verschachtelung gebildet wird, kann bei der Erstellung klarer und prägnanter Ausdrücke helfen.

Umgang mit großen Listen in variadischen Operationen

Bei der Arbeit mit großen Listen in variadischen Operationen wie and, or, add, sub und mul (in Langsamen Ausdrücken) können die folgenden Praktiken hilfreich sein:

  • Leistungsüberlegungen: Umfangreiche Listen können die Abfrageleistung beeinträchtigen. Achten Sie auf die Komplexität des Ausdrucks und testen Sie mit realistischen Datengrößen, um die Effizienz sicherzustellen.
  • Lesbarkeit beibehalten: Strukturieren Sie große Listen in handhabbare Teile und gruppieren Sie zusammengehörende Ausdrücke, um die Lesbarkeit zu verbessern.

Überlegungen zur NULL-Handhabung

Das Verständnis und die Handhabung von NULL-Werten sind ein wesentlicher Aspekt des Filterausdrucks-Designs:

  • Explizite NULL-Prüfungen: Verwenden Sie isNull und isNotNull für spezifische NULL-Prüfungen, da sie für diesen Zweck konzipiert sind.

Tipps zum Testen von Ausdrücken

Angesichts der eingeschränkten Möglichkeit, Ausdrücke zu debuggen, können die folgenden Strategien wertvoll sein:

  • Iterative Entwicklung: Beginnen Sie mit einfachen Ausdrücken und bauen Sie schrittweise Komplexität auf. Testen Sie in jedem Stadium, um zu überprüfen, ob der Ausdruck wie beabsichtigt funktioniert.
  • Repräsentative Daten verwenden: Verwenden Sie Daten, die tatsächlichen Anwendungsfällen ähneln, um eine genaue Beurteilung dessen zu erhalten, wie der Ausdruck in der Produktion funktionieren wird.
  • Komplexe Ausdrücke dokumentieren: Führen Sie klare Dokumentationen für komplexe Ausdrücke durch, um zukünftiges Verständnis und Wartung zu erleichtern.

Durch die Berücksichtigung dieser Überlegungen können Sie Filterausdrücke erstellen, die nicht nur funktional genau, sondern auch optimiert, klar und wartbar sind. Diese Praktiken ergänzen die zuvor detaillierte Syntax und Semantik und bieten einen umfassenden Blick auf das Ausdrucksdesign innerhalb des GraphQL-Schemas.

5.8 Beispiele

In diesem Kapitel werden konkrete Beispiele für die Verwendung von Filterausdrücken im GraphQL-Schema vorgestellt. Die Beispiele demonstrieren die Anwendung der definierten Operatoren und Direktiven und zeigen, wie komplexe Bedingungen formuliert werden können.

Beispiel 1: Kombination von logischen und Vergleichsoperatoren (Schnelle Ausdrücke)

Ziel

Filterung von Datensätzen, bei denen das Feld fldLagBestArt entweder den Wert 1 oder 7 hat, und das Feld fldAuftrNr nicht null und nicht leer ist.

Code

{
  and: [
    {
      or: [
        { eq: [ { field: fldLagBestArt } { value: 1 } ] }
        { eq: [ { field: fldLagBestArt } { value: 7 } ] }
      ]
    }
    {
      not: {
        or: [
          { isNull: { field: fldAuftrNr } }
          { eq: [ { field: fldAuftrNr } { value: "" } ] }
        ]
      }
    }
  ]
}

Analyse

  • Der äußere and-Operator verbindet zwei Bedingungen.
  • Der innere or-Operator überprüft, ob das Feld fldLagBestArt entweder den Wert 1 oder 7 hat.
  • Der not-Operator negiert die folgende Bedingung, die überprüft, ob das Feld fldAuftrNr null ist oder einen leeren String enthält.

Dieses Beispiel zeigt, wie verschiedene logische und Vergleichsoperatoren kombiniert werden können, um eine komplexe Bedingung zu formulieren.

Beispiel 1.5: Optimierung mit dem IN-Operator (Langsame Ausdrücke)

Ziel

Optimierung des vorherigen Beispiels durch Verwendung des in-Operators, um die Überprüfung zu vereinfachen, ob das Feld fldLagBestArt einen der Werte 1 oder 7 enthält.

Code

{
  and: [
    {
      in: {
        left: { field: fldLagBestArt }
        list: [ { value: 1 } { value: 7 } ]
      }
    }
    {
      not: {
        or: [
          { isNull: { field: fldAuftrNr } }
          { eq: [ { field: fldAuftrNr } { value: "" } ] }
        ]
      }
    }
  ]
}

Analyse

  • Der in-Operator vereinfacht den Ausdruck, indem er die Notwendigkeit der Verwendung mehrerer eq-Operationen im or-Operator eliminiert.
  • Stattdessen wird eine Liste von Werten bereitgestellt, und der in-Operator prüft, ob das Feld fldLagBestArt einen der Werte in der Liste enthält.

Hinweis: Diese Optimierung sollte nur dann in Betracht gezogen werden, wenn der Ausdruck aus anderen Gründen bereits ein Langsamer Ausdruck ist. Der Vorteil des in-Operators über 2x eq ist viel kleiner als der Nachteil von Langsam vs. Schnell.

Beispiel 2: Verschachtelung von logischen Operatoren und direktes Negieren eines Booleschen Feldes (Schnelle Ausdrücke)

Ziel

Filterung von Datensätzen, bei denen das Feld fldErledigtKz entweder null ist oder false ist, und das Feld fldArt nicht null und nicht gleich 0 ist.

Code

{
  and: [
    {
      or: [
        { isNull: { field: fldErledigtKz } }
        { not: { field: fldErledigtKz } }
      ]
    }
    {
      not: {
        or: [
          { isNull: { field: fldArt } }
          { eq: [ { field: fldArt } { value: 0 } ] }
        ]
      }
    }
  ]
}

Analyse

  • Der äußere and-Operator verbindet zwei Hauptbedingungen.
  • Die erste Hauptbedingung verwendet den or-Operator, um zu überprüfen, ob das Feld fldErledigtKz entweder null ist oder false ist. Die Negation wird direkt auf das Feld fldErledigtKz angewendet, ohne es mit einer True/False-Konstante zu vergleichen.
  • Die zweite Hauptbedingung verwendet den not-Operator, um die folgende Bedingung zu negieren, die überprüft, ob das Feld fldArt null ist oder den Wert 0 hat.

Dieses Beispiel hebt hervor, wie die direkte Negation eines booleschen Feldes innerhalb eines Filterausdrucks verwendet werden kann. Es zeigt die Feinheiten und Flexibilität, die bei der Formulierung von Filterbedingungen in diesem Schema möglich sind.

6. Arbeiten mit Datensätzen

Nachdem wir bis jetzt die Feinheiten der Datensatzauswahl behandelt haben, wenden wir uns nun dem Zugriff auf die Datenbankfelder eines Datensatzes zu. Dieser Abschnitt konzentriert sich auf die spezifischen Aspekte des microtech GraphQL-Schemas und setzt ein grundlegendes Verständnis von GraphQL voraus.

6.1 Zusätzliche skalare Typen im microtech GraphQL-Schema

Im microtech GraphQL-Schema gibt es zusätzlich zu den Standard-GraphQL-Typen auch spezielle skalare Typen. Diese werden sowohl für die Eingabe als auch für die Ausgabe verwendet.

  • Void: Akzeptiert nur null als Eingabe und gibt immer null zurück.
  • BigInt: Unterstützt positive oder negative Ganzzahlen mit bis zu 64 Ziffern.
  • Variant: Akzeptiert jedes grammatikalisch gültige GraphQL-Skalar, Liste oder Eingabeobjekt, das als Windows Variant-Typ dargestellt werden kann. Es gibt JSON zurück, das jede gültige Eingabe darstellen kann.
  • Any: Akzeptiert jedes grammatikalisch gültige GraphQL-Skalar, Liste oder Eingabeobjekt. Gibt JSON zurück, das jede gültige Eingabe darstellen kann.
  • Guid: Ein String, der eine GUID im Standardformat enthält, beispielsweise: {A63CBE46-D82C-4347-9AA7-7B6BDBC3FA72}.
  • LocalDate: Ein lokaler Datums-String im YYYY-MM-DD Format.
  • LocalTime: Ein lokaler Zeit-String im 24-Stunden-Format HH:mm[:ss[.SSS]].
  • LocalDateTime: Ein lokaler Datums-Zeit-String im YYYY-MM-DDTHH:mm[:ss[.SSS]] Format.
  • RtfString: Ein String, der RTF enthält.
  • Html: Ein String, der HTML enthält.
  • Url: Ein String, der eine URL enthält.
  • Char: Ein String, der ein einzelnes Zeichen enthält.

6.1.1 Kontextabhängigkeit von Variant und Any

Bei der Verwendung der Typen Variant und Any als Eingabetypen hängt die Gültigkeit der Eingabe vom spezifischen Kontext der Abfrage ab. Ähnlich ist bei der Verwendung dieser Typen als Ausgabetypen die erwartete Ausgabe kontextabhängig.

6.2 Datenfelder

In den Datentabellen des microtech GraphQL-Schemas haben alle Datenbankfelder den Rückgabetyp Any. Dies bietet eine hohe Flexibilität bei der Abfrage von Daten, da der gewünschte Rückgabetyp über den as-Parameter spezifiziert werden kann.

6.2.1 Verwendung des as-Parameters

Der as-Parameter nimmt einen Enum-Wert an, der den gewünschten Formatierungstyp des Feldes bestimmt. Jeder unterstützte Feldtyp hat verschiedene Enums für mögliche Ausgabeformate.

Falls der as-Parameter nicht explizit angegeben wird, gilt ein datenfeldtypabhängiger Standardwert.

Beispiel:

query {
  products {
    artNr(as: STRING)
  }
}

6.2.2 Unterstützte Ausgabeformate

Mögliche Ausgabeformate, die über den as-Parameter gewählt werden können, stammen aus den folgenden Enum-Werten:

enum FieldAsEnum {
  IS_ACCESS_ALLOWED
  IS_NULL
  ALWAYS_NULL
  TEXT
  DISPLAY_TEXT
  BOOLEAN
  FLOAT
  LOCALDATE
  LOCALTIME
  LOCALDATETIME
  INT
  BIGINT
  CHAR
  STRING
  HEXSTRING
  BASE64
  RTF_STRING
  HTML
  GUID
  URL
}

IS_ACCESS_ALLOWED

Ein Boolean, der angibt, ob der Zugriff auf dieses Feld möglich ist. Zugriff kann aufgrund von Berechtigungen oder anderen Faktoren für bestimmte Datensätze oder Datenfelder eingeschränkt sein.

Wenn IS_ACCESS_ALLOWED den Wert false zurückliefert, werden alle anderen Ausgabeformate null zurückliefern, außer DISPLAY_TEXT, welches möglicherweise einen Hinweistext zur fehlenden Zugriffsberechtigung zurückgibt.

IS_NULL

Ein Boolean, der angibt, ob das Datenfeld null ist (true) oder nicht (false). Falls der Zugriff nicht erlaubt ist (IS_ACCESS_ALLOWED = false), kann der Rückgabewert auch null sein.

Falls IS_NULL wahr ist (true), werden alle folgenden Ausgabeformate stets null zurückgeben.

ALWAYS_NULL

Liefert immer null.

TEXT

Eine Textrepräsentation des Datenfeldes. Die genaue Formatierung hängt vom zugrunde liegenden Datenfeldtyp, Parametern, dem aktuellen Benutzer usw. ab. Ein Wert, der als TEXT ausgelesen wurde, kann normalerweise in derselben Anmeldesitzung auch wieder über text geschrieben werden.

DISPLAY_TEXT

Eine für die Anzeige gedachte Textrepräsentation des Datenfeldes, wie sie auch in der Tabellenansicht der microtech Software dargestellt wird. Dies kann weitere Informationen als nur TEXT enthalten, und der zurückgegebene Wert kann eventuell nicht als text zurückgeschrieben werden.

BOOLEAN

Ein boolescher Wert (true/false).

FLOAT

Eine Fließkommazahl im standardmäßigen JSON-Number-Format.

LOCALDATE

Ein lokaler Datums-String im YYYY-MM-DD-Format. Die Zeitzone hängt vom Server und Mandanten ab.

LOCALTIME

Ein lokaler Zeit-String im 24-Stunden-Format HH:mm[:ss[.SSS]]. Die Zeitzone hängt vom Server und Mandanten ab.

LOCALDATETIME

Ein lokaler Datums-Zeit-String im YYYY-MM-DDTHH:mm[:ss[.SSS]]-Format. Die Zeitzone hängt vom Server und Mandanten ab.

INT

Ein 32-Bit-Signed-Integer im standardmäßigen JSON-Number-Format.

BIGINT

Ein Integer, entweder im standardmäßigen JSON-Number-Format (für Werte zwischen -9007199254740991 und 9007199254740991) oder als JSON-String, der die Zahl enthält.

CHAR

Ein JSON-String mit genau einem Zeichen.

STRING

Ein JSON-String. Für String-Datenfelder ist dies der exakte Wert, der im Datenfeld gespeichert ist. Für andere Datenfeldtypen ist STRING eine Formatierung, die nicht von externen Faktoren abhängt (im Gegensatz zu TEXT).

HEXSTRING

Ein JSON-String, der die Daten des Feldes als Folge von Hexadezimalzahlen kodiert. Jeweils zwei Zeichen entsprechen einem Byte.

BASE64

Ein JSON-String, der die Daten des Feldes als Base64 kodiert.

RTF_STRING

Ein JSON-String mit Text und Formatierung im Rich-Text-Format.

HTML

Ein JSON-String mit Text und Formatierung im HTML-Format.

GUID

Ein JSON-String mit einer GUID im Standardformat (z. B. "{5A4F8D06-7021-4668-A321-B6AB5314D2A6}").

URL

Eine URL, mit der der Inhalt des Datenfeldes in einer weiteren HTTP-Anfrage später angefordert werden kann. Die Gültigkeit der URL ist nur während der aktuellen Anmeldesitzung und nur solange garantiert, bis die Daten geändert werden.

6.2.3 Kontextspezifische Bedeutung und Verfügbarkeit

Es ist wichtig zu beachten, dass die Bedeutung und die Verfügbarkeit der Werte des as-Parameters kontextabhängig sind. Unterschiedliche Datenfeldtypen erlauben unterschiedliche Untermengen der möglichen Enum-Werte.

Die exakten möglichen Werte sind dem Schema zu entnehmen.

Beispiel:

enum GetBooleanFieldAsEnum {
  IS_ACCESS_ALLOWED
  IS_NULL
  TEXT
  DISPLAY_TEXT
  BOOLEAN
  INT
  STRING
}

enum GetRtfBlobFieldAsEnum {
  IS_ACCESS_ALLOWED
  IS_NULL
  TEXT
  DISPLAY_TEXT
  RTF_STRING
  HTML
}

6.3 Datenfelder, die auf andere Datensätze verweisen

Einige Datenfelder können durch ihren Wert auf einen anderen Datensatz verweisen, der sich auch in anderen Datentabellen befinden kann. In solchen Fällen existiert ein zusätzliches GraphQL-Feld. Der Name dieses zusätzlichen Feldes setzt sich aus dem Namen des ursprünglichen Datenfeldes plus dem Wort Referenced und dem Namen eines Datensatzes in der Ziel-Datentabelle zusammen.

Diese speziellen GraphQL-Felder haben keine Parameter, und der Rückgabewert ist entweder ein Datensatz (ein tabellenspezifisches Row-Objekt) oder null, falls das Datenfeld null ist oder der Wert nicht auf einen gültigen Datensatz verweist.

Beispiel:

query {
  products {
    artNr
    wgrNr
    wgrNrReferencedProductGroup {
      wgrNr
      bez      
    }
    einh
    einhReferencedUnit {
      kuBez
      bez
      mgeFak
      ePreisFak
    }
  }
}

6.4 Datenfelder, die Datentabellen enthalten

Einige Datenfelder können komplette Datentabellen umfassen. In solchen Fällen existiert ein zusätzliches GraphQL-Feld, dessen Bezeichnung sich aus dem Namen des ursprünglichen Datenfeldes, ergänzt um das Wort Contained und dem Namen der enthaltenen Datentabelle, zusammensetzt.

Diese besonderen GraphQL-Felder repräsentieren die vollständige, enthaltene Datentabelle und akzeptieren dieselben Parameter für Sortierung, Dateneinschränkung und Filterung, wie sie in den Kapiteln 2 bis 5 erläutert wurden. Der Rückgabewert ist entweder eine Liste von Datensätzen (eine Liste von tabellenspezifischen Row-Objekten) oder null. Letzteres ist der Fall, wenn das Datenfeld null ist oder keine Datensätze den festgelegten Auswahlkriterien entsprechen.

Beispiel:

query {
  products {
    artNr
    ums # nur IS_ACCESS_ALLOWED und IS_NULL
    umsContainedUms12UmsRohStGew {
      jahr
      umsJan
      umsFeb
      # ...
      gesRoh
      gesMgeUms
      gesGewUms
    }
  }
  
  client {
    mandNr
    bnkVbContainedBankAccounts (
      allBetween:{
        byBlzKtoNr: { }         
      }
    ) {
      nr
      zahlArt
      bnkVb
      blz     
    }
  }
}

6.5 Betragsgruppen

Betragsgruppen sind spezielle Datenfelder, die eine detaillierte Aufschlüsselung von Preisen in Brutto-, Netto- und Steuerbeträge darstellen und zudem eine weitere Aufgliederung nach individuellen Steuerschlüsseln ermöglichen.

Speicherung und Berechnung

Die Betragsgruppen können Preise entweder in Brutto- oder in Nettoform speichern. Abhängig von dieser Auswahl wird der jeweils andere Preis (Brutto oder Netto) sowie die zugehörige Steuer automatisch berechnet.

Erweiterte GraphQL-Felder

Für jedes Betragsgruppen-Datenfeld steht ein ergänzendes GraphQL-Feld zur Verfügung. Der Name dieses zusätzlichen Feldes setzt sich aus dem Namen des ursprünglichen Datenfeldes und dem Suffix Composition zusammen. Dies ermöglicht einen strukturierten Zugriff auf die komplexen Informationen innerhalb der Betragsgruppe.

Zusätzlich bietet dieses erweiterte GraphQL-Feld die Möglichkeit, für jeden verwendeten Steuerschlüssel verschachtelten Zugriff auf den entsprechenden Umsatzsteuerdatensatz zu erhalten.

Beispiel:

query {
  transactionPositions {
    ePr(as: TEXT)
    ePrComposition {
      totalGrossAmount
      totalNetAmount
      totalTaxAmount
      isCalculatedAndStoredAsGross
      allEntries {
        taxCode
        grossAmount
        netAmount
        taxAmount
        valueAddedTaxType {
          stSchl
          stArt
          bez
        }
      }
    }    
  }
}

In dieser Abfrage zeigt ePrComposition die erweiterten Informationen an, die in der Betragsgruppe ePr gespeichert sind. Sie erhalten Zugriff auf die Gesamtbeträge (Brutto, Netto, Steuer), den Berechnungsmodus und die Aufgliederung nach einzelnen Steuerschlüssel mit ihren zugehörigen Umsatzsteuerdatensätzen.

7. Verknüpfte Datentabellen

Viele Datensätze stehen in Beziehung zu anderen Datentabellen. Diese Verknüpfungen werden im GraphQL-Schema der microtech Software durch spezielle GraphQL-Felder repräsentiert. Der Name eines solchen Feldes setzt sich aus dem Präfix related und dem Namen der verknüpften Datentabelle zusammen. Diese Felder erfordern bestimmte Parameter und liefern eine Liste von Datensätzen (tabellenspezifische Row-Objekte) zurück.

7.1 Sortierung

Der erste Parameter definiert die gewünschte Sortierung der verknüpften Datentabelle. Hierfür gibt es einen oder mehrere mögliche Parameter, deren Namen sich aus dem Präfix by und dem Namen einer Sortierfolge der Tabelle zusammensetzt. Nur einer dieser Parameter darf gleichzeitig verwendet werden. Zudem stehen nur Sortierfolgen zur Verfügung die einen Bezug zu Feldern im ursprünglichen Datensatz haben. Dieser Parameter ist zwingend erforderlich.

Beispiel:

query {
  addresses {
    adrNr  
    relatedOpenItems (      
      byAdrNr: {
        # ...
      }
    ) {      
      buchDat
      belegNr
      rBet
    }
  }  
}

7.2 Verknüpfungsfelder

Innerhalb des Sortierungsparameters gibt es weitere mögliche Parameter, die festlegen, welche Felder des Ursprungsdatensatzes für die Sortierungsfelder der verknüpften Datentabelle verwendet werden. Der Name dieser Parameter setzt sich aus dem Präfix using und dem Namen eines oder mehrerer Datenfelder im Ursprungsdatensatz zusammen. Auch dieser Parameter ist zwingend erforderlich.

Beispiel:

query {
  addresses {
    adrNr  
    relatedOpenItems (      
      byAdrNr: {
        usingAdrNr: {
        }        
      }
    ) {      
      buchDat
      belegNr
      rBet
    }
  }  
}

7.3 Weitere Einschränkungen über Sortierungsfelder

Falls in der gewählten Sortierfolge der verknüpften Datentabelle weitere Sortierungsfelder enthalten sind, die noch nicht durch den using-Parameter vorgegeben sind, können diese für weitere Einschränkungen der zurückgelieferten Datensätze genutzt werden. Hierfür kann innerhalb des using-Parameters das erste zusätzliche Sortierfeld mittels dessen Namen spezifiziert werden. Weitere Felder können ebenfalls, wie ab "3.5 Angabe von Feldwerten in der Sortierfolge" beschrieben, verschachtelt angegeben werden. Diese Angaben sind optional.

Beispiel:

query {
  addresses {
    adrNr  
    relatedOpenItems (      
      byAdrNr: {
        usingAdrNr: {
          buchDat: {localdate: "2023-10-30"
            belegNr: {string: "RE100005"}
          }
        }        
      }
    ) {      
      buchDat
      belegNr
      rBet
    }
  }  
}

7.4 Filter

Wie bei direkten Zugriffen auf Datentabellen können auch bei verknüpften Datentabellen Filter zur weiteren Einschränkung der Auswahl angewendet werden. Dazu stehen die fastFilter- und slowFilter-Parameter auf der selben Ebene wie die by-Parameter, sowie innerhalb des using-Parameters der keyFilter-Parameter zur Verfügung. Die Verwendung dieser Filter ist optional.

Beispiel:

query {
  addresses {
    adrNr  
    relatedOpenItems (      
      byAdrNr: {
        usingAdrNr: {
          keyFilter: { ... }
        }        
      }
      fastFilter: { ... }
      slowFilter: { ... }
    ) {      
      buchDat
      belegNr
      rBet
    }
  }  
}

8. Paginierung durch Relay Connection Spezifikation

8.1 Einleitung

Die Relay Connection Spezifikation ist ein essentieller Bestandteil moderner GraphQL-Implementierungen, insbesondere wenn es um die Verarbeitung und Paginierung großer Datenmengen geht. Diese Spezifikation definiert ein standardisiertes Muster für die Struktur von GraphQL-Abfragen, um den Umgang mit Listen von Daten zu optimieren.

8.2 Verständnis der Relay Connection Spezifikation

Bevor wir uns der Implementierung in der microtech Software zuwenden, ist es wichtig, die Schlüsselkonzepte der Relay Connection Spezifikation zu verstehen:

  • Connection und Edge: Ein Connection-Objekt enthält eine Sammlung von Edges. Jedes Edge repräsentiert eine Beziehung zwischen Knoten (Nodes) und enthält neben dem Knoten selbst auch einen Cursor für die Paginierung.
  • Node: Der Node in einem Edge enthält die tatsächlichen Daten des Datensatzes.
  • PageInfo: Dieses Objekt bietet nützliche Metadaten für die Paginierung, wie den endCursor und die Angabe, ob weitere Seiten verfügbar sind (hasNextPage).

8.3 Implementierung in der microtech Software

In der microtech Software wurde die Relay Connection Spezifikation für eine Untermenge der direkt im query-Objekt abfragbaren Datentabellen implementiert.

Zu den vorhandenen GraphQL-Datentabellen-Feldern auf Ebene des query-Objektes, welche direkt Listen von Datensätzen (Row-Objekten) zurückgeben (beispielsweise products oder addresses), wurden spezielle Connection-Felder hinzugefügt (beispielsweise productsConnection oder addressesConnection), welche die selben Parameter zur Datenauswahl wie die einfachen Datentabellen-Felder akzeptieren, und zusätzlich die in der Relay Connection Spezifikation vorgeschriebenen Parameter enthalten. Connection-Felder sind auf den Zugriffsmodus allBetween beschränkt.

Diese Connection-Felder geben statt einer direkten Liste von Datensätzen ein datentabellenspezifisches Connection-Objekt zurück.

Beispiel:

query {
  productsConnection(first: 10, after: "...cursor...") {
    edges {
      cursor
      node {
        ... (Felder des Artikel-Datensatzes)
      }
    }
    pageInfo {
      hasNextPage
      endCursor
      edgeCount
    }
  }
}

8.4 Zusätzliche Parameter für Connection-Felder

  • first: Optionaler Parameter wie viele Datensätze maximal abgerufen werden sollen. Dies muss 1 oder mehr sein. Wenn nicht angegeben werden alle Datensätze zurückgegeben.
  • after: Optionaler Parameter um den Startpunkt festzulegen ab dem Datensätze zurück gegeben werden. Der Wert muss ein String sein welcher zuvor aus dem cursor-, startCursor-, oder endCursor-Feldern eines Connection-Objekts für die selbe Datentabelle und mit der selben Sortierung ausgelesen wurde. Der erste zurückgegebene Datensatz ist dann der erste nach der Position die durch after angegeben wird. Der Datensatz von dem der cursor stammt kann zwischenzeitlich geloescht oder verändert (und damit evtl. an anderer Stelle in der Sortierfolge) sein. Die Startposition orientiert sich dann weitherhin an der Position in der Sortierfolge wo der Datensatz bei der Rückgabe des cursor war.

8.5 Felder des Connection-Objekts

  • edges: Eine Liste von datentabellenspezifischen Edge-Objekten.
  • pageInfo: Ein PageInfo-Objekte welches gesamtheitliche Informationen zu den aktullen edges liefert.

In der microtech Software sollte das pageInfo-Feld immer nach den edges abgefragt werden. Diese Reihenfolge ermöglicht es, dass die Informationen in pageInfo effizienter berechnet werden, da sie durch das Durchlaufen der edges bereits intern verfügbar sind. Wird pageInfo vor den edges abgefragt, führt dies zwar zu korrekten Ergebnissen, erfordert aber intern ein doppeltes Durchlaufen der Datensätze.

8.6 Felder des Edge-Objektes

  • cursor: Ein String welcher die Position dieses Edge-Objektes in der aktuellen Sortierfolge beschreibt. Dieser kann bei einem späteren Aufruf an den after-Parameter gegeben werden.
  • node: Der Datensatz für dieses Edge-Objekt. Das zurückgegebene Row-Objekt ist identisch zu denen welche in der Liste nicht-Connection-Felder enthalten sind.

8.7 Felder des PageInfo-Objektes

  • hasPreviousPage: Dieses in der Connection Spezifikation verlangte Feld gibt immer false zurück.
  • hasNextPage: Gibt true zurück falls es noch weitere Datensätze gibt deren Rückgabe durch die Beschränkung des first-Parameters verhindert wurde.
  • startCursor: Ein String welcher die Position des ersten zurückgegebenen Edge-Objektes beschreibt. Dieser kann bei einem späteren Aufruf an den after-Parameter gegeben werden.
  • endCursor: Ein String welcher die Position des letzten zurückgegebenen Edge-Objektes beschreibt. Dieser kann bei einem späteren Aufruf an den after-Parameter gegeben werden.
  • edgeCount: Ein Integer, der die Anzahl der zurückgegebenen Edge-Objekte enthält.

Die Felder des PageInfo-Objektes sind auch verfügbar wenn nur auf dieses zugegriffen wird und die edges nicht abgefragt werden.

8.8 Vorteile und Anwendungsbereiche

Die Relay Connection Spezifikation bietet eine Reihe von Vorteilen:

  • Effiziente Paginierung: Ermöglicht das Laden großer Datensätze in handhabbaren Teilen.
  • Verbesserte Performance: Reduziert die Belastung auf dem Server durch gezielte Datenabfragen.
  • Flexibilität und Konsistenz: Bietet ein einheitliches Muster für den Zugriff auf Listen von Daten.
  • No labels