 Hallo und herzlich willkommen zum TalkTransDB-Umgebungssuche und Domain-Specific Language mit Leila und Lena. Ich bin Silvia und möchte euch vorab ein paar Hinweise zum Ablauf geben. Der Talk dauert circa 20 Minuten. Für Fragen wechseln wir im Anschluss in einen Big Blue Button-Raum. Der Link wird am Ende des Talks als QR-Code eingeblendet und lautet https doppelpunkt slash slash. Events.hexen.org slash awesome underscore adder.html. Ihr bekommt eine kurze Vorstellung von transdb.de. Ihr bekommt erklärt, wie eine Umgebungssuche ohne externe API funktioniert und was Domain-Specific Languages sind. Während des Vortrags könnt ihr Fragen auf Twitter oder Mastodon über den Account Hexen C loswerden oder im Nachgang im direkten Gespräch im Big Blue Button stellen. Soweit. Ich wünsche uns nun allen einen interessanten Talk. Guten Tag. Wir sind TransDB. Wir sind eine öffentliche Liste und Suchmaschine mit einer Umgebungssuche für Transpersonen um Anlaufstellen zu finden. Ich bin Lena, eine der beiden Entwicklerinnen bei TransDB. Und ich bin Leila, auch einer der beiden Entwicklerinnen bei TransDB. Transpersonen sind alle diejenigen, die das bei ihrer Geburt zugewiesene Geschlecht ablehnen. Hoffentlich haben diese Menschen den Wunsch, eine Transition zu machen, also sich ihrem eigentlichen Geschlecht anzupassen. Meistens fängt das mit therapeutischer Unterstützung und Kontakt und Selbsthilfegruppen an. Im weiteren Verlauf möchten manche Transpersonen medizinische Maßnahmen durchlaufen. Diese können zum Beispiel Hormonbehandlungen, Stimmtraining oder diverse Operationen umfassen. Viele Transpersonen haben Schwierigkeiten, Anlaufstellen und Arztpersonen zu finden. Das liegt einmal daran, dass Transpersonen eine Randgruppe sind, für die es recht wenige Anlaufstellen gibt. Und dass im medizinischen Bereich viel Transphobie und Diskriminierung gibt. Deshalb haben wir eine Liste mit Positivempfehlungen erstellt. Unser Konzept ist es, deutschlandweit möglichst flächendeckende Angebote bereitzustellen. Das machen wir, indem unsere Liste auf Community-Inhalten basiert, die von allen möglichen Transpersonen eingereicht werden. Das funktioniert spezifisch so, dass Leute auf unserer Website über ein Formular neue Einträge einreichen können. Diese Einträge werden dann von unserem Moderationsteam überprüft und freigeschaltet. Nach der Freischaltung können Nutzer auf unserer Seite die Einträge suchen und filtern. Auf der technischen Seite haben wir das Ganze auf View mit Nuxt.js aufgebaut, aber sind zurzeit dabei das Ganze auf Zwelt mit Zweltkit zu ändern. Als Programmiersprache verwenden wir im neuen Frontend sowie im Backend TypeScript. Unser Backend ist auf Express.js aufgebaut und mittlerweile sind wir dabei, das Ganze noch mit Overnight.js Objektorientier zu machen. Als Datenbank-System haben wir uns für MongoDB entschieden, weil es ja einfach flexibel und performant ist. Zum Hosten nutzen wir Docker-Compose, weil es das Hosting sehr einfach und auch sicher macht. Des Weiteren nutzen wir GitHub, um unseren Code öffentlich einsehbar zu machen und GitHub's Continuous-Deployment, um unsere Anwendung zu kompilen. Jetzt erzählen wir noch etwas von der technischen Seite in Form von zwei Minitalks. Zuerst erzählt euch Lena etwas über unsere Umgebungssucher. Danach erzählt euch Leila etwas über Domain-Specific-Languages. Ich erzähle euch jetzt ein bisschen etwas über die Umgebungssuche, die wir auf unserer Webseite haben, wie wir das implementiert haben und was wir dabei alles beachten mussten. Zuerst einmal, warum brauchen wir überhaupt eine Umgebungssuche? Das Ganze hat den Vorteil, dass Nutzer auf unserer Seite mit wenigen Klicks passende Einträge finden. Natürlich könnten wir das Ganze auch nach Bundesland filtern lassen, aber das ist sehr, sehr ungenau. Deswegen haben wir uns dazu entschieden, eine richtige Umgebungssuche einzubauen, weil das auch einfach die User-Experience insgesamt verbessert. Schauen wir uns doch erstmal an, wie das Ganze funktioniert. Wir haben auf unserer Webseite hier oben im Banner so ein Button mit Umgebungssuche. Darüber kann der Nutzer seinen Standort teilen und dann sehen wir schon auf der linken Seite, wo der Standort erkannt und unter jedem Eintrag ist die entsprechende Entfernung. Wenn ein Nutzer seinen Standort nicht freigeben möchte, bieten wir natürlich auch noch die Suche nach einem Ort oder einer Postleitzahl an. Das funktioniert im Grunde genauso. Um so eine Umgebungssuche zu bauen, haben wir auch gewisse Anforderungen gehabt und zwar soll das Ganze lokal und selbst gebaut sein. Denn X-Tier und API sind kostenpflichtig und haben Rate Limits und da wir das Ganze als ehrenamtliches Projekt machen, haben wir nicht die finanziellen Mittel, da jetzt ein API zu bezahlen und weil wir auch nicht wissen, wie viele Nutzer am Ende das Ganze nutzen, können wir auch nicht so wirklich Rate Limits abschätzen. Ein anderer wichtiger Punkt dabei ist Datenschutz. Denn wenn wir einen X-Tier-Anbieter nutzen, würden wir die ganze Zeit die Koordinaten unserer User zu irgendwelchen Diensten schicken und das ist halt nicht so datenschutzfreundlich. Deswegen wollen wir das Ganze lieber bei uns auf unseren Servern behalten. Für das Ganze benötigen wir natürlich die Geodaten für die eigentlich Umgebungssuche. Nutzen wir da OpenGeoDB. Das ist so eine offene Datenbank mit Ortsnamen und Postelzahlen in Deutschland und Umgebung, die man einfach so kostenlos runterladen kann. Eine X-Tier-Anbieter ist dann doch noch drin, und zwar nominär Team von OpenStreetMaps. Das ist allerdings nur dafür da, um die Adressen von neuen Einträgen in Koordinaten umzuwandeln. Schauen wir uns mal an, was OpenGeoDB uns so an Daten liefert. Dafür sehe ich hier so eine schöne Tabelle. Das ist einfach so eine CSV-Datei, die sich auf der Webseite von OpenGeoDB runterladen lässt. Und da haben wir im Grunde genau die Informationen drin, die wir brauchen, nämlich die Koordinaten, Postelzahlen, Ortsnamen und so einen ganzen Kram. Diese Daten haben wir allerdings nicht eins zu eins in unsere Datenbank importiert, sondern ein kleines Skript geschrieben, um das Ganze noch ein bisschen umzuformatieren. Denn manche Felder davon benötigen wir überhaupt nicht und somit könnten wir Speicherplatz sparen. Und die Koordinaten brauchen wir als GeoJacePoint, damit das in unser MongoDB vernünftig funktioniert. Nachdem wir das importiert haben, sieht das jetzt in unser MongoDB so aus. Das sind ungefähr 60.000 Dokumente. Da sehen wir dann auch die ganzen Felder, die wir brauchen. Es sind nicht mehr so viele wie es vorhin in der Tabelle waren, sondern angepasst, damit wir ein bisschen Speicherplatz sparen können. Da ist jetzt auch dieser GeoJacePoint drin und Name und Postelzahlen und so. Jetzt, wo wir die Daten in der MongoDB haben, können wir ein 2D-Sphere-Index benutzen, um die GeoJace-Punkte zu indexieren. Denn das bringt uns nachher bei den Anfragen einen großen Performance-Vorteil. Diesen Index legen wir sowohl in der Collection an, wo die OpenGeoDB-Daten liegen, weil da ja Koordinaten drin sind, als auch bei unseren Entries, der dir ebenfalls Koordinaten für ihren Standort haben. Nachdem wir das jetzt implementiert haben, können wir an unsere API eine Anfrage schicken, wo wir einen Suchtext mit einem Ortsname oder Postelzer hinschicken und rechts in der Responsibility dann kriegen wir die entsprechenden Ergebnisse zurück. Wenn wir jetzt Einträge sortieren wollen, gibt es da zwei verschiedene Fälle, wie wir die Geodaten verwenden. Im ersten Fall startet der Nutzer eine Suche mit den Koordinaten. Da nutzen wir dann die Geodaten, um die Koordinaten aufzulösen und einen Ortsname zu erhalten. Danach werden die Einträge nach den Koordinaten sortiert und wir hängen den Ortsname an die Response ran. Das machen wir dann mit auf der Website nachher in der Sidebar, der erkannt Ortsname steht, einfach aus User Experience gründen. Im zweiten Fall startet der Nutzer eine Suche nach einem Ortsname. Da müssen wir den Ortsname damit Hilfe unserer Geodaten auflösen und die Koordinaten dafür zu erhalten. Mit diesen Koordinaten können wir dann die Einträge sortieren und den Ortsname hängen wir ebenfalls an der Response wieder mit ran. In der Praxis sieht das jetzt so aus, dass wir ein Webrequest an unserer API machen, wo wir dann im QueryString Letitude und Longitude mitgeben können. Rechts in der Response sehen wir dann ein Array mit Entries. Jeder Entry hat ein Distance Feld. Da ist die Distance in Kilometern drin. Ganz unten ist ein Location Name Feld, wo der erkannte Ortsname drin ist. Natürlich hat das Ganze auch Limitierungen, hauptsächlich von OpenGODB, denn das ist so eine Sache mit der Verfügbarkeit. Im letzten Jahr war OpenGODB nicht immer zu erreichen. Also die Downloadseite war teilweise offline. Wir haben zum Glück noch einen recht aktuellen Stand. Das soll jetzt auch bald wieder online kommen, aber das ist halt auch ein Punkt, der ein bisschen kritisch ist, was natürlich dann auch dafür sorgt, dass wir das nicht immer aktuell halten können. Eine andere Sache ist die länderspezifische Verfügbarkeit. Denn OpenGODB deckt nicht die ganze Welt ab. Das könnten wir auch gar nicht in unseren Datenlang importieren. Deswegen ist die Suche aktuell nur in Deutschland verfügbar. Ja, so funktioniert unsere Umgebungssuche bei TransDB. Ich hoffe, ich konnte euch darüber einen guten Überblick geben. Sollten noch Fragen aufkommen, könnte die gerne noch im Q&A nachherstellen. Und jetzt erzählt euch Leila noch etwas über domain-specific Languages. Guten Tag, ich bin Leila. Ich bin eine der Programmierenden von TransDB. Und ich möchte euch heute ein Feature vorstellen und ein bisschen darüber reden, wie wir das gemacht haben. Das ist ein Feature, das man jetzt im Frontend als Endnutzer nicht die ganze Zeit sieht. Das ist für unser Management-Team da. Und zwar ist es eine domain-specific Language, die wir im Management-Backend nutzen. Da jetzt erstmal dazu, warum wir eine domain-specific Language haben. Also das Problem ist, wir haben hunderte von Einträgen in unserer Datenbank, die unser Management-Team effizient durchsuchen muss. Und da gibt es jetzt natürlich verschiedene Lösungseinsätze. Wir könnten irgendwie eine Datenbankquery versuchen zu exposen. Der ist aber ein bisschen gefährlich. Der Datenbankquery ist ja immer ein bisschen mehr machen können, als nur Daten abrufen. Und die auch sehr ineffizient sein können, bzw. sehr absichtlich ineffizient gemacht werden. Das ist allgemein ein bisschen ein Security-Locker, selbst wenn es für das Management ist. Der andere Faktor ist natürlich, dass Datenbankquery sehr komplex sind. Und jetzt nichts, was man zwingend nicht so technischen Personen mehr zumuten kann. Die andere mögliche Lösung für dieses Problem wären Filterfelder einzubauen. Nur ist da die Sache, dass es halt einschränkend anhand von welcher Filterfelder wir jetzt implementiert haben als Developer. Da kann man jetzt nicht zwingend alles mit filtern. Und wenn man mehr gefiltert werden muss oder neue Felder hinzugefügt werden, dann müssen die auch immer bei uns im Frontend erweitert werden. Und das macht es halt schwer zu erweitern. Das haben wir da geguckt, ob es da noch weitere Lösungen gäbe. Und eine unserer Inspirationen war die Filterfelder von Discord. Discord hat hier oben rechts in der Ecke von jedem Chat so ein Filterfeld, das sehr leicht zu nutzen ist. Der ist ein schönes Autokomplitat, was es auch so leicht zu nutzen macht, mit dem man wirklich alles filtern kann, unabhängig von irgendwelchen spezifischen Filterfeldern. Und das ist sprachunabhängig. Also wenn man es jetzt auf Englisch oder auf Deutsch hat, dann ändert sich hier auch die Sprache. Nur gab es nichts, was man jetzt so als fertiges Paket einbinden könnte. Und deshalb haben wir unsere eigene Sprache gemacht. Sprache bedeutet hier jetzt was ziemlich allgemeines, das ist jetzt keine Programmiersprache, sondern eine Filtersprache. Und diese Filtersprache ist jetzt nur ganz spezifisch für einen ganz bestimmten Zweck, was Englisch übersetzt domain-specific heißt. Da ergibt sich auch der Name domain-specific-language her. Ich möchte jetzt mal zeigen, wie das Ganze bei uns aussieht. Jetzt einmal hier wechseln. Das ist bei uns die Managementoberfläche. Und hier oben ist bei uns das Filterfeld. Wie ihr sehen könnt, haben wir jetzt nicht so ein Riesenhaufen Filterfelder, sondern nur dieses eine Feld. Und da kann man jetzt Dinge eingeben, wie freigeschaltet von Leila. Dann sieht man alle Dinge, die ich freigeschaltet habe. Und ich möchte jetzt auch noch nur Dinge, die Gruppen und Vereine sind. Und jetzt habe ich nur hier Gruppen und Vereine, die von uns freigeschaltet wurden. Ja, dieser Filter ist halt sehr versett hier, sehr dynamisch, was man da alles eingeben kann. Und man kann auch immer mehr dranhängen. Ja, die Ziele unserer Sprache sind einmal die Sicherheit durch das Go, also durch was wir zulassen, dass wir das sehr stark auf unseren Zweck anpassen können. Und das ist für unser Management-Team sehr leicht nutzbar. Das könnte man jetzt auch allgemein ausweiten, auf warum man unbedingt eine domain-specific-language haben würde. Also das sind auch allgemeine Gründe für eine domain-specific-language, nicht nur unsere Gründe. Beispiele für andere domain-specific-languages wären zum Beispiel Excel. Wenn man in Excel einfach ein Feld kann man eine Formel eingeben. Das ist quasi eine eigene Sprache. Und ein anderes Beispiel wäre im Spiel Overwatch. Da kann man ja eigene Game-Mode-Skripten und die haben das in erster Linie für die Sicherheit gemacht. Weil der Code, den man das skripten kann, auf deren Servern läuft, der wollen ja natürlich nicht alles zulassen. Wie man eine eigene DSL angehen kann, ist jetzt auf drei verschiedene Weisen. Man kann die entweder interpretiert machen, wo der Code beim Einlesen ausgeführt wird direkt. Man könnte sie kompilieren, wo der Code der Sprache zu Bytecode verwandelt wird und später meistens auf einer virtuellen Maschine ausgeführt wird. Oder man könnte es transpilen, wo man seinen eigenen Syntag zu einem anderen Sprache übersetzt. Und da wir im Hintergrund schon eine Datenbank haben, die einen Syntag hat, ist das derangehensweise, die wir gewählt haben. Wie man jetzt so eine transpilte Sprache aufbaut, die besteht noch mal aus verschiedenen Segmenten. Ich erkläre diese Segmente später nochmal genau anhand eines Beispiels. Zuerst ist das Alexa, dessen Aufgabe es ist, die Token zu erstellen. Dann haben wir den Passer, dessen Aufgabe es ist, ein abstraktes Zwischenformat zu erstellen. Das abstraktes Zwischenformat könnte man nutzen, um es zu optimisen. Wir verlassen uns allerdings größtenteils auf den Optimizer der Zielsprache, also der Query von MongoDB. Und dann haben wir den Compiler, welcher dann zu der Zielsprache, also der MongoDB Pipeline führt. Das Ganze packen wir jetzt hier mal in unsere Maschine, um das mit einem Beispiel durchzugehen. Das ist Logopaden-Hatch-Website. Und packen wir jetzt mal in unsere Maschine rein und gucken dann, wie das aussehen würde. Als Erstes kommt das Ganze hier im Lexa an. Und die Aufgabe vom Lexa ist es, diesen Input-Text in einzelne Tokens zu unterteilen. Das ist jetzt unser Input-Text. Und ein Token ist quasi eine Art Wort. Also es ist so eine Abtrennung und die verschiedenen Faktoren abgetrennt werden kann, wie es unterteilt wird. Das ist der erste Token ist und wir suchen uns jetzt nicht nur den Token raus, sondern der packt da auch noch verschiedene Informationen mit dran. Und zwar einmal den Inhalt, natürlich das ist. Dann die Position im Input. Das ist für zum Beispiel autocomplete wichtig. Und dann noch, ob es ein Filter sein könnte. Wir haben jetzt hier, das hier ist immer ein Filter und das hier ist ein Wert. Die Sprache muss ja wissen, was ein Filter und was ein Wert ist. Das ist der Lexa, der rät das nur. Denn das lässt sich nicht mit kompleter Sicherheit sagen. Man könnte natürlich auch nach Werten suchen, die einen Doppelpunkt beinhalten. Und die wären dann keine Filter. Das ist aber etwas, was die Aufgabe des Parsas ist. Der Lexa unterteilt wirklich nur die Worte und tagt die. Die weiteren Tokens, die der Lexa erstellt, ist dann Logo Paladin, Hat und Website. Und die packen wir dann in eine Array und geben das weiter an den nächsten Schritt, und zwar den Parsa. Der Parsa liest diese Tokens jetzt ein und der hat dazu den Kontext. Und der bekommt auch noch von außen eine Sprachdefinition, die jetzt bei uns im Front entliegt, die halt definiert, welche Felder es gibt, was ein Filter ist. Und nutzt diesen Kontext und die Reihenfolge, um die Begriffe zu übersetzen und ein abstraktes Format, das wir hier unten sehen, zu erstellen. Auch noch packt der ein Autocomplete String, weil der weiß ja aus dem Kontext, was möglich ist. Und sagt quasi alle Sachen, die von hier aus an möglich sind, gibt er als eine Liste, als Autocomplete zurück nach oben an das Filterfeld. Das wäre jetzt der erste Token, der verwandelt werden würde, in der Sprachdefinition steht, dass dieses Istfeld ein Compair-Feld ist. Das ist das Datenbankfeld Type kompären soll und dass es eine Equal2-Comparison machen soll. Also zum Beispiel Zahlen lassen sich auch greater than, smaller than, kompären und es gibt auch Not Equal2. Und das hier ist jetzt der Wert, der erkannt wurde anhand, dass wir hier einen validen Filter haben, der hier reingepackt wurde. Das ist auch noch includes. Includes bedeutet einfach, dieses Feld muss vorhanden sein bei den Sachen, die gefiltert werden und das ist der Name des Feldes, das vorhanden sein muss. Das wird auch alles anhand der Sprachdefinition gemacht und noch eine wichtige Sache am Pasa ist, dass der Pasa nicht versagt. Wenn der Pasa irgendeinen invaliden Input hat, wenn zum Beispiel etwas das aussieht wie ein Filter, aber kein valider Filter ist, dann wird das nach Text Filter an die Datenbank weitergegeben. Das ist damit man im Frontend als Endnutzer nicht die ganze Zeit irgendwie Error-Messages hat, sondern etwas möglichst sinnvolles zurückgibt und man kann ja anhand der Autocomplete sehen, ob das ein valider Input schon ist, den man da hat. Das Ganze wird jetzt in ein Objekt gepackt, das ist dann unser abstraktes Format und geht weiter in den nächsten Schritt und zwar den Compiler. Das ist die Aufgabe, dieses abstrakte Format einmal zu validieren und dann zu übersetzen. Und der macht auch noch ein bisschen Optimization anhand der Reihenfolge. Also zum Beispiel diese Includesfilter kommen immer zuerst. Wir sehen können, das hier ist jetzt eine MongoDB Pipeline. Das Includesfeld sagt quasi, das Websitefeld darf nicht leer sein. Das kommt immer zuerst, weil wenn das Websitefeld leer ist, dann wird die Reihenfolge noch leichter gemacht werden. Genau, und hier ist diese Comparison. Da sagen wir jetzt, das Typefeld braucht einen Comparison Regex. Und keine Sorge, hier ist keine Injection möglich, das Regex wird komplett escaped. So, und diese Pipeline geht jetzt weiter an die Datenbank und die Datenbank gibt uns dann die gefilterten Werte zurück. Jetzt bleibt ein Endeffekt nur noch eine Frage offen. Wo senden wir das Ganze vom Frontend zum Backend? Also hier, das ist jetzt der Vorteil, dass das sehr leicht zu validieren ist und damit auch sicher. Wir können einfach gucken, ist das Text und dass die Domain-Specific Language diese Implementation an einem Ort liegt. Die brauchen wir gar nicht im Frontend. Der Nachteil ist, dass wir Sprachdateien im Backend haben, das wollen wir eigentlich vermeiden, weil wir alle Sprachdateien schon an einem Ort im Frontend haben möchten, falls wir die Sprachdateien im Ende mal irgendwie ändern wollen. Und dass es autocomplete langsam ist, weil immer, wenn der Pasa ein Autocomplete-Liste hat, ganz nach oben zurück an das Frontend senden muss. Und das hat natürlich ein Netzwerk-Delay. Das ist ein bisschen unschön zu nutzen. Eine nächste Option wäre vor der Datenbank. Das ist eine schreckliche Idee, weil da die ganze Sicherheitsaspekte eigentlich im Sprachhaus im Fenster flöten geht, da könnte man dann direkt eine Pipeline reingeben. Der einzige Vorteil hier ist jetzt, dass es leichter zu nutzen ist, aber die Sicherheit ist weg, weil wir die Validierung sowieso machen müssten. Aber der Vorteil ist natürlich, dass wir die Domain-Specific-Länge im Frontend haben und die Sprachdateien jetzt auch im Frontend haben. Was wir am Ende gemacht haben, ist zwischen Compiler und Pasa, also das abstrakte Format rüber senden. Das wird ja einmal vom Compiler validiert, aber das kann auch so validiert werden von anderer Validierungssoftware. Das Autocomplete ist trotzdem noch sehr schnell, weil das allein nicht im Frontend setzt und die Sprachdateien sind es nur im Frontend, weil dieses abstrakte Format ja schon die übersetzten Backendfelder hat und nicht dieses hat hier ist Logopaden, das ist ja Deutsch, das nutzen wir das im Backend nicht, aber das wurde ja hier schon im Frontend jetzt übersetzt. Der Nachteil ist, dass wir diese Domain-Specific-Lengauge dann im Frontend und Backend brauchen, aber dass wir das jetzt als eigenes Paket haben, ist das jetzt kein so großes Problem. Ja, das ist, was wir im Endeffekt verwendet haben. Ich bin da jetzt ein bisschen durchgedüst, also falls ihr noch Fragen habt, wir haben jetzt noch ein Q&A im Big Blue Ratten Raum könnt ihr mich die gerne fragen. Danke fürs Zuhören und noch einen schönen Tag. Ja, vielen Dank Lina und Leila. Total Klasse, wie ihr das gelöst habt und obendrein hilft ihr vielen Transgender-Menschen damit enorm weiter. Jetzt geht es weiter mit den Q&A. Wir wechseln dazu in einen Big Blue-Button-Raum. Die Adresse des Raumes ist eingeblendeten QR-Code hinterlegt und lautet htgps www.slashslash wenz.hexen.org slash orsam.anderscore.ader.html Bis gleich.