Die hybride Sprachmodellierungskomponente ist eine Reihe von Konzeptobjekten, die durch logische Beziehungen verbunden sind. Es gelang mir, über die wichtigsten Methoden zur Definition von Konzepten zu sprechen, einschließlich der Vererbung und der Definition von Beziehungen zwischen ihnen. Und auch über einige der Nuancen der logischen Programmierung, einschließlich der Semantik des Negationsoperators und der Logik höherer Ordnung. Eine vollständige Liste der Veröffentlichungen zu diesem Thema finden Sie am Ende dieses Artikels.
Im Bereich der Arbeit mit Daten ist die SQL-Sprache unbestritten führend. Einige seiner Funktionen, die sich in der Praxis als sehr praktisch erwiesen haben, wie z. B. die Aggregation, wurden später auf die Logikprogrammierung migriert. Daher ist es hilfreich, für die Modellierungskomponente so viel wie möglich aus SQL zu leihen. In diesem Artikel möchte ich Ihnen zeigen, wie verschachtelte Abfragen, äußere Verknüpfungen und Aggregationen in Konzeptdefinitionen eingebettet werden können. Ich werde auch über eine andere Art von Konzepten sprechen, die mit einer Funktion beschrieben wird, die Objekte (Entitäten) in einem algorithmischen Stil generiert, ohne auf eine logische Suche zurückzugreifen. Und ich werde Ihnen zeigen, wie Sie Arrays von Objekten als übergeordnete Konzepte in Analogie zur SQL UNNEST- Operation verwenden können Dadurch werden Sammlungen in das Tabellenformat konvertiert und können mit anderen Tabellen in der FROM-Klausel verknüpft werden .
Anonyme Definitionen von Konzepten
In der SQL-Welt sind verschachtelte Abfragen ein Tool, das häufig verwendet wird, wenn Zwischendaten für die weitere Verarbeitung in der Hauptabfrage abgerufen werden müssen. Die Modellierungskomponente benötigt keinen so dringenden Bedarf an Zwischendaten, da der Weg zu deren Erhalt als separates Konzept formalisiert werden kann. Es gibt jedoch Fälle, in denen verschachtelte Konzeptdefinitionen nützlich wären.
Manchmal müssen Sie das Konzept leicht ändern, die einzelnen Attribute auswählen und die Werte herausfiltern. Wenn diese Änderung nur an einer Stelle erforderlich ist, ist es nicht sinnvoll, ein separates Konzept mit einem eindeutigen Namen zu erstellen. Diese Situation tritt häufig auf, wenn Konzepte Argumente für Funktionen wie exist , find oder sind findOne , das die Ableitbarkeit des Konzepts überprüft und alle oder nur das erste Objekt (die Entität) des Konzepts findet. Hier können Sie eine Analogie zu anonymen Funktionen in funktionalen Programmiersprachen ziehen, die häufig als Argumente für Funktionen wie Map , Find , Filter usw. verwendet werden.
Beachten Sie die Syntax zum Definieren eines anonymen Konzepts. Im Allgemeinen folgt es der Syntax einer allgemeinen Konzeptdefinition, außer dass Sie in einigen Fällen die Attributlisten und den Namen des untergeordneten Konzepts weglassen können. Wenn ein anonymes Konzept als Argument verwendet wird, um zu existierenWenn dann der Name und die Liste der Attribute nicht wichtig sind, reicht es aus, zu überprüfen, ob zumindest ein Ergebnis vorliegt. Die Funktionen find und findOne benötigen möglicherweise nicht den Konzeptnamen, wenn die Ausgabe nicht als gesamtes Konzept verwendet wird, sondern nur als Satz von Attributen in einem assoziativen Array. Wenn keine Attribute angegeben sind, wird standardmäßig der Vererbungsmechanismus verwendet, und die Attribute werden von übergeordneten Konzepten geerbt. Wenn kein Konzeptname angegeben wird, wird dieser automatisch generiert.
Versuchen wir anhand einiger Beispiele zu erklären, was oben geschrieben wurde. Mit der existiert Funktion, können Sie die Deduzierbarkeit oder Nicht-Ableitbarkeit eines eingebetteten Konzept überprüfen:
concept freeExecutor is executor e where not exists ( task t where t.executor = e.id and t.status in ('assigned', 'in process') )
In diesem Beispiel ist das anonyme Konzept:
(task t where t.executor = e.id and t.status in ('assigned', 'in process'))
ist eigentlich ein Konzept, das alle Attribute des Aufgabenkonzepts erbt :
(concept _unanimous_task_1 as task t where t.executor = e.id and t.status in ('assigned', 'in process'))
Mit der Suchfunktion können Sie alle Werte eines Konzepts als Liste zurückgeben, die dann einem Attribut zugeordnet werden können:
concept customerOrdersThisYear is customer c with orders where c.orders = find( (id = o.id, status = o.status, createdDate = o.createdDate, total = o.total) from order o where o.customerId = c.id and o.createdDate > '2021-01-01' )
In diesem Beispiel erweitern wir den Begriff des Kunden um eine Liste von Bestellungen, bei denen es sich um Objekte handelt, die die ausgewählten Attribute des Bestellbegriffs enthalten . Wir haben eine Liste von Attributen für ein anonymes Konzept angegeben, deren Name jedoch weggelassen wird.
Die Bedingungen in dem Abschnitt , wo anonyme Konzepte können auch andere Attribute der Mutter- oder Tochtergesellschaften Konzepte, in diesem Fall sind c.id . Ein Merkmal anonymer Konzepte ist, dass alle diese externen Variablen und Attribute zum Zeitpunkt des Starts der Suche nach Lösungen unbedingt mit Werten verknüpft sein müssen. Somit können die Objekte des anonymen Konzepts erst gefunden werden, nachdem die Objekte des Kundenkonzepts gefunden wurden . ...
Externe Verbindungen
Anonyme Konzeptdefinitionen können auch im Abschnitt from verwendet werden , wo sie übergeordnete Konzepte darstellen. Darüber hinaus ist es bei der Definition eines anonymen Konzepts möglich, einige der Bedingungen, die es verbinden, mit anderen Konzepten zu übertragen, was einen besonderen Effekt hat. Diese Bedingungen werden bei der Suche nach einer Lösung für das anonyme Konzept überprüft und haben keinen Einfluss auf den Inferenzprozess des untergeordneten Konzepts. Hier können Sie eine Analogie zwischen den Bedingungen im Abschnitt where eines anonymen Konzepts und den Bedingungen im Abschnitt JOIN ON von SQL ziehen.
Somit können anonyme Konzepte verwendet werden, um ein SQL-Analogon des linken äußeren Joins zu implementieren. Dies erfordert drei Dinge.
- Ersetzen Sie zunächst das gewünschte übergeordnete Konzept durch ein darauf basierendes anonymes Konzept und übertragen Sie alle Verbindungen mit anderen übergeordneten Konzepten darauf.
- Zweitens, um darauf hinzuweisen, dass das Scheitern der Folgerung dieses Konzepts nicht zu einem automatischen Scheitern der Folgerung des gesamten Kinderkonzepts führen sollte. Dazu müssen Sie dieses übergeordnete Konzept mit dem optionalen Schlüsselwort markieren .
- Und drittens können Sie im Abschnitt where des untergeordneten Konzepts prüfen, ob es Lösungen für dieses anonyme Konzept gibt.
Schauen wir uns ein kleines Beispiel an:
concept taskAssignedTo (task = t, assignee = u, assigneeName) from task t, optional (user where id = t.assignedTo) u where assigneeName = if(defined(u), u.firstName + ' ' + u.lastName, 'Unassigned')
Zu den Attributen des TaskAssignedTo- Konzepts gehören Objekte der Task, ihr Executor und separat der Name des Executors. Die übergeordneten Konzepte sind Aufgabe und Benutzer , und letztere können leer sein, wenn die Aufgabe noch keinen Ausführenden hat. Es wird in eine anonyme Konzeptdefinition eingeschlossen, der das optionale Schlüsselwort vorangestellt ist . Die Inferenzprozedur findet zuerst Objekte des Aufgabenkonzepts und erstellt dann basierend auf dem Benutzer ein anonymes Konzept, das es einem bestimmten Wert des Attributs "assignTo" des Aufgabenkonzepts zuordnet . Stichwort optional teilt der Inferenzroutine mit, dass wenn das Konzept fehlschlägt, sein Objekt dem speziellen Wert UNDEFINED zugeordnet wird . Durch Überprüfen des Ergebnisses der Ausgabe auf der Ebene des untergeordneten Konzepts kann das Attribut assigneeName einen Standardwert festlegen. Wenn das optionale Schlüsselwort nicht angegeben wurde, schlägt der aktuelle Zweig der untergeordneten Konzeptsuche fehl, wenn kein anonymes Konzept abgeleitet wird. Dies wäre analog zum inneren Join von SQL.
Da die Bedingungen in der wo - Klausel des anonymen Konzept gehören das AssignedTo Attribut des anderen Elternteils Konzept Aufgabe , dann ist die Suche nach Objekten des Konzepts Benutzer ist nur möglich , nach der Bindung Aufgabe Objekte mit Werten. Sie können nicht getauscht werden:
from optional (user where id = t.assignedTo) u, task t
Da der Wert von t.assignedTo im Anfangsstadium unbekannt ist, funktioniert es nicht, eine Definition eines anonymen Konzepts zu erstellen.
Wenn in SQL die Reihenfolge der Tabellen in dem von Abschnitt spielt keine Rolle, dann bestimmt in Prolog die Reihenfolge der Prädikate in einer Regel eindeutig die Reihenfolge des Durchlaufens des Entscheidungsbaums. Gleiches gilt für die Simulationskomponente, deren Ausgaberegel auf der in Prolog verwendeten SLD-Auflösung basiert. Darin bestimmt das Ergebnis der Ausgabe von Objekten des linken Konzepts die Einschränkungen für die Ausgabe von Objekten des rechten Konzepts. Aus diesem Grund funktioniert es leider nicht, die Operationen für den rechten äußeren Join und den vollständigen äußeren Join auf dieselbe natürliche Weise zu implementieren. Da die Kardinalität der Ergebnismenge des rechten übergeordneten Konzepts größer sein kann als die des linken, müssen sie in die entgegengesetzte Richtung ausgegeben werden - vom rechten Konzept nach links. Leider schränken die Besonderheiten des gewählten Inferenzverfahrens die Funktionalität der Sprache ein. Die vollständige äußere Verbindungsoperation kann jedoch durch Verbinden der inneren, emuliert werden.linke und rechte Gewerkschaft:
concept outerJoinRelation( concept1Name, concept2Name, concept1Key, concept2Key, concept1 = c1, concept2 = c2 ) from <concept1Name> c1, <concept2Name> c2 where c1.<concept1Key> = c2.<concept2Key>; concept outerJoinRelation( concept1Name, concept2Name, concept1Key, concept2Key, concept1 = c1, concept2 = null ) from <concept1Name> c1 where not exists( <concept2Name> c2 where c1.<concept1Key> = c2.<concept2Key>); concept outerJoinRelation( concept1Name, concept2Name, concept1Key, concept2Key, concept1 = null, concept2 = c2 ) from <concept2Name> c2 where not exists( <concept1Name> c1 where c1.<concept1Key> = c2.<concept2Key>);
Dieses verallgemeinerte Konzept wird unter Verwendung der im vorherigen Artikel beschriebenen Logik höherer Ordnung beschrieben . Seine Definition ist in drei Teile gegliedert. Der erste findet Schnittpunkte von Konzepten, der zweite Objekte, die der linke und der linke nicht hat, und der dritte - umgekehrt. Da die Namen der einzelnen Teile gleich sind, werden die Ergebnisse ihrer Folgerung kombiniert.
Anhäufung
Die Aggregation ist ein wesentlicher Bestandteil sowohl der relationalen Algebra als auch der Logikprogrammierung. In SQL können Sie mit der GROUP BY-Klausel Zeilen mit demselben Schlüsselwert in Zusammenfassungszeilen gruppieren. Es ermöglicht das Entfernen doppelter Werte und wird häufig mit Aggregatfunktionen wie Summe , Anzahl , Min , Max , Durchschnitt verwendet.... Aggregatfunktionen geben für jede Zeilengruppe einen normalen Wert zurück, der auf allen Zeilen in dieser Gruppe basiert. In der Logikprogrammierung hat die Aggregation eine komplexere Semantik. Dies liegt an der Tatsache, dass in einigen Fällen der rekursiven Definition von SLD-Regeln die Auflösung in eine Endlosschleife gerät und nicht abgeschlossen werden kann. Wie im Fall der Verweigerung als Fehler wird das Problem der Rekursion in der Aggregationsoperation unter Verwendung einer persistenten Modellsemantik oder einer fundierten Semantik gelöst. Ich habe versucht, im vorherigen Artikel kurz über diese Ansätze zu sprechen . Da die Semantik der Modellierungskomponente jedoch so einfach wie möglich sein sollte, wird die Standard-SLD-Auflösung bevorzugt. Und das Problem der Vermeidung einer unendlichen Rekursion lässt sich besser lösen, indem die Verbindungen zwischen den Konzepten neu hergestellt werden.
Die Aggregation könnte natürlich in einem funktionalen Stil unter Verwendung der Rechenkomponente einer Hybridsprache implementiert werden. Dazu reicht eine Funktion aus, die Inferenzergebnisse in eindeutige Gruppen zusammenfasst und für jede von ihnen Aggregatfunktionen berechnet. Die Aufteilung der Definition eines Konzepts in logische und funktionale Teile ist jedoch nicht die bequemste Lösung für ein so wichtiges Werkzeug wie die Aggregation. Erweitern Sie die Syntax der Definition besser um einen Gruppierungsabschnitt und Aggregationsfunktionen:
concept < > < > ( < > = <>, ... ) group by < >, ... from < > < > ( < > = <> , ... ), ... where < >
Die Gruppe nach Abschnitt enthält genau wie in SQL eine Liste von Attributen, mit denen die Gruppierung durchgeführt wird. Beziehungsausdrücke können auch Aggregationsfunktionen enthalten. Ausdrücke, die solche Funktionen enthalten, werden als undefiniert betrachtet, bis die Werte aller übergeordneten Konzepte gefunden und eine Gruppierung durchgeführt wurden. Dann können ihre Werte für jede Gruppe berechnet, Attributen zugeordnet und / oder zum Filtern von Gruppen verwendet werden. Mit diesem faulen Ansatz zur Bewertung und Überprüfung von Bedingungen ist kein Abschnitt HAVING erforderlich , der die Filterbedingungen vor und nach der Gruppierung trennt. Die Laufzeit wird dies automatisch tun.
Die Hauptaggregationsfunktionen sind count , sum, Durchschnitt , min , max . Der Zweck der Funktionen kann aus ihren Namen verstanden werden. Da die Modellierungskomponente natürlich mit zusammengesetzten Datentypen arbeiten kann, können Sie auch eine Funktion hinzufügen, die gruppierte Werte als Liste zurückgibt. Nennen wir es Gruppe . Das Eingabeargument ist ein Ausdruck. Die Funktion gibt eine Liste der Ergebnisse der Auswertung dieses Ausdrucks für jedes Element in der Gruppe zurück. Durch die Kombination mit anderen Funktionen können Sie eine beliebige Aggregationsfunktion implementieren. Die Gruppenfunktion ist bequemer als SQL-Funktionen wie group_concat oder json_arrayagDiese werden häufig als Zwischenschritt verwendet, um ein Array von Feldwerten zu erhalten.
Gruppierungsbeispiel:
concept totalOrders ( customer = c, orders = group(o), ordersTotal = sum(o.total) ) group by customer from customer c, order o where c.id = o.customerId and ordersTotal > 100
Das Auftragsattribut enthält eine Liste aller Benutzerbestellungen, ordersTotal - die Summe aller Bestellungen. Die Bedingung ordersTotal> 100 wird überprüft, nachdem die Gruppierung abgeschlossen und die Summenfunktion berechnet wurde .
Konzept definiert durch Funktion
Die deklarative logische Form der Beschreibung von Konzepten ist nicht immer zweckmäßig. Manchmal ist es bequemer, eine Folge von Berechnungen festzulegen, deren Ergebnis die Essenz des Konzepts ist. Diese Situation tritt häufig auf, wenn Fakten aus externen Datenquellen geladen werden müssen, z. B. aus einer Datenbank, Dateien, Anforderungen an externe Dienste senden usw. Es ist zweckmäßig, das Konzept in Form einer Funktion darzustellen, die die Eingabeabfrage in eine Abfrage in die Datenbank übersetzt und das Ergebnis der Ausführung dieser Abfrage zurückgibt. Manchmal ist es sinnvoll, eine logische Schlussfolgerung aufzugeben und sie durch eine bestimmte Implementierung zu ersetzen, die die Besonderheiten eines bestimmten Problems berücksichtigt und effizienter löst. Es ist auch bequemer, unendliche Sequenzen in einem funktionalen Stil zu beschreiben, die Konzeptentitäten erzeugen, beispielsweise eine Folge von ganzen Zahlen.
Die Prinzipien der Arbeit mit solchen Konzepten sollten dieselben sein wie mit den übrigen oben beschriebenen Konzepten. Die Suche nach Lösungen sollte mit denselben Methoden gestartet werden. Sie können selbst als übergeordnete Konzepte in Konzeptdefinitionen verwendet werden. Nur die interne Implementierung der Suche nach einer Lösung sollte sich unterscheiden. Daher werden wir eine andere Möglichkeit vorstellen, ein Konzept mithilfe einer Funktion zu definieren:
concept < > ( < >, ... ) by < >
Um ein mit einer Funktion definiertes Konzept zu definieren, müssen Sie eine Liste seiner Attribute und eine Funktion zum Generieren von Objekten angeben. Ich habe beschlossen, die Liste der Attribute zu einem obligatorischen Element der Definition zu machen, da dies die Verwendung eines solchen Konzepts vereinfacht. Um seine Struktur zu verstehen, müssen Sie die Funktion der Generierung von Objekten nicht untersuchen.
Lassen Sie uns nun über die Funktion zum Generieren von Objekten sprechen. Offensichtlich sollte es eine Anfrage als Eingabe erhalten - die Anfangswerte der Attribute. Da diese Werte angegeben werden können oder nicht, können sie der Einfachheit halber in einem assoziativen Array platziert werden, das das Eingabeargument der Funktion ist. Es ist auch hilfreich zu wissen, in welchem Modus die Suche nach Konzeptbedeutungen gestartet wird - finden Sie alle möglichen Werte, suchen Sie nur die ersten oder überprüfen Sie nur die Existenz einer Lösung. Daher fügen wir den Suchmodus als zweites Eingabeargument hinzu.
Das Ergebnis der Bewertung der Funktion sollte eine Liste von Konzeptobjekten sein. Da die auf der Suche mit Backtracking basierende Inferenzprozedur diese Werte jedoch einzeln verbraucht, ist es möglich, das Ausgabeargument der Funktion nicht zur Liste selbst, sondern zu einem Iterator zu machen. Dies würde die Definition des Konzepts flexibler machen, zum Beispiel würde es bei Bedarf ermöglichen, eine verzögerte Bewertung oder eine unendliche Folge von Objekten zu implementieren. Sie können einen Iterator einer beliebigen Standardsammlung verwenden oder eine eigene benutzerdefinierte Implementierung erstellen. Das Auflistungselement muss ein assoziatives Array mit den Werten der Konzeptattribute sein. Die Essenzen des Konzepts werden automatisch auf ihrer Basis erstellt.
Die Verwendung eines Iterators als Rückgabetyp hat seine Nachteile. Es ist umständlicher und weniger benutzerfreundlich als nur eine Ergebnisliste zurückzugeben. Die beste Option zu finden, die Vielseitigkeit, Einfachheit und Benutzerfreundlichkeit kombiniert, ist eine Herausforderung für die Zukunft.
Betrachten Sie als Beispiel das Konzept, das Zeitintervalle beschreibt. Angenommen, wir möchten den Arbeitstag in 15-Minuten-Intervalle unterteilen. Wir können dies mit einer ziemlich einfachen Funktion tun:
concept timeSlot15min (id, hour, minute) by function(query, mode) { var timeSlots = []; var curId = 1; for(var curHour = 8; curHour < 19; curHour += 1) { for(var curMinute = 0; curMinute < 60; curMinute += 15) { timeSlots.push({ id: curId, hour: curHour, minute: curMinute; }); curId++; } } return timeSlots.iterator(); }
Die Funktion gibt einen Iterator für alle möglichen Werte eines 15-Minuten-Zeitintervalls für einen Arbeitstag zurück. Es kann zum Beispiel verwendet werden, um nach freien Slots zu suchen, die noch nicht gebucht wurden:
concept freeTimeSlot is timeSlot15min s where not exists (bookedSlot b where b.id = s.id)
Die Funktion überprüft nicht , das Ergebnis der Berechnungen für die Einhaltung der Abfrage Abfrage , wird dies automatisch durchgeführt werden , wenn eine Reihe von Attributen zu einer Einheit umgewandelt wird . Bei Bedarf können jedoch Abfragefelder verwendet werden, um die Funktion zu optimieren. Bilden Sie beispielsweise eine Datenbankabfrage basierend auf den Abfragefeldern für ein Konzept.
Ein Konzept durch eine Funktion kombiniert logische und funktionale Semantik. Wenn im Funktionsparadigma die Funktion das Ergebnis für die angegebenen Werte der Eingabeargumente berechnet, gibt es im logischen Paradigma keine Unterteilung in Ausgabe- und Eingabeargumente. Es kann nur ein Teil der Argumente in beliebiger Kombination angegeben werden, und die Funktion muss die Werte der verbleibenden Argumente ermitteln. In der Praxis ist es nicht immer möglich, eine solche Funktion zu implementieren, mit der Berechnungen in jede Richtung durchgeführt werden können. Daher ist es sinnvoll, die möglichen Kombinationen freier Argumente einzuschränken. Deklarieren Sie beispielsweise, dass einige Argumente an Werte gebunden sein müssen, bevor Sie eine Funktion bewerten. Markieren Sie dazu solche Attribute in der Definition des Konzepts mit dem gewünschten Schlüsselwort .
Betrachten Sie als Beispiel das Konzept, das die Werte einer bestimmten Exponentialskala definiert.
concept expScale (value, position, required limit) by function(query, mode) { return { _curPos = 0, _curValue = 1, next: function() { if(!this.hasNext()) { return null; } var curItem = {value: this._curValue, position: this._curPosition, limit: query.limit}; this._curPos += 1; this._curValue = this._curValue * Math.E; return curItem; }, hasNext: function() { return query.limit == 0 || this._curPos < query.limit; } }}
Die Funktion gibt einen Iterator zurück, der Konzeptentitäten mithilfe einer verzögerten Auswertung generiert. Die Größe der Sequenz wird durch den Wert des Grenzwertattributs begrenzt. Wenn sie jedoch Null ist, wird sie unendlich. Konzepte, die auf unendlichen Sequenzen basieren, müssen sehr sorgfältig verwendet werden, da sie nicht garantieren, dass die Inferenzroutine abgeschlossen wird. Das Limit- Attribut ist Hilfsattribut und wird zum Organisieren von Berechnungen verwendet. Wir können es nicht aus den Werten anderer Attribute ableiten. Es muss bekannt sein, bevor die Berechnung beginnt, daher wurde es als erforderlich markiert
Wir haben eine der Optionen in Betracht gezogen, wie ein Konzept als Funktion aussehen könnte. Die Fragen der Sicherheit und Verwendbarkeit solcher Konzepte erfordern jedoch in Zukunft detailliertere Untersuchungen.
Verschachtelte Sammlungen reduzieren
Einige SQL-Dialekte, die mit Daten in Objektform arbeiten können, unterstützen eine Operation wie UNNEST , die den Inhalt einer Auflistung in ein Tabellenformat (Rowset) konvertiert und die resultierende Tabelle zur FROM-Klausel hinzufügt . Es wird normalerweise verwendet, um Objekte mit verschachtelten Strukturen zu reduzieren, dh um sie zu reduzieren oder zu reduzieren. Beispiele für solche Sprachen sind BigQuery und SQL ++.
Angenommen, wir speichern Benutzerinformationen als JSON-Objekt:
[ { "id":1, "alias":"Margarita", "name":"MargaritaStoddard", "nickname":"Mags", "userSince":"2012-08-20T10:10:00", "friendIds":[2,3,6,10], "employment":[{ "organizationName":"Codetechno", "start-date":"2006-08-06" }, { "organizationName":"geomedia", "start-date":"2010-06-17", "end-date":"2010-01-26" }], "gender":"F" }, { "id":2, "alias":"Isbel", "name":"IsbelDull", "nickname":"Izzy", "userSince":"2011-01-22T10:10:00", "friendIds":[1,4], "employment":[{ "organizationName":"Hexviafind", "startDate":"2010-04-27" }] }, …]
Benutzerobjekte speichern verschachtelte Sammlungen mit Listen von Freunden und Arbeitsorten. Es ist möglich, die angehängten Informationen über die Arbeitsorte des Benutzers zu extrahieren, indem Sie sie mit den Daten über den Benutzer zusammenkleben, die mithilfe einer SQL ++ - Abfrage von der oberen Ebene des Objekts stammen:
SELECT u.id AS userId, u.name AS userName, e.organizationName AS orgName FROM Users u UNNEST u.employment e WHERE u.id = 1;
Das Ergebnis wird sein:
[ { "userId": 1, "userName": "MargaritaStoddard", "orgName": "Codetechno" }, { "userId": 1, "userName": "MargaritaStoddard", "orgName": "geomedia" } ]
Diese Operation wird hier ausführlicher erläutert .
Im Gegensatz zu SQL müssen in der Modellierungskomponente die eingebetteten Daten nicht in Tabellenform, sondern in Objektformat konvertiert werden. Die oben diskutierten Konzepte helfen uns dabei - ein Konzept, das durch eine Funktion und ein anonymes Konzept definiert wird. Mit einem Konzept über eine Funktion können Sie eine verschachtelte Sammlung in ein Objektformat umwandeln, und mit einem anonymen Konzept können Sie ihre Definition in die Liste der übergeordneten Konzepte einbetten und auf die Werte ihrer Attribute zugreifen, die die gewünschte verschachtelte Sammlung enthalten.
Da die vollständige Definition eines Konzepts durch eine Funktion zu umständlich ist, um als anonymes Konzept verwendet zu werden:
concept conceptName(attribute1, attribute2, ...) by function(query, mode) {...}
Wir müssen einen Weg finden, es zu verkürzen. Zunächst können Sie den Titel der Funktionsdefinition mit den Abfrage- und Modusparametern entfernen . In der Position des übergeordneten Konzepts lautet das Argument mode immer "Alle Konzeptwerte finden". Das Abfrageargument ist immer leer, da Abhängigkeiten von den Attributen anderer Konzepte in den Hauptteil der Funktion eingebettet werden können. Das Konzept-Schlüsselwort kann auch gelöscht werden. So erhalten wir:
conceptName(attribute1, attribute2, ...) {…}
Wenn der Name des Konzepts nicht wichtig ist, kann er weggelassen werden und wird automatisch generiert:
(attribute1, attribute2, ...) {…}
Wenn es in Zukunft möglich ist, einen Compiler zu erstellen, der eine Liste von Attributen aus dem von einer Funktion zurückgegebenen Objekttyp ableiten kann, kann die Liste der Attribute verworfen werden:
{…}
Ein Beispiel mit Benutzern und ihren Arbeitsplätzen als Konzept würde also so aussehen:
concept userEmployments ( userId = u.id, userName = u.name, orgName = e.orgName ) from users u, {u.employment.map((item) => {orgName: item.organizationName}).iterator()} e
Die Lösung erwies sich als etwas ausführlich, aber universell. Wenn gleichzeitig keine Transformation der Objekte der verschachtelten Sammlung erforderlich ist, kann dies erheblich vereinfacht werden:
concept userEmployments ( userId = u.id, userName = u.name, orgName = e. organizationName ) from users u, {u.employment.iterator()} e
Ergebnisse
Dieser Artikel konzentrierte sich auf zwei Themen. Zunächst die Übertragung einiger Funktionen der SQL-Sprache auf die Modellierungskomponente: verschachtelte Abfragen, äußere Verknüpfungen, Aggregationen und Verknüpfungen mit verschachtelten Sammlungen. Zweitens die Einführung von zwei neuen Konstruktionen in die Modellierungskomponente: anonyme Definitionen von Konzepten und durch eine Funktion definierten Konzepten.
Ein anonymes Konzept ist eine abgekürzte Form einer Konzeptdefinition, die als Argumente für Funktionen ( find , findOne und existent ) oder als verschachtelte Konzeptdefinition in einer where-Klausel verwendet werden soll... Es kann als analog zu anonymen Funktionsdefinitionen in funktionalen Programmiersprachen angesehen werden.
Ein durch eine Funktion definiertes Konzept ist ein Konzept, dessen Art der Erzeugung von Objekten unter Verwendung eines Algorithmus in einer expliziten Form ausgedrückt wird. Es ist eine Art "Schnittstelle" zwischen den Welten der funktionalen oder objektorientierten Programmierung und der Logikprogrammierung. In vielen Fällen ist es hilfreich, wenn eine logische Methode zur Definition eines Konzepts nicht bequem oder unmöglich ist: Zum Beispiel, um die anfänglichen Fakten aus ihren Datenbanken, Dateien oder Anfragen an Remotedienste zu laden und die universelle logische Suche durch ihre spezifische zu ersetzen optimierte Implementierung oder Implementierung beliebiger Regeln zum Erstellen von Objekten.
Interessanterweise erforderten Ausleihen aus SQL wie verschachtelte Abfragen, äußere Verknüpfungen und verschachtelte Sammlungsverknüpfungen keine wesentlichen Änderungen in der Logik der Modellierungskomponente und wurden mithilfe von Konzepten wie anonymen Konzepten und Konzepten über eine Funktion implementiert. Dies legt nahe, dass diese Arten von Konzepten flexible und vielseitige Werkzeuge mit großer Ausdruckskraft sind. Ich denke, es gibt viele weitere interessante Möglichkeiten, sie zu verwenden.
In diesem und zwei vorherigen Artikeln habe ich die grundlegenden Konzepte und Elemente der Modellierungskomponente einer hybriden Programmiersprache beschrieben. Bevor ich mich jedoch den Fragen der Integration einer Modellierungskomponente in eine Berechnungskomponente zuwandte, die einen funktionalen oder objektorientierten Programmierstil implementiert, beschloss ich, den nächsten Artikel den möglichen Optionen für ihre Anwendung zu widmen. Aus meiner Sicht hat die Modellierungskomponente Vorteile gegenüber herkömmlichen Abfragesprachen, hauptsächlich SQL, und kann ohne tiefe Integration in die Berechnungskomponente allein verwendet werden, was ich im nächsten Artikel anhand mehrerer Beispiele demonstrieren möchte.
Der vollständige wissenschaftliche Text in englischer Sprache ist verfügbar unter: papers.ssrn.com/sol3/papers.cfm?abstract_id=3555711
Links zu früheren Veröffentlichungen:
Entwerfen einer Programmiersprache mit mehreren Paradigmen. Teil 1 - Wofür ist es?
Wir entwerfen eine Multi-Paradigma-Programmiersprache. Teil 2 - Vergleich von Gebäudemodellen in PL / SQL, LINQ und GraphQL Wir
entwerfen eine Programmiersprache mit mehreren Paradigmen. Teil 3 - Überblick über Wissensrepräsentationssprachen Wir
entwerfen eine Programmiersprache mit mehreren Paradigmen. Teil 4 - Grundkonstruktionen der Modellierungssprache Wir
entwerfen eine Programmiersprache mit mehreren Paradigmen. Teil 5 - Funktionen der Logikprogrammierung