PHP-Modul zum Arbeiten mit hierarchischen Daten in InterSystems IRIS

Bild Seit Beginn seiner Zeit ist PHP dafür bekannt (und kritisiert), die Integration in eine Vielzahl von Bibliotheken sowie in fast alle DBMS auf dem Markt zu unterstützen. Aus seltsamen Gründen wurden hierarchische Datenbanken auf Globals jedoch nicht unterstützt.

Globale sind Strukturen zum Speichern hierarchischer Informationen. Sie erinnern etwas an die Datenbank „Schlüssel -> Wert“, mit dem einzigen Unterschied, dass der Schlüssel mehrstufig sein kann:

Set ^inn("1234567890", "city") = "Moscow" Set ^inn("1234567890", "city", "street") = "Req Square" Set ^inn("1234567890", "city", "street", "house") = 1 Set ^inn("1234567890", "year") = 1970 Set ^inn("1234567890", "name", "first") = "Vladimir" Set ^inn("1234567890", "name", "last") = "Ivanov" 

In diesem Beispiel speichert die in der globalen Festplatte integrierte ObjectScript-Sprache, die auf der Festplatte gespeichert ist (angezeigt durch das Symbol ^ vor dem globalen Namen), mehrstufige Informationen.

Um mit Globals aus PHP arbeiten zu können, benötigen wir natürlich neue Funktionen, die vom PHP-Modul hinzugefügt werden. Dies wird weiter unten erläutert.

Globals unterstützen viele Funktionen für die Arbeit mit Hierarchien: Umgehen von Schlüsseln auf jeder Ebene separat, Löschen, Kopieren und Einfügen ganzer Bäume und einzelner Knoten. Nun, ebenso wie jede gute ACID-Transaktionsdatenbank. All dies geschieht aus zwei Gründen extrem schnell (etwa 10 5 -10 6 Einfügevorgänge pro Sekunde auf normaler Hardware):

  1. Globals sind eine niedrigere Abstraktion als SQL.
  2. Die globalen Basen sind seit Jahrzehnten in Produktion und in dieser Zeit gelang es ihnen, ihren Code zu lecken und ihn gründlich zu optimieren.

Mehr über Globals in der Artikelserie „Globals - Swords-Masons for Data Storage“:

Die Bäume. Teil 1.
Die Bäume. Teil 2.
Spärliche Arrays. Teil 3

In dieser Welt haben Globale ihren Hauptanwendungsort in Speichersystemen mit wenig strukturierten und spärlichen Informationen gefunden, wie z. B. medizinische, personenbezogene Daten, Bankgeschäfte usw.

Ich liebe PHP (und entwickle es) und wollte mit Globals herumspielen. Es gab kein fertiges Modul. Ich schrieb an InterSystems und bat mich, es zu erstellen. Das Warten hat zu nichts geführt, und am Ende haben wir (ich zusammen mit meinem Doktoranden) das Modul selbst gemacht. InterSystems hat die Entwicklung im Rahmen eines Ausbildungsstipendiums gesponsert.

Im Allgemeinen handelt es sich bei InterSystems IRIS um ein DBMS mit mehreren Modellen. Von PHP aus können Sie also über ODBC mit SQL damit arbeiten. Ich war jedoch an globalen Daten interessiert, und es gab keinen solchen Konnektor.

Das Modul ist also für PHP 7.x verfügbar (getestet unter 7.0-7.2). Derzeit funktioniert es nur mit InterSystems IRIS und Caché, die auf demselben Host installiert sind.

Modulseite in OpenExchange (ein Katalog mit Projekten und Add-Ons für Entwickler in InterSystems IRIS und Caché).

Es gibt einen nützlichen DISKUSSION-Abschnitt, in dem Personen ihre Nutzungserfahrungen teilen.

Hier herunterladen:
https://github.com/intersystems-community/php_ext_iris
Laden Sie das Repository von der Befehlszeile herunter:

 git clone https://github.com/intersystems-community/php_ext_iris 

Anleitung zur Installation des Moduls in Englisch und Russisch.

Modulfunktionen:
PHP-FunktionBeschreibung
Mit Daten arbeiten
iris_set ($ node, value)
Installationsort.
  1. iris_set ($ global, $ subscript1, ..., $ subscriptN, $ value);
    iris_set ($ global, $ value);

    Rückgabe: true oder false (bei Fehler).
    Alle Funktionsparameter sind Zeichenfolgen oder Zahlen. Der erste ist der Name des globalen, dann die Indizes, der letzte Parameter ist der Wert.

     iris_set('^time',1); iris_set('^time', 'tree', 1, 1, 'value'); 

    Analog auf ObjectScript

     Set ^time = 1 Set ^time("tree", 1, 1) = "value" 
  2. iris_set ($ arrayGlobal, $ value);
    Nur 2 Parameter: Der erste ist ein Array, in dem der Name des globalen und alle seine Indizes gespeichert sind; Das zweite ist die Bedeutung.

     $node = ['^time', 'tree', 1, 1]; iris_set($node,'value'); 

iris_get ($ node)
Knoten lesen.
Rückgabe: Wert (Zahl oder Zeichenfolge), NULL (Wert nicht definiert) oder FALSE (bei Fehler).

  1. iris_get ($ global, $ subscript1, ..., $ subscriptN);
    iris_get ($ global);
    Alle Funktionsparameter sind Zeichenfolgen oder Zahlen. Der erste ist der Name des Globalen, dann die Indizes. Ein Global hat möglicherweise keine Indizes.

     $res = iris_get('^time'); $res1 = iris_get('^time', 'tree', 1, 1); 
  2. iris_get ($ arrayGlobal);
    Der einzige Parameter ist das Array, in dem der Name des globalen Index und alle seine Indizes gespeichert sind.

     $node = ['^time', 'tree', 1, 1]; $res = iris_get($node); 

iris_zkill ($ node)
Knotenwert löschen.
Rückgabe: TRUE oder FALSE - bei Fehler.

Es ist wichtig zu beachten, dass diese Funktion nur den Wert im Knoten löscht und die zugrunde liegenden Zweige nicht berührt.

  1. iris_zkill ($ global, $ subscript1, ..., $ subscriptN);
    iris_zkill ($ global);
    Alle Funktionsparameter sind Zeichenfolgen oder Zahlen. Der erste ist der Name des Globalen, dann die Indizes. Ein Global hat möglicherweise keine Indizes.

     $res = iris_zkill('^time'); //    . $res1 = iris_zkill('^time', 'tree', 1, 1); 
  2. iris_zkill ($ arrayGlobal);
    Der einzige Parameter ist das Array, in dem der Name des globalen Index und alle seine Indizes gespeichert sind.

     $a = ['^time', 'tree', 1, 1]; $res = iris_zkill($a); 

iris_kill ($ node)
Löschen Sie einen Knoten und alle untergeordneten Zweige.
Rückgabe: TRUE oder FALSE - bei Fehler.

  1. iris_kill ($ global, $ subscript1, ..., $ subscriptN);
    iris_kill ($ global);
    Alle Funktionsparameter sind Zeichenfolgen oder Zahlen. Der erste ist der Name des Globalen, dann die Indizes. Ein Global hat möglicherweise keine Indizes. In diesem Fall wird er vollständig gelöscht.

     $res1 = iris_kill('^example', 'subscript1', 'subscript2'); $res = iris_kill('^time'); //   . 
  2. iris_kill ($ arrayGlobal);
    Der einzige Parameter ist das Array, in dem der Name des globalen Index und alle seine Indizes gespeichert sind.

     $a = ['^time', 'tree', 1, 1]; $res = iris_kill($a); 

iris_order ($ node)
Umgehen Sie globale Niederlassungen auf einer bestimmten Ebene
Rückgabe: Ein Array, das den vollständigen Namen des nächsten globalen Knotens oder FALSE enthält (bei Fehler).

  1. iris_order ($ global, $ subscript1, ..., $ subscriptN);
    Alle Funktionsparameter sind Zeichenfolgen oder Zahlen. Der erste ist der Name des Globalen, dann die Indizes.

    PHP-Nutzungsformular und ObjectScript-Äquivalent:

     iris_order('^ccc','new2','res2'); // $Order(^ccc("new2", "res2")) 
  2. iris_order ($ arrayGlobal);
    Der einzige Parameter ist das Array, in dem der globale Name und die Indizes des Startknotens gespeichert sind.

     $node = ['^inn', '1234567890', 'city']; for (; $node !== NULL; $node = iris_order($node)) { echo join(', ', $node).'='.iris_get($node)."\n"; } 

    Wird uns eine Schlussfolgerung geben:

     ^inn, 1234567890, city=Moscow ^inn, 1234567890, year=1970 

iris_order_rev ($ node)
Umgehen Sie globale Zweige auf einer bestimmten Ebene in umgekehrter Reihenfolge
Rückgabe: Ein Array, das den vollständigen Namen des vorherigen globalen Knotens auf derselben Ebene oder FALSE enthält (falls ein Fehler auftritt).

  1. iris_order_rev ($ global, $ subscript1, ..., $ subscriptN);

    Alle Funktionsparameter sind Zeichenfolgen oder Zahlen. Der erste ist der Name des Globalen, dann die Indizes.

    PHP-Nutzungsformular und ObjectScript-Äquivalent:

     iris_order_rev('^ccc','new2','res2'); // $Order(^ccc("new2", "res2"), -1) 
  2. iris_order_rev ($ arrayGlobal);

    Der einzige Parameter ist das Array, in dem der globale Name und die Indizes des Startknotens gespeichert sind.

     $node = ['^inn', '1234567890', 'name', 'last']; for (; $node !== NULL; $node = iris_order_rev($node)) { echo join(', ', $node).'='.iris_get($node)."\n"; } 

    Wird uns eine Schlussfolgerung geben:

     ^inn, 1234567890, name, last=Ivanov ^inn, 1234567890, name, first=Vladimir 

iris_query ($ CmdLine)
Umgehung globaler Niederlassungen mit Zugang zu niedrigeren Ebenen
Rückgabe: Ein Array, das den vollständigen Namen des zugrunde liegenden Knotens (falls vorhanden) oder des nächsten globalen Knotens (falls kein verschachtelter Knoten vorhanden ist) enthält.

  1. iris_query ($ global, $ subscript1, ..., $ subscriptN);
    Alle Funktionsparameter sind Zeichenfolgen oder Zahlen. Der erste ist der Name des Globalen, dann die Indizes.

    PHP-Nutzungsformular und ObjectScript-Äquivalent:

     iris_query('^ccc', 'new2', 'res2'); // $Query(^ccc("new2", "res2")) 
  2. iris_query ($ arrayGlobal);
    Der einzige Parameter ist das Array, in dem der globale Name und die Indizes des Startknotens gespeichert sind.

     $node = ['^inn', 'city']; for (; $node !== NULL; $node = iris_query($node)) { echo join(', ', $node).'='.iris_get($node)."\n"; } 

    Wird uns eine Schlussfolgerung geben:

     ^inn, 1234567890, city=Moscow ^inn, 1234567890, city, street=Req Square ^inn, 1234567890, city, street, house=1 ^inn, 1234567890, name, first=Vladimir ^inn, 1234567890, name, last=Ivanov ^inn, 1234567890, year=1970 

Die Reihenfolge unterscheidet sich von der Reihenfolge, in der wir festlegen, da in der globalen automatisch beim Einfügen alles in aufsteigender Reihenfolge sortiert wird.
Servicefunktionen
iris_set_dir ($ FullPath)
Festlegen eines Verzeichnisses mit einer Datenbank
Rückgabe: TRUE oder FALSE - bei Fehler.

 iris_set_dir('/InterSystems/Cache/mgr'); 

Es muss abgeschlossen sein, bevor eine Verbindung zur Datenbank hergestellt wird.
iris_exec ($ CmdLine)
Führen Sie den DB-Befehl aus
Rückgabe: TRUE oder FALSE - bei Fehler.

 iris_exec('kill ^global(6)'); //   ObjectScript    

iris_connect ($ login, $ pass)Stellen Sie eine Verbindung zur Datenbank her
iris_quit ()Unterbrechen Sie die Verbindung zur Datenbank
iris_errno ()Fehlercode abrufen
iris_error ()Holen Sie sich eine Textbeschreibung des Fehlers

Wenn Sie selbst mit dem Modul spielen möchten, dann:

Speziell für Habr-Benutzer wurde eine Docker-Datei erstellt, um das Image zu erstellen.
 git clone https://github.com/intersystems-community/php_ext_iris cd php_ext_iris/iris docker-compose build docker-compose up -d 

Testen der Demoseite auf localhost: 52080 im Browser.

PHP-Dateien, die bearbeitet und mit ihnen abgespielt werden können, befinden sich im Ordner php / demo und werden im Container bereitgestellt.

Verwenden Sie zum Testen von IRIS den Benutzernamen admin . Das Kennwort lautet SYS .

Verwenden Sie die folgende URL, um die IRIS-Einstellungen einzugeben:
http: // localhost: 52773 / csp / sys / UtilHome.csp

Verwenden Sie den folgenden Befehl, um die IRIS-Konsole dieses Containers aufzurufen:
 docker exec -it iris_iris_1 iris session IRIS 


Speziell für Benutzer von Habr und InterSystems Caché wurde die Virtualka mit PHP-Modul aufgehoben.
Demoseite in russischer Sprache.
Demoseite auf Englisch.
Login: habr_test
Passwort: burmur # @ 8765

Zur Selbstinstallation des Moduls unter InterSystems Caché
  1. Habe Linux. Ich habe unter Ubuntu getestet, unter Windows sollte das Modul auch gebaut werden, aber ich habe es nicht getestet.
  2. Kostenlose Version herunterladen:
  3. Installieren Sie das cach.so-Modul gemäß den Anweisungen in PHP.

Im Docker-Container auf meinem PC (AMD FX-9370 @ 4700 MHz 32 GB, LVM, SATA SSD) habe ich zum Spaß zwei primitive Tests zur Geschwindigkeit des Einfügens neuer Werte in die Datenbank durchgeführt.

  • Das Einfügen von 1 Million neuen Elementen in das globale Element dauerte 1,81 Sekunden oder 552.000 Einfügungen pro Sekunde.
  • Das Aktualisieren eines Werts in derselben globalen 1.000.000-fachen Zeit dauerte 1,98 Sekunden oder 505.000 Aktualisierungen pro Sekunde. Eine interessante Tatsache ist, dass das Einfügen erfolgt als das Aktualisieren. Anscheinend ist dies eine Folge der anfänglichen Datenbankoptimierung zum schnellen Einfügen.

Es ist klar, dass diese Tests nicht behaupten können, genau und nützlich zu sein, da sie primitiv sind und in einem Behälter durchgeführt werden. Auf leistungsfähigerer Hardware mit einem PCIe-SSD-Festplattensystem können zig Millionen Einfügungen pro Sekunde erzielt werden.

Was kann abgeschlossen werden und der aktuelle Status


  1. Sie können nützliche Funktionen für die Arbeit mit Transaktionen hinzufügen (Sie können sie weiterhin über iris_exec verwenden).
  2. Die Funktion zum Zurückgeben der gesamten Struktur des Globalen ist nicht implementiert, sodass PHP das Globale nicht umgeht.
  3. Die Funktion zum Speichern des PHP-Arrays als Teilbaum ist nicht implementiert.
  4. Der Zugriff auf lokale Datenbankvariablen ist nicht implementiert. Nur über iris_exec, obwohl es über iris_set besser ist.
  5. Nicht implementiert, indem die globale Tiefe in die entgegengesetzte Richtung umgangen wird.
  6. Der Zugriff auf die Datenbank über das Objekt mithilfe von Methoden (ähnlich den aktuellen Funktionen) ist nicht implementiert.

Das aktuelle Modul ist wahrscheinlich noch nicht produktionsbereit: Es wurden keine Tests auf hohe Lasten und Speicherlecks durchgeführt. Wenn es jedoch jemand braucht, kontaktieren Sie mich (Sergey Kamenev updates@mail.ru).

Fazit


Lange Zeit haben sich die Welten von PHP und die hierarchischen Grundlagen auf Globals praktisch nicht überschnitten, obwohl Globals eine starke und schnelle Funktionalität für bestimmte Datentypen (medizinisch, persönlich) bieten.

Ich hoffe, dass dieses Modul als Anstoß für Experimente von PHP-Programmierern mit Globals und ObjectScript-Programmierern zur einfachen Entwicklung von Webschnittstellen in PHP dienen wird.

PS Vielen Dank für Ihre Aufmerksamkeit!

Source: https://habr.com/ru/post/de443440/


All Articles