Der Fokus von OParl 1.0 liegt auf einem schnellen und effizienten Abruf von Daten. Dies beinhaltet auch das schnelle und effiziente Aktualisieren eines lokalen Bestandes. Bei einer täglichen Synchronisation kann so mit einer geringen Anzahl an Anfragen an den OParl-Server ein Update durchgeführt werden. Diese Funktionsweise und die Nutzung dieses Mechanismus soll in diesem Artikel genauer beleuchtet werden.
UPDATE: entstanden durch diese Diskussion auf Github wurden Teile des Artikels verändert, da wir dank der Weiterentwicklung der Ratsinformationssysteme nun einen besseren Update-Mechanismus nutzen können. Dadurch wird der Absatz „Modified-Handling bei eingebetteten Objekten“ obsolet.
Dies bedeutet auch, dass der Update-Mechanismus erst ab OParl 1.1 funktionieren wird. Mit OParl 1.1 werden wir Objektlisten aller Objekte von Body aus einführen, und alle Objekte erhalten die Attribute created und modified.
Erstabruf von Daten
Möchte man den gesamten Bestand aktualisieren, so muss man lediglich die von Body ausgehenden URLs aufrufen. Beginnt man also mit dem Abruf des Bodys:
$ curl -s https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1 | json_pp
so erhält man die gewünschten Links:
{
id: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1",
type: "https://schema.oparl.org/1.0/Body",
system: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/system",
name: "Gemeinde Vettweiß",
ags: "05358060",
contactEmail: "sdnetrim@kdvz-frechen.de",
contactName: "Gemeinde Vettweiß",
organization: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/organization",
person: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/person",
meeting: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/meeting",
paper: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/paper",
legislativeTerm: [ ],
location: {
id: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/location/0-1",
streetAddress: "Gereonstraße 14",
postalCode: "52391"
}
}
Um nun also für die erste Synchronisation alle Paper eines OParl Bodies abzurufen, ruft man die Paper-URL ohne Parameter auf:
$ curl -s https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/paper | json_pp
Als Antwort erhält man eine externe Objektliste (vgl. Kapitel 2.5.3), in der sämtliche Daten der Paper enthalten sind:
{
data: [
{
id: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/paper/13",
type: "https://schema.oparl.org/1.0/Paper",
body: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1",
name: "Unterschutzstellung des Bildstockes Ecke Triftstraße/Antoniusstraße in der Ortschaft Ginnick",
reference: "V-4/2007",
date: "2007-06-14",
paperType: "Vorlage",
mainFile: {
id: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/file/1-127",
type: "https://schema.oparl.org/1.0/File",
body: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1",
name: "Vorlage (Unterschutzstellung des Bildstockes Ecke Triftstraße/Antoniusstraße in der Ortschaft Ginnick)",
mimeType: "application/pdf",
accessUrl: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/files/rim4992/UGhVM0hpd2NXNFdFcExjZZLUWrBty0zU28z_p4UFrKwU9pu0Gz_iPO0hlnPSFDZ8/Vorlage_V-4-2007.pdf",
downloadUrl: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/files/rim4992/UGhVM0hpd2NXNFdFcExjZZLUWrBty0zU28z_p4UFrKwU9pu0Gz_iPO0hlnPSFDZ8/Vorlage_V-4-2007.pdf",
created: "2010-01-20T17:32:45+01:00",
modified: "2010-01-20T17:32:45+01:00"
},
consultation: [
{
id: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/consultation/8",
type: "https://schema.oparl.org/1.0/Consultation",
agendaItem: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/agendaitem/373",
meeting: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/meeting/257",
organization: [
"https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/organization/184"
]
},
{
id: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/consultation/10",
type: "https://schema.oparl.org/1.0/Consultation",
agendaItem: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/agendaitem/396",
meeting: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/meeting/249",
organization: [
"https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/organization/183"
]
}
],
created: "2010-01-20T17:32:45+01:00",
modified: "2010-01-20T17:32:45+01:00"
},
[...]
],
pagination: {
totalElements: 977,
elementsPerPage: 25,
currentPage: 1,
totalPages: 40
},
links: {
first: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/paper?page=1",
self: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/paper?page=1",
last: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/paper?page=40",
next: "https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/paper?page=2"
}
}
Mit den Links in dem pagination
-Block können dann die weiteren Seiten aufgerufen werden, so dass man Seite für Seite den kompletten Datenbestand herunterladen kann. Mit der stabilen Sortierung (vgl. Kapitel 2.5.4) wird sichergestellt, dass keine Elemente verloren gehen.
Update eines Datenbestandes
Da ein Ratsinformationssystem üblicherweise aus sehr vielen Objekten besteht, dauert eine Synchronisation wie die oben beschriebene doch etwas Zeit. Hat man also ein mal einen Bestand heruntergeladen, so möchte man nur noch die Änderungen Abfragen, die mit weit weniger Anfragen auskommen. Dies wird über die Filter (vgl. Kapitel 2.5.5) realisiert.
Hierzu speichert man Datum und Uhrzeit der letzten Synchronisation ab und nutzt dieses für die nächste Synchronisation als URL-Parameter. Da man alle Änderungen seit der letzten Synchronisation haben möchte, nutzt man den Parameter modified_since
:
$ curl -s https://sdnetrim.kdvz-frechen.de/rim4992/webservice/oparl/v1/body/1/paper?modified_since=2017-09-01T00%3A00%3A00%2B01%3A00 | json_pp
Als Antwort erhält man die oben beschriebene externe Objektliste, lediglich gefiltert um alle Objekte nach dem übergebenen Zeitpunkt, was für viel weniger Objekte und damit in vielen Fällen nur eine einzige Seite sorgt. Bei einem regelmäßigen Update sind so lediglich der Aufruf des Body
s und der davon abgehenden Objektlisten notwendig. Die so deutlich reduzierten Anfragen gegenüber einer vollen Synchronisation sparen Zeit und Ressourcen.
Empfehlungen für stabile Sortierung
Ein sauberes Update kann nur dann funktionieren, wenn die Paginierung stabil ist und sich so die Liste nicht zwischen zwei Abrufen verändert. Dies klingt einfacher, als es tatsächlich ist. Unsere Behandlung gelöschter Objekte vereinfacht dies jedoch erheblich. In der Praxis haben sich je nach Datenbank-System zwei Konzepte als sinnvoll herausgestellt:
- Sortierung nach ID: im Fall einer SQL-Datenbank mit Auto-Increment-Index (z.B. MySql und Postgres) eignet sich dieser Index für eine Sortierung, da neue Datensätze dann immer am Ende eingefügt werden.
- Sortierung nach
created
Wenn wie zum Beispiel in der NoSQL-Datenbank MongoDB kein Auto-Increment-Index zur Verfügung steht, eignet sich dascreated
-Attribut für eine Sortierung. Wenncreated
sauber implementiert wird, so ist ein neuer Wert in der Datenbank zwangsweise nach allen anderen, dacreated
nach der Erstellung des Objekts nicht mehr verändert werden darf.
Beachtet man die Regeln für gelöschte Werte nicht, so rutscht die gesamte Liste bei einem Löschvorgang zwischen zwei Client-Requests die gesamte Liste nach Vorne, und der Client bekommt Objekte nicht mit. Deswegen ist es absolut elementar, die gelöschten Objekte wie in der Spezifikation beschrieben einzusortieren.
Weitere Fragen und Antworten
Wenn bei dem Update-Mechanismus oder anderen Details der Spezifikation Fragen auftreten, so kommentieren Sie gerne diesen Blogbeitrag oder schreiben Sie eine Mail!