Lade...
 

Query - Die Syntax eines Querystrings

Die Syntax eines Querystrings:

Query_Expression ::=
Boolean_Expression | Compare_Expression | Quoted_Expression | Bracket_Expression | Exist_Expression | Nested_Expression | Boolean_Constant
Boolean_Expression ::= (Query_Epression '|' Query_Expression) | (Query_Expression '&' Query_Expression)
Compare_Expression ::= Statement Operator Statement
Quoted_Expression ::=
# [Ascii(32)...Ascii(34), Ascii(36)...Ascii(127)]* #
Nested_Expression ::=
[!] Path ([: Query_Expression :] | [% Query_Expression %])
Exist_Expression ::=
[!]?Path
Bracket_Expression ::=
[!] ( Query_Expression )
Statement ::= Path | Placeholder | Constant
Operator ::= = | >= | <= | < | > | != | @ | !@ | <> | >< | ~
Path ::= Identifier.Path | Identifier->Path | Identifier
Placeholder ::= %{ s | h | i | N | V | D | T | P | F | S | O | STRING | HASH_STRING | INTEGER | Classname}
Constant ::= Boolean_Constant | String_Constant | Numeric_Constant | Classname | Null_Constant
Identifier ::= Member_Identifier | Method_Identifier
Member_Identifier ::=
[&][::] a..Z [a..Z | 0..9]*
Method_Identifier ::=
a..Z [a..Z | 0..9]* ()
Classname ::= CX_NUMERIC | CX_VALUE | CX_DATE | CX_SPAN_DATE . . .
Boolean_Constant ::= true | false
Numeric_Constant ::= Integer_Constant | Real_Constant | Value_Constant
Null_Constant ::= NULL
Integer_Constant ::= 0..9 0..9*
Real_Constant ::= Integer_Constant . Integer_Constant
Value_Constant ::= (Real_Constant | Integer_Constant) Unit
Unit - eine der im ClassiX®-System definierten Einheiten -

Semantik:

  • Für variable Werte, die vom Stack genommen werden, müssen im Querystring Platzhalter erscheinen:
      oder für Beispiel
    %s
    %STRING einen String
    "000001" "uniqueID = %s" FindFirst(CX_CUSTOMER)
    %h
    %HASH_STRING einen Hash-String  
    %i
    %INTEGER einen Integer-Wert
    1 "settings= %i" FindFirst(CX_CUSTOMER)
    %N %CX_NUMERIC CX_NUMERIC  
    %V %CX_VALUE CX_VALUE  
    %D %CX_DATE CX_DATE  
    %T %CX_TIME CX_TIME  
    %P %CX_PERCENT CX_PERCENT  
    %F %CX_FRACTION CX_FRACTION  
    %S %CX_SPAN_DATE CX_SPAN_DATE  
    %O %CX_CLASS ein Objekt  
    %Klassenname - ein Objekt "partner @ %CX_CORPORATION" FindFirst(CX_CUSTOMER)
  • Die Platzhalter werden der Reihe nach von links nach rechts mit den Werten vom Stack ersetzt. Der erste Platzhalter von links wird durch den obersten Wert (Top) vom Stack ersetzt, der zweite durch den zweitobersten usw.
    Aus

    "a" "b" "c" "x = %s & y = %s & z = %s" Find(...)

    würde also

    "x = \"c\" & y = \"b\" & z = \"a\""

    gebildet werden.
    Als Eselsbrücke ziehe man in Gedanken zwischen dem letzten Parameter und dem Querystring einen Querstrich, der einen Spiegel symbolisiert. Der Platzhalter, der auf der rechten Seite dem Spiegel am nächsten ist, korrespondiert mit dem Parameter auf der linken Seite, der ebenfalls dem Spiegel am nächsten ist. Anschließend bewegt man sich auf der rechten Seite vom Spiegel weg (nach rechts) zum nächsten Platzhalter, und auf der linken Seite bewegt man sich ebenfalls vom Spiegel weg (nach links) zum nächsten Parameter:

    "a" "b" "c" | "x = %s & y = %s & z = %s" Find(...)
    (| ist der Spiegel)
     
  • Für a priori bekannte Werte können Konstanten geschrieben werden:
    Typ Beispiel
    Zeichenkette "name = \"Worthing\""
    numerische Konstante "number = 2.1828"
    Value-Konstante "credit > 25000.00$"
    ID einer Klasse "classID = CX_ITEM_CAST"

    Allerdings haben Platzhalter einen Vorteil: Wird eine Query mit derselben Struktur aber unterschiedlichen Werten (also z.B. mit Platzhaltern) mehrfach ausgeführt, dann werden Zwischenobjekte der Query mit dem InstantView-Querystring gecached und können beim wiederholten Aufruf schneller abgerufen werden. 

    suchString "uniqueID = %s" Find(CX_ITEM)

    ist also häufig mit unterschiedlichen Suchstrings aufgerufen schneller als:

    "uniqueID = \"" suchString + "\"" + Find(CX_ITEM)

 

  • Datenfelder der Objekte werden mit ihrem Namen bezeichnet - dies gilt auch für dynamische Datenfelder.
  • Ist ein Feldname doppelt belegt, d.h. der gleiche Name bezeichnet sowohl ein Datenmember einer Klasse als auch ein im ClassiX®-System bekanntes dynamisches Datenfeld, so bezieht sich der Name im Queryausdruck immer auf das dynamische Datenfeld. Um sich auf das "feste" Datenmember zu beziehen muss in diesem Fall dem Namen ein Scopeoperator vorangestellt werden: ::feldName.
  • Funktionen eines Objekts können nur dann aufgerufen werden, wenn sie als Query function im ClassiX®-System angemeldet wurden. Dies ist nur für parameterlose Funktionen möglich.
  • Im Querystring kann mit der Pseudofunktion Retrieve ein InstantView®-Zugriffsausdruck angegeben werden. Damit entfallen viele der oben genannten Einschränkungen; z.B. können beliebige Funktionen mit Parametern aufgerufen werden, auch solche, die transiente Daten zurückgeben.
  • Zur Beschleunigung der Queries können Indices aufgebaut werden (Indexmanager)
  • Der Querymanager im ClassiX®-System verwaltet alle mit Find, FindFirst oder FindExists ausgeführten Queries.
  • Queries können generell nur über collections von Klassen gestartet werden, die vom Typ CX_EXPANDABLE abgeleitet sind.
  • Mit collection[:queryExpression:] können nested queries definiert werden. ObjectStore muss den Typ der Objekte in der Collection kennen. Ein dafür nötiger Cast - (osSet<Klasse> &) - wird vom Query-Manager automatisch konstruiert, wenn dort das Tupel (Collectionname, Elementtyp) angemeldet wurde.
  • Ein @  vor einem Datenfeld oder Slot bewirkt, dass nicht der Inhalt des Objektes betrachtet werden soll, sondern die Adresse. Das ist gerade für Indizes nützlich, wenn z.B. ein festes Datenfeld vom Typ CX_ITEM_PATTERN indiziert werden soll: Das Objekt selbst kann nicht indiziert werden, wohl aber dessen Adresse. Die Suche nach einem bestimmten Pattern wird dadurch beschleunigt ("pattern @ %CX_ITEM_PATTERN" Find(CX_ITEM)).
  • Indizes auf eine Adresse können nicht sortiert sein.
     
  • Mit dem Fragezeichen-Operator (?) kann geprüft werden, ob ein Slot existiert oder nicht. "?comment" findet z.B. alle Objekte, deren comment-Slot vorhanden ist. Es ist unerheblich, ob letztendlich ein Wert darin gespeichert ist oder nicht (bei Strings ein Leerstring). Mit ! kann der Ausdruck negiert werden: "!?comment" würde alle Objekte finden, deren comment-Slot NICHT vorhanden ist.
    Bei Konstruktionen wie z.B. "?father.father" wird auf die Existenz des Slots "Vater von Vater" oder "Großvater" geprüft, d.h. nur beim letzten Slot des Ausdrucks wird auf Existenz geprüft.
  • Mit dem Operator ~ können Zeichenketten mit Hilfe von Mustern (engl. Patterns) verglichen werden. (QM Modul: Queries mit Pattern Matching)

    Beispiel: "name ~ \"A*\"" findet alle Objekte, deren Attribut 'name' mit einem großen A beginnt. In den Mustern können keine regulären Ausdrücke benutzt werden!

    Es gelten hierbei folgende Regeln: Ein * (Asterisk) steht für beliebig viele Zeichen, wobei die Anzahl auch 0 betragen kann. "Haus" würde also auf das Muster "H*aus" passen. Ein ? (Fragezeichen) steht für genau 1 beliebiges Zeichen. * und ? können beliebig miteinander kombiniert werden und auch mehrfach vorkommen. Wenn nach dem Zeichen ? oder * gesucht werden soll, muss ein \ vorangestellt werden. Um z.B. alles, was mit "Auto*Star" beginnt, zu suchen, ist "Auto\*Star*" der richtige Querystring. Dasselbe gilt für den Backslash selbst.

    Eine Besonderheit stellt der Ausdruck \i dar, z.B. bei "name ~ \"\ia*\"". \i bewirkt, dass der Vergleich der Zeichenketten ohne Beachtung der Groß-/Kleinschreibung durchgeführt wird. In dem letzten Beispiel würden also alle Objekte gefunden werden, deren Attribut 'name' mit einem großen oder kleinen A beginnt. \i darf sich nur am Anfang des Musters befinden.

    Der Ausdruck, der mit dem Muster verglichen werden soll, steht immer links vom Operator ~, das Muster immer rechts davon. Die linke Seite kann eine Konstante sein, ein Platzhalter, ein festes oder indiziertes Datenfeld oder die Retrieve()-Funktion. Auf der rechten Seite darf sich nur eine Konstante oder ein Platzhalter befinden. Datenfelder werden an dieser Stelle nicht unterstützt. Wenn diese Regeln nicht beachtet werden, bemängelt ObjectStore den Fehler im Query-Ausdruck.

    Der Vergleich mit Muster wird von ObjectStore zur Verfügung gestellt, nicht vom ClassiX®-System. Das bedeutet, dass bei Queries Indizes mit verwendet werden. Mustervergleiche in der Art "ABC*" werden daher besonders effizient ausgeführt, da bei Vergleichen dieser Art der Index benutzt werden kann.
  • ClassiX hält Queries in einem Cache und merkt sich ObjectStore-relevante berechnete Informationen für den nächsten Zugriff. Damit wird die Phase von der Erzeugung der Query bis zu dessen Ausführung beschleunigt. Query-Strings mit Pattern-Matching haben eine Besonderheit, als dass Pattern-Matching mit Platzhaltern an dieser Stelle kaum einen Performance-Gewinn gegenüber der Verwendung von Konstanten bietet, weil die Query zur Optimierung je nach konkretem Wert des Suchstrings anders übersetzt werden muss. Lediglich das parsen des Query-Strings selbst entfällt.
    "Ad*" "uniqueID ~ %s" Find(CX_ITEM)

    ist also in praktisch äquivalent zu

    "uniqueID ~ \"Ad*\"" Find(CX_ITEM)
  • Falls eine gewünschte Query aufgrund der von ClassiX durchgeführten Query-Übersetzung nicht funktioniert, gibt es den Ausweg über die Quoted_Expression. Hierbei werden alle Zeichen zwischen den "#"-Zeichen ohne Übersetzung an den Query-Mechanismus von ObjectStore übergeben.

Achtung!
Sobald eine Collection neben Objekten einer bestimmten Klasse X auch "gewrappte" Objekte dieser Klasse enthält, kann keine Query formuliert werden, die sich auf feste Datenfelder und oder Queryfunktionen der Klasse X bezieht.
Queries, die sich nur auf dynamische Datenfelder beziehen, funktionieren auch in diesem Fall.

 

Achtung:
Wird eine Query über einen CX_VALUE-Slot durchgeführt, dann muss beachtet werden, dass die Query fehlschlägt, falls nach einem Value mit konkreter Einheit (Bsp: 2m) gesucht wird und die Objekte in der durchsuchten Collection inkompatible Einheiten enthalten (Bsp: 1$). In dem Fall kann die Query nur über den Zahlenwert durchgeführt werden, indem mit der Einheit ? gesucht wird (Bsp: 2?).

- Beispiele -