Der Datenbank-Server (Backend) ist nach langen zwei Wochen Recherche und Entwicklung endlich eingerichtet. Es hat viel Rechenleistung und Zeit für das Unterfangen gebraucht, daher nun ein Update zum Fortschritt.

Es fing alles mit einer drastischen Kursänderung im Bezug auf meine Datenbankstrategie an. Mein Ziel war es vollkommen auf Firebase (einen, der Google Datenbank Services) zu setzen. Leider musste ich feststellen, dass übliche SQL-Querys auf dem NoSql Datenbankanbieter nicht funktionieren. Speziell leiden würde die Suche unter diesem Umstand. Was ich damit genau meine, stelle ich im folgenden Beispiel dar:

MySQL APPHOVEN-DB

Composer-Name: Mozart, Wolfgang Amadeus

Composer-Name: Beethoven, Ludwig van

Ich möchte mit dem Üben eines neues Stückes anfangen und gebe daher den Namen des Komponisten in die Apphoven-Suche ein: „Mozart“ – Leider kein Ergebnis… Weshalb? Weil ich nicht genau „Mozart, Wolfgang Amadeus“ eingegeben habe… Ich versuche es mit „Ludwig van Beethoven“ – Keine Chance, ich muss genau „Beethoven, Ludwig van“ eingeben… Schlimmer noch: Ein User wird nicht wissen, wie die Namen in der Datenbank gespeichert sind. Daher ist eine Suche unmöglich.

Wirklich schade also, dass meine Strategie nicht aufgeht. Nichtsdestotrotz muss diese Funktionalität umgesetzt werden. Und zwar auf intelligentere Weise, als es Firebase ermöglicht. Denn für Suchanfragen, die von Menschen auf eine Firebase Datenbank ausgeführt werden, ist der Service leider untauglich. „Back to the routes“ zu einer 30 Jahre lang bewährten Technologie: MySQL. Jeder, der mal mit PHP rumgespielt hat, wird MySQL sehr wahrscheinlich kennen, denn irgendwo müssen die Daten effizient gespeichert werden. MySQL (Inno-DB) ist eine Datenbank, die weit verbreitet ist und kontinuierlich entwickelt wird. Ausserdem erhält man per PhpMyAdmin ein einfaches Webinterface um die Datenbank über den Webbrowser einzusehen und gegebenenfalls Änderungen vorzunehmen. Besonders an MySQL (und da konnte Firebase eben – noch – nicht mithalten) sind die verschieden Möglichkeiten der Query. Zurück zum Beispiel: Um den Eintrag von „Mozart, Wolfgang Amadeus“ zu finden muss ich nur eine Query mit folgender Suchanfrage absenden: LIKE ‚%Mozart%‘. Die MySQL-Datenbank wird darauf hin auf „Mozart“ durchsucht. Es spielt hierbei keine Rolle was vor „Mozart“, oder danach („Wolfgang Amadeus“) steht, denn dieser Text wird durch die Platzhalter „%“ ersetzt. Eine Suche wird dadurch schnell und einfach möglich. Der Nutzer kann eintippen was er weiss und die Suche wird mögliche Resultate wie den gewollten „Mozart, Wolfgang Amadeus“ ausgeben.

Soweit kannte ich mich mit MySQL bereits aus. Rückblickend ist bisher nur ein Tag von zwei Wochen intensiver Arbeit vergangen.

Doch was genau ist das Ziel der Datenbank?

Wieso muss der User nach Begriffen suchen können (und kann sie beispielsweise nicht selbst eintippen)?

Ganz einfach: Ich will die bestmögliche UX (User-Experience garantieren). Denn wenn ein Nutzer Apphoven herunterladen sollte, wird einer der ersten seiner Aktionen das Hinzufügen des Stückes sein, welches er gerade übt. Auch längerfristig gesehen, muss der User (jedes Mal wenn er mit dem Üben eines neuen Stücks anfängt) dieses Prozedere durchlaufen. Damit er nicht jedes Mal den Komponisten und das Stück manuell eingeben muss, hilft ihm die Suche (=> die Datenbank) dabei:

Mozart? Direkt gefunden:

Mozart?

Direkt gefunden

Mozart, Wolfgang Amadeus. Geboren 1756, Gestorben 1791. 626 Stücke. Komponist der Wiener Klassik.

Gut, und nun das Stück. Wie hiess das Stück nochmals? Kleine Nachtmusik? Eine kleine… ? Hmm, ich suche einfach „Nachtmusik“. Kein Problem, direkt gefunden:

Eine, äh... "Nachtmusik"?

Direkt gefunden

„Eine kleine Nachtmusik“, K.525, 4 Sätze: I. Allegro, II. Romanze: Andante, III. Menuetto: Allegretto, IV. Rondo: Allegro. Komponiert wurde das Stück 1787 und es ist in G-Dur, ein klassisches Stück.

Nicht schlecht oder? Vor allem: dies sind tatsächliche Ergebnisse der App (verfügbar für einen Grossteil von bekannten Stücken und Komponisten). Und definitiv besser als die manuelle Eingabe des Stücks war diese informative Suche bestimmt. Noch besser ist aber, dass mir dieses System eine eindeutige Identifikation (ID) der Stücke ermöglicht. Ohne die Datenbank würde jeder Nutzer einen anderen Namen für den Komponisten und einen anderen Titel für das Stück eingeben. Einer spielt „Nachtmusik“ von „Mozart“, der andere spielt „Die kleine Nachtmusicc“ von „Wolfgang Amade Mozarrt“ (oh Gott, Schreibfehler!), noch ein anderer spielt „eine kleine nacht musik“ von „wolf gang amad. mozart“. Alles redundante Informationen, die Speicherplatz fressen, unpräzise und vor allem nicht weiterverwertbar sind.

Die Festlegung auf einen Mozart und eine Nachtmusik ermöglicht mir nämlich noch einen weiteren, interessanten Aspekt: Statistik! Ich werde auswerten können, welche Stücke am meisten gespielt werden, welcher Komponist derzeit der beliebteste ist, wie lange die User durchschnittlich benötigen um ein Stück zu erlernen etc.! Doch nun zur Frage, die das Hauptproblem des ganzen Unterfangens ist:

Woher kommen die Daten über Komponisten und Stücke?

Antwort: Von der Internationalen Musikstück Datenbank (auch Petrucci oder IMSLP gennant). Das beste daran: Die Petrucci-Datenbank verfügt sogar über eine API, die Entwicklern Zugang zu Informationen Stücken, Komponisten und Herausgebern gewährt. Tolle Sache, jeder kann sich für einen solchen Zugang anmelden, sogar kostenlos für schulische oder non-profit Projekte.

Daher habe ich eine freundliche und ausführliche Anfrage geschickt mit der Antwort:

Nein, sorry. Wir entwickeln gerade selbst eine Android-App und vergeben daher keine Accounts für andere Android-Apps.“ (Sinngemässes Zitat)

Und das war sehr schlecht. Ich habe den Inhaber erneut kontaktiert mit dem Versuch klarzustellen, dass meine App kein Klon von der eigentlichen Petrucci-Datenbank sein wird (das Hauptziel dieser ist nämlich das Sammeln und zu Verfügung stellen von Notenmaterial – sprich: den Notenblättern). Lediglich die Meta-Informationen zu Komponist und Stücken sind für mich von Interesse.

Keine Antwort mehr seither. Sehr, sehr schlecht. Mir ist aber aufgefallen, dass die Seite über das Opensource CMS Mediawiki betrieben wird. Ein CMS welches von sich aus eine API besitzt. Übrigens ist der Inhalt der Webseite zur Weiterverwendungen (sogar kommerziell) lizensiert. Daher habe ich einige Stunden lang ausprobiert, verschiedene Querys (Suchanfragen) ausprobiert. Ich bin auch oft gescheitert. Dennoch konnte ich nach Tagen eine gute Liste von über 14’000 Komponisten zusammentragen. Über diese versuchte ich dann an die Stücke zu kommen. Vergeblich: Denn die gelisteten Stücke (auf der Petrucci-Webseite eines Komponisten) waren eigentlich „nur“ Werke. Also keine Songs, sondern Alben.

Wer mit dem Üben eines Stückes anfängt, übt jedoch nicht gleich ein ganzes Album sondern erst ein Stück. Daher waren die ca. 115’000 Records (= MySQL-Datenbank-Einträge), die ich gesammelt hatte wertlos. Ein letzter Versuch: die Petrucci-API (das ist nicht die Mediawiki-API, über welche ich die Komponisten-Namen sammeln konnte, sondern die API, zu der mir der Zugang verwehrt wurde) besitzt zwei öffentlich frei zugängliche Subsets der API: Eine Sammel-Liste von Komponisten, Herausgebern, Editoren etc. (die war für mich nicht interessant, da ich nur die Komponisten benötige, diese aber von Herausgebern und Editoren der API nicht zu unterscheiden sind) und eine Liste mit Stücken. Diese Liste ist… ziemlich gross.

Nicht Hundertausend, eine Million oder ein dutzend Millionen. Nach einer ersten Analyse habe ich den Wert auf ca. 115’000’000 Einträge bestimmen können. Logischerweise konnte ich vorher nicht wissen, was in diesen 115 Einträgen steht – Alben / Sammlungen / Stücke? Ich habe ein erstes Skript geschrieben um die Einträge der Petrucci-Datenbank in meine MySQL-Datenbank zu speisen. Erste Berechnungen ergaben, dass dieser Transfer jedoch über dreieinhalb Monate (!) dauern würde. Das war deutlich zu lange und das Risiko zu hoch, dass sich die Daten kurz vor Abgabe der Arbeit (= der App) als unbrauchbar erweisen würden. Ich habe bei der Datenmenge auch mit massiven Performance-Problemen zu rechnen… Fest steht: Der Transfer muss beschleunigt werden, und zwar drastisch. Skript optimieren und Geschütze voll auffahren: Aufstocken der Serverkapazität von 512 MB RAM und einem CPU-Kern auf 16 GB RAM und acht CPU-Kerne. Ausserdem: Anschliessen einer 200GB SSD an den internen Speicher.

Wie das Feuerwerk ausgeht, dazu im nächsten Blogartikel mehr. Ausserdem wird noch geklärt werden, wie diese Masse an Daten in solch kurzer Zeit kopiert werden kann. Von Hand wohl eher kaum, und die Mediawiki API bietet mir nur Zugriff auf die Webseite als wäre ich ein regulärer Besucher der Webseite, also keine klaren Werte, keine systematische Sortierung und Darstellung: Eine äusserst knifflige Situation.

Übrigens werde ich (vorerst) keine Informationen zur genauen Datenbankstruktur veröffentlichen. Ich habe mich dazu entschieden die Datenstrukturen unter Verschluss zu halten, um möglich künftige Angriffe zu vermeiden. Sollte ich jetzt Namen von Datenbanken, Tabellen und Zeilen verraten, ist es Angreifern deutlich einfacher einen Diebstahl zu tätigen. Im nächsten Blogpost werde ich evtl. Auszüge des Parser-Skripts präsentieren, um dennoch Einblicke für Interessierte zu ermöglichen.

[Nachträgliches Update]

Das dargestellte Konzept zur Suche der Komponisten / Stücke und dessen Resultaten wurde schlussendlich nicht in die finale Release-Version der App mitaufgenommen. Ich habe mich dagegen entschieden, da es zu Verwechslungen zwischen der „Stück-Hinzufügen“-Komponente (Stück suchen und zur Übeliste hinzufügen) und der weiteren Komponente des vorgestellten Konzepts kommen könnte.

Die Informationen (wie sie im Beispiel gezeigt werden) bleiben dennoch auf den Servern gespeichert. Gut möglich, dass man eine erweiterte Ausgabe der Information (wie es dieses Konzept vorschlägt) in einem künftigen Update umsetzt.