Kombination aus Datum und Tag (Label) persistieren

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
paedubucher
Beiträge: 855
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Kombination aus Datum und Tag (Label) persistieren

Beitrag von paedubucher » 31.08.2019 12:23:28

Für ein kleines Prototyp-Projekt, das Self-Tracking beinhaltet, möchte ich pro Tag (Tagesdatum) mehrere mögliche Tags abspeichern können, also etwa folgendermassen:

Code: Alles auswählen

2019-08-31: Sonne, Bier, Spaziergang, Freizeit
2019-09-01: Besuch, Essen, Rotwein, Kaffee
2019-09-02: Kursbesuch, Lärm, Schlafmangel, Käsekuchen
Die Tags ("Sonne", "Bier", usw.) kommen aus einer endlichen aber erweiterbaren Menge, und ihr Auftreten an mehreren Tagen soll verfolgt werden können ("Wie oft wurde der Tag 'Rotwein' insgesamt verwendet?" wäre ein möglicher Anwendungsfall. Weiter benötige ich eine Möglichkeit, alle bisher verwendeten Tags schnell herauslesen zu können.) Nun frage ich mich, welche Speichertechnologie ich dafür verwenden soll.

Ein Verzeichnis mit einer Datei pro Tag (Dateiname = Datumsangabe) und einem Tag pro Zeile wäre natürlich eine sehr einfache Variante. In meinem Prototyp-Projekt geht es aber eher um den Web-UI-Aspekt, und weniger um die Persistenz. Ich möchte mich also möglichst wenig mit der Persistierung beschäftigen, und hätte am liebsten einen Service, den ich mit Docker Compose unter einem bestimmten Port reinhängen und dann damit lospersistieren könnte. (Es schadet auch nicht, wenn ich dabei eine neue Technologie kennenlerne.)

Eine relationale Datenbank wäre recht gut geeignet, da man die Tags in einer Tabelle und die Zuordnung von Tagesdatum zu Tag in einer Beziehungstabelle abbilden könnte. Die Zugriffe sollten dann mit SQL einfach und schnell vonstatten gehen. Leider benötige ich hier eine statische Tabellenstruktur, die beim ersten Aufstarten erzeugt und bei Updates angepasst werden müsste. Das verursacht bereits weitere Komplexität, auf die ich lieber verzichten würde. Ausserdem wäre hier der Lerneffekt eher klein, zumal ich schon seit vielen Jahren mit SQL und relationalen Datenbanken arbeite.

Mongo DB oder eine sonstige Dokumentdatenbank wäre da eine Variante, die ich schon einmal für ein anderes Projekt verwendet habe. Pro Tag könnte ich ein Dokument anlegen, und darin einfach ein Array von Tags ablegen. Der Lookup nach Tagen wäre dann sehr einfach, aber für das Auslesen aller Tags müsste ich wiederum alle Dokumente einlesen.

Schliesslich wäre da noch die Variante Key-Value-Store, wie z.B. Redis. Hier habe ich noch gar keine Erfahrungen, und wüsste auch nicht, wie ich das am besten modellieren sollte. Das Tagesdatum als Key und die Tags in einem Array als Value? Oder pro Datum-Tag-Kombination (Key) ein Boolean (Value), ob diese Kombination existiert?

Oder sollte ich die Sache gar umdrehen, und pro Tag eine Liste von Daten speichern? Das würde den Lookup vereinfachen, aber die Datenerfassung, die täglich passiert, etwas schwieriger machen.

Code: Alles auswählen

Spaziergang: 2019-08-03, 2019-08-20, 2019-09-01
Bier: 2019-08-12, 2019-08-15
Schlafmangel: 2019-08-07, 2019-08-17, 2019-08-24, 2019-09-01
Der Zielkonflikt wäre also: Einfache Datenerfassung vs. einfache Datenauswertung.

Lösungsvarianten wären genügend da, und umsetzen kann man es für die bescheidene Datenmenge (für den Prototyp) wohl mit jeder Technologie problemlos. Am interessantesten wären natürlich verschiedene Implementierungen, die man dann miteinander vergleichen könnte, aber dazu fehlt mir dann doch die Zeit.

Was habt ihr für Erfahrungen und Ratschläge betreffend Datenmodellierung und Speichertechnologie? Meine Hauptkriterien sind Einfachheit in der Handhabung (möglichst wenig selber Programmieren, kein Setup) und Eleganz (was die Datenmodellierung gemäss Problemstellung betrifft). Der Lerneffekt (Key-Value-Store > Dokument-DB > SQL-Datenbank) ist auch ein Kriterium.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

TomL

Re: Kombination aus Datum und Tag (Label) persistieren

Beitrag von TomL » 31.08.2019 14:58:08

Hier mal ein Vorschlag, wie ich das Problem lösen würde.
  1. Eine Sqlite-DB entsprechend meiner Vorstellungen erstellen. Das würde ich mit diesem Admin-Tool tun, was ich seit gut 10 Jahren als Top-Tool einschätze.
  2. sqlite-odbc installieren, wenns aktueller ist von C.Werner, sonst aus dem Repo
  3. mithilfe simpelster Kommandos die DB füllen, mit beliebigen geeigneten Werkzeugen, z.b. via Bash

    Code: Alles auswählen

    echo "INSERT INTO Umsaetze (Buchungstext, Verwendungszweck) VALUES( 'verplempert', 'zum vergnügen');" | isql -v MeineCashDB
  4. Komfortabel auswerten mit LibreOffice (Base u/o Calc nach Bedarf, Lust und Laune
Der Zielkonflikt wäre also: Einfache Datenerfassung vs. einfache Datenauswertung.
Für meine Ansprüche hätte ich damit für beide Aspekte das optimale erreicht.

Benutzeravatar
paedubucher
Beiträge: 855
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Kombination aus Datum und Tag (Label) persistieren

Beitrag von paedubucher » 31.08.2019 15:05:12

TomL hat geschrieben: ↑ zum Beitrag ↑
31.08.2019 14:58:08
Hier mal ein Vorschlag, wie ich das Problem lösen würde.
  1. Eine Sqlite-DB entsprechend meiner Vorstellungen erstellen. Das würde ich mit diesem Admin-Tool tun, was ich seit gut 10 Jahren als Top-Tool einschätze.
  2. sqlite-odbc installieren, wenns aktueller ist von C.Werner, sonst aus dem Repo
  3. mithilfe simpelster Kommandos die DB füllen, mit beliebigen geeigneten Werkzeugen, z.b. via Bash

    Code: Alles auswählen

    echo "INSERT INTO Umsaetze (Buchungstext, Verwendungszweck) VALUES( 'verplempert', 'zum vergnügen');" | isql -v MeineCashDB
  4. Komfortabel auswerten mit LibreOffice (Base u/o Calc nach Bedarf, Lust und Laune
SQLite ist sicher eine gute Option als eingebettete Datenbank. Die DB müsste ich aber auf jeden Fall mittels Skripts initialisieren, da zur Evaluation der Applikation von einer anderen Person (Informatiker) nur git clone und docker-compose up verwendet werden sollte.

Ich muss auch erwähnen, dass da noch ein kleines Backend (wahrscheinlich in Go) dazukommt, um der Web-Applikation eine REST-API anzubieten.

Die eingebettete Natur von SQLite ist in diesem Use-Case nicht so wichtig, zumal ich mit Docker eine beliebige DB mitliefern kann.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

reox
Beiträge: 2463
Registriert: 06.06.2006 22:09:47
Lizenz eigener Beiträge: MIT Lizenz

Re: Kombination aus Datum und Tag (Label) persistieren

Beitrag von reox » 01.09.2019 11:13:21

Wenn dein Anwendungsfall "Wie oft wurde X verwendet" der einzige ist, sollten files ausreichen, in denen du jedes Datum pro Zeile erfasst.
Dann ist die Auswertung ebenfalls sehr einfach.
Wenn du aber sowas brauchst wie "Wie oft wurde X zwischen dem dd.mm.yyyy und dd+n.mm+m.yyyy+y verwendet" wird die auswertung wieder komplizierter und ich würde das in SQL machen.
Wenn du dann noch die Umgekehrte Auswertung brauchst wie "Welche tags wurden am dd.mm.yyyy verwendet" dann sind die files zwar immer noch OK (da du einfach greppen kannst) aber solltest du hier auch datumsbereiche brauchen, wärst du vermutlich wieder bei SQL.
Key-Value stores bringen dir nur so lange was, solange du nur nach Keys suchst. Da müsstest du dir halt überlegen was für dich der Schlüssel ist. Sobald du beide Seiten als Schlüssel brauchst, degradieren die KV stores zu lookups mit O(n). (Außer du speicherst separat die selbe struktur nur anders herum)

Benutzeravatar
paedubucher
Beiträge: 855
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Kombination aus Datum und Tag (Label) persistieren

Beitrag von paedubucher » 01.09.2019 20:06:59

reox hat geschrieben: ↑ zum Beitrag ↑
01.09.2019 11:13:21
Wenn dein Anwendungsfall "Wie oft wurde X verwendet" der einzige ist, sollten files ausreichen, in denen du jedes Datum pro Zeile erfasst.
Dann ist die Auswertung ebenfalls sehr einfach.
Es soll grundsätzlich eine Art der Auswertung geben, die gebraucht wird, um den Phi-Koeffizienten zu berechnen. Dabei geht man immer von zwei Tags aus, und bildet dann vier Kategorien:
1. Anzahl Daten, für die weder Tag A noch B zutrifft
2. Anzahl Daten, für die Tag A aber nicht Tag B zutrift
3. Anzahl Daten, für die Tag A nicht aber Tag B zutrifft
4. Anzahl Daten, für die Tag A und Tag B zutreffen
reox hat geschrieben: Wenn du aber sowas brauchst wie "Wie oft wurde X zwischen dem dd.mm.yyyy und dd+n.mm+m.yyyy+y verwendet" wird die auswertung wieder komplizierter und ich würde das in SQL machen.
Datumsbereiche sind in meinem Zusammenhang nicht wichtig. Ich kann die Daten sogar als reine Strings ablegen und so lexikalisch sortieren (mit dem Format YYYY-MM-DD).
reox hat geschrieben: Wenn du dann noch die Umgekehrte Auswertung brauchst wie "Welche tags wurden am dd.mm.yyyy verwendet" dann sind die files zwar immer noch OK (da du einfach greppen kannst) aber solltest du hier auch datumsbereiche brauchen, wärst du vermutlich wieder bei SQL.
Das ist zwar kein eigentlicher Anwendungsfall, könnte aber für die Ergonomie der Applikation hilfreich sein, wenn ich etwa Tags nacherfassen möchte.
reox hat geschrieben: Key-Value stores bringen dir nur so lange was, solange du nur nach Keys suchst. Da müsstest du dir halt überlegen was für dich der Schlüssel ist. Sobald du beide Seiten als Schlüssel brauchst, degradieren die KV stores zu lookups mit O(n). (Außer du speicherst separat die selbe struktur nur anders herum)
Grundsätzlich brauche ich in meiner Applikation schon beide Richtungen.
1. Auflisten aller bestehenden Tags
2. Auflisten aller bestehenden Daten
Und dann kommt die eigentliche Auswertung, wofür ich jedes Tagesdatum wie oben beschrieben klassifizieren muss. In SQL-Begriffen könnte man das mit vier Abfragen realisieren. Bei einem Key-Value-Store müsste ich wohl alle Keys (Tagesdatum) auflisten lassen, und dann die beschriebene Logik auf jeden Tag anwenden. Bei Redis wäre da wohl ein Set und SISMEMBER eine mögliche Lösung.

Andererseits könnte man bei Redis auch mit Mengenoperationen arbeiten. Hierzu müssten die Tags als Key und die Tagesdaten als Values (Vektor von Tagesdaten) gespeichert werden. SINTER gäbe die Schnittmenge zurück (obige Kategorie 4), mit SDIFF könnte man A aber nicht B und B aber nicht A umsetzen (Kategorien 2 und 3). Das Problem wäre aber hier wiederum Kategorie 1, wozu man die Anzahl der Tagesdaten ermitteln und dann die anderen drei ermittelten Zahlen davon subtrahieren müsste. Hierzu wäre SQL sicher besser geeignet.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

reox
Beiträge: 2463
Registriert: 06.06.2006 22:09:47
Lizenz eigener Beiträge: MIT Lizenz

Re: Kombination aus Datum und Tag (Label) persistieren

Beitrag von reox » 01.09.2019 20:41:08

paedubucher hat geschrieben: ↑ zum Beitrag ↑
01.09.2019 20:06:59
Es soll grundsätzlich eine Art der Auswertung geben, die gebraucht wird, um den Phi-Koeffizienten zu berechnen. Dabei geht man immer von zwei Tags aus, und bildet dann vier Kategorien:
1. Anzahl Daten, für die weder Tag A noch B zutrifft
2. Anzahl Daten, für die Tag A aber nicht Tag B zutrift
3. Anzahl Daten, für die Tag A nicht aber Tag B zutrifft
4. Anzahl Daten, für die Tag A und Tag B zutreffen
Willst du berechnen ob es einen Zusammenhang zwischen A und B gibt?

Mh also ich denke in einem KV system müsstest du dann im Idealfall einmal Datum -> Tags und einem Tag -> Daten.
paedubucher hat geschrieben: ↑ zum Beitrag ↑
01.09.2019 20:06:59
Andererseits könnte man bei Redis auch mit Mengenoperationen arbeiten. Hierzu müssten die Tags als Key und die Tagesdaten als Values (Vektor von Tagesdaten) gespeichert werden. SINTER gäbe die Schnittmenge zurück (obige Kategorie 4), mit SDIFF könnte man A aber nicht B und B aber nicht A umsetzen (Kategorien 2 und 3). Das Problem wäre aber hier wiederum Kategorie 1, wozu man die Anzahl der Tagesdaten ermitteln und dann die anderen drei ermittelten Zahlen davon subtrahieren müsste. Hierzu wäre SQL sicher besser geeignet.
offenbar sind die Abfragen mit SINTER und SDIFF im Worstcase sehr langsam - wobei ich jetzt auch nicht weiß ob es das bei SQL nicht auch wäre. Ansonsten schaut das in redis ja eh nicht schlecht aus. Ich hab redis allerdings noch nie verwendet.

Benutzeravatar
paedubucher
Beiträge: 855
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Kombination aus Datum und Tag (Label) persistieren

Beitrag von paedubucher » 01.09.2019 21:31:24

@reox: Die redundante Speicherung wäre schon eine interessante Option, wobei sie natürlich auf den ersten Blick doch recht abenteuerlich anmutet. Aber da die Datenbank nur additiv ist, d.h. ständig Informationen eingefügt werden, und nicht entfernt oder modifiziert, könnte das ein doch gangbarer Weg sein. Als Experiment sicherlich interessant! Und als Fallback könnte ich dann immer noch SQL verwenden. Schlussendlich ist es ja ein Studienprojekt, wo die saubere Dokumentation des Scheiterns ebensoviele Punkte (und noch mehr Lerneffekt) bringt als eine lauffähige Applikation :wink:
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
paedubucher
Beiträge: 855
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Kombination aus Datum und Tag (Label) persistieren

Beitrag von paedubucher » 28.09.2019 20:01:25

Noch einmal ein Updates meinerseits: Ich konnte das Projekt jetzt erfolgreich mit Redis umsetzen. (siehe Repo auf GitHub)

Ich speichere jetzt pro User die Tags als Set ab:

Code: Alles auswählen

[username].[date] = Set[Tag1, Tag2, Tag3, ...]
Also beispielsweise:

Code: Alles auswählen

paedubucher.2019-09-28 = Set[Zugfahrt, Kaffee, Projektarbeit, Schlafen, Bier, Müdigkeit]
Mit der KEYS-Funktion von Redis kann ich dann mit dem Muster [username].* alle Daten eines Benutzers (Tage mit Journaleinträgen) finden. Das ist herrlich trivial.

Um die einzelnen Tags zu finden, mache ich keine Union in Redis, sondern lese einfach alle Einträge für einen Benutzer aus und packe die in ein Set (JavaScript).

Von daher hat sich die Wahl von Redis durchaus gelohnt!
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
Meillo
Moderator
Beiträge: 8813
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Kombination aus Datum und Tag (Label) persistieren

Beitrag von Meillo » 01.10.2019 22:28:56

Ich bin etwas spaet dran, habe aber noch eine Frage: Um welche Groessenordnungen von Tagen und Tags handelt es sich denn etwa. Also wieviele Tage werden es etwa werden und wieviele Tags?


Wenig ueberraschend, mag ich ja Unix-Tools. Dort gibt es alles was man braucht, um auf Basis von Textdateien Datenbankoperationen auszufuehren: grep, comm, sort, ... Ich denke, ich haette Lust, deine Anforderungen damit umzusetzen (wenn die erwarteten Groessenordnungen nicht zu gross sind). Du musst mir halt den noetigen Input liefern.
Use ed once in a while!

Benutzeravatar
paedubucher
Beiträge: 855
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Kombination aus Datum und Tag (Label) persistieren

Beitrag von paedubucher » 13.10.2019 18:48:02

@meillo: Solche Sachen finde ich auch immer sehr interessant mit den einfachsten Tools umzusetzen. Da es sich hierbei um eine Blockwoche an der Hochschule zum Thema Web-Anwendungen handelte, habe ich aber die Chance genutzt, mich mal mit ein paar populären Technologien vertraut zu machen. Redis landete für mich gerade in der goldenen Mitte zwischen Einfachheit und Trendtechnologie: Es ist eine Art Persistenzmechanismus für Datenstrukturen wie Listen, Maps, Sets und dergleichen; Legosteine der alten Sorte (orthogonal). Bei der Diskussion zu meinem Projekt sind wir noch auf zwei weitere Use-Cases für Redis gekommen: Message Queue und Time-Series-Datenbank.

Aber um auf deine Frage zu kommen: Das Mengengerüst ist und war in diesem Kontext völlig irrelevant. So dürfte der Flaschenhals nicht die Persistierung, sondern mein minimalistisches UI sein, das bei jeder Datenoperation komplett alles neu aufbaut. Ausserdem hängt die Anwendung nicht im Netzt, sondern wird derzeit lokal ausgeführt.

Aber strengen wir doch versuchshalber ein paar Überlegungen an: Pro Benutzer sollte man ca. zehn Einträge pro Tag rechnen. Das war etwa meine Grössenordnung im Selbstversuch für die Demonstration der Anwendung. Und so ein Tagebuch soll schon Monate bis Jahre geführt werden. Rechnen wir mal mit ca. 350 Tagesdaten mit je 10 Tags, also 3500 Einträge pro Benutzer. Pro Benutzer könnte man das gut in einer Datei ablegen, z.B. als CSV-Datei:

Code: Alles auswählen

2019-10-13:Ausschlafen,Kaffee,Sonnenschein,Spaziergang,Projektarbeit,Internet,Computerspiel,Wein,Lasagne,Mittagsschlaf
Das wären dann an die 120 Bytes pro Tag, sprich ca. 400 kB pro Jahr und Benutzer. Bei ca. 4 GB Memory könnte man die Daten von ca. 10'000 Benutzerjahren im Speicher halten.

Wenn man sowas mit Node.js implementiert, muss man das ganze I/O natürlich asynchron machen, sonst hängt das. Wenn man sowas wie Go nimmt, hätte ich da überhaupt keine Bedenken. Da ich aber vorher CSV getippt habe, würde mir spontan AWK als Lösung einfallen. Das dürfte dann schon eher in deine Richtung gehen, oder?
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Antworten