Datenbank¶
Überblick¶
Für die Speicherung der Daten verwendet der VLS die Datenbank Firebird. Bei Firebird handelt es sich um ein Open-Source relationales DBMS, das für unterschiedliche Betriebssysteme verfügbar ist, u.a. Linux und Windows. Außerdem läßt es sich leicht installieren und verwalten. Zum Einsatz kommt die aktuelle Version 2.5.x.
Weitere Informationen zu Firebird findet man auf der Projektseite unter http://www.firebirdsql.org/ .
Für den direkten Zugriff auf Firebird kommt u.a. Flame Robin in Frage: http://www.flamerobin.org/ , das ebenfalls für Linux und Windows verfügbar ist.
Verbindung¶
Die Verbindungseigenschaften von VLS zur Firebird-Datenbank lassen sich
in der server.ini
konfigurieren:
[database]
# activate database component
on = boolean(default=true)
# skip tcp-ip server use direct file connection
direct = boolean(default=False)
# host/ip of firebird server
host = string(default=127.0.0.1)
# file to the .fdb file
file = path(noCreate, default=/this/is/not/a/valid/fdb/path)
role = string(default=vlServer)
user = string(default=sysdba)
password = password(default=masterkey)
charset = string(default=AUTO)
# we will create the db, if its local and doesnt exist
isLocal = checkLocalDatabase(default='')
# minimum needed database version - will trigger autoupdate on startup
minVersion = string(default=3.5.6)
# ping database server periodically - and reconnect if connection problem
ping = boolean(default=false)
# cp thread independent connection pool size
poolSize = integer(default=10)
# supported: dirtyInsertTS (allow modification of OPR_Content.OC_InsertTS
behaviors = set(default='')
Datenstruktur¶
Bezeichnungen¶
Für Tabellennamen gibt es ein Bezeichnungsschema, um Daten aus
unterschiedlichen Bereichen zu separieren. Ein Tabellenname besteht aus:
<Bereich>_<Name>
.
Als Bereiche sind bisher definiert:
laufende beim Betrieb anfallende Daten = OPeRational (
OPR
)Benutzerdaten = PRiVate (
PRV
)Systemdaten = SYStem (
SYS
)Konfigurationsdaten = ConFiGuration (
CFG
)Temporäre Daten = TeMPorary (
TMP
)
Bei Tabellen, die Daten versionieren, endet die Kurzbeschreibung auf
History
.
Alle Feldnamen werden mit jeweils einem eindeutigen Namen benannt.
Der Feldname besteht aus: <Prefix>_<Name>
. Der Prefix besteht aus dem
ersten Buchstaben des Bereichskürzel und einer Abkürzung der
Tabellen-Kurzbeschreibung (In der Regel aus dem ersten Buchstaben davon.
Mehrere Buchstaben werden verwendet, wenn die Abkürzung bereits vergeben
wurde). Für History-Tabellen kommt als letzter Buchstabe ein H dazu.
Beispiele:
Tabelle
OPR_Tree
-> Prefix OTTabelle
OPR_TopStructure
-> Prefix OTSTabelle
PRV_UserHistory
-> Prefix PUH
OPR_Tree¶
Die zentrale Tabelle für die operativen Daten ist OPR_Tree
. Die
meisten anderen OPR-Tabellen verbinden ihre Datensätze über einen
Fremdschlüssel mit dieser Tabelle und ergänzen die Daten.
Über den Inhalt von OT_OT_Parent
wird eine Baumstruktur aufgebaut. Ein
Datensatz auf oberster Ebene innerhalb einer Domain hat keinen Parent
(OT_OT_Parent is NULL
). OT_Path
enthält den ausgeflachten Pfad in
der Form |ID1||ID2||ID3||ID4|
. Über OT_Path
lassen sich Abfragen
ausführen, die alle Nachfahren ab einem bestimmten Baumknoten
zurückliefern:
select
*
from
OPR_Tree
where
OT_Path starting with '|ID1||ID2||'
Ein Datensatz wird anhand von zwei Merkmalen typisiert:
Datentyp/(Unter-)Typ (OT_RCV_Datatype
, OT_RCV_Type
). Hauptsächlich
bestimmt der Datentyp, welche anderen OPR-Tabellen zusätzliche Daten
enhalten müssen oder enthalten können. Zum Beispiel sind Seiten vom
Datentyp 3
und enthalten genau einen Eintrag in OPR_Page
. Metadaten
(Bücher, Zeitschriften, …) sind vom Datentyp 1
und enthalten genau
einen Eintrag in OPR_Content
.
Welche Kombinationen aus Datentyp/Typ erlaubt sind wird auch von der
Serverkonfiguation der Types (config/types.ini
, typesspec.ini
)
bestimmt.
Struktur von OPR_Tree
Feldname |
Beschreibung |
---|---|
OT_ID |
Primärschlüsssel |
OT_OD_ID |
Domain (Sammlung), in die dieser Datensatz enthalten ist. Fremdschlüssel auf OPR_Domain.OD_ID. |
OT_OT_Parent |
Elternknoten innerhalb der Baumstruktur. Fremdschlüssel auf OT_ID (Null erlaubt). |
OT_OT_Link |
Verlinkter Datensatz. Zeigt auf die OT_ID des Originals. |
OT_RCV_Datatype |
Datentyp |
OT_RCV_Type |
Typ innerhalb des Datentyps |
OT_Caption |
Text zur Anzeige im Baum |
OT_Sort |
Sortierreihenfolge innerhalb von Geschwisterknoten (Daten mit gleichem Elternknoten). |
OT_DelState |
Lösch-Status: 0 nicht gelöscht, 1 gelöscht (im Papierkorb) |
OT_Path |
Flache Baumpfad Information mit dem aufgelösten Inhalt von OT_ID und OT_Parent. Inhalt: |<oberster OT_Parent>||…||<OT_Parent>||<OT_ID>| Beispiel: |23||144||6008| |
OT_State |
Freigabestatus: 0 nicht freigegeben, 1 freigegeben |
OT_InsertTS |
Zeitpunkt, an dem der Datensatz in die Tabelle eingefügt wurde. |
OT_UpdateTS |
Zeitpunkt, an dem der Datensatz zuletzt geändert wurde. |
OPR_Content¶
Die Tabelle OPR_Content
verwaltet bibliographische Daten (Metadaten)
für Datensätze vom Datentyp 1
. Die Verbindung zu OPR_Tree
wird über
den Fremdschlüssel OC_OT_ID
hergestellt. Änderungen an OPR_Content
werden abhängig von den geänderten Feldern in OPR_ContentHistory
,
OPR_ContentXmlHistory
oder OPR_Comment
versioniert.
Struktur von OPR_Content
Feldname |
Beschreibung |
---|---|
OC_ID |
Primärschlüssel |
OC_OT_ID |
1:1 Fremdschlüssel nach OPR_Tree |
OC_OT_MetaParent |
OT_ID des nächsten Eltern-Metadaten innerhalb der Baumstruktur in OPR_Tree |
OC_MpMode |
Typ von OT_OT_MetaParent: 0 keine Eltern-Metadaten vorhanden, 1 Eltern-Metadaten vorhanden, 2 Kind-Metadaten vorhanden, 3 Eltern- und Kind-Metadaten vorhanden |
OC_OT_Deliverer |
OT_ID des Deliverers (Ablieferer, Verlag) für diese Metadaten. Kann nur für Metadaten, die keinen Metadaten-Parent enthalten, eingestellt werden. |
OC_OT_Titlepage |
OT_ID der Seite, die als Titelseite für diese Metadaten z. B. auf der Webseite Titelinfo angzeigt wird. |
OC_OT_TitlepageMode |
Typ von OC_OT_Titlepage: 0 Eintrag automatisch gesetzt, 1 Eintrag von Hand gesetzt |
OC_SourceIdent |
Primäre Katalog-ID (oder eine andere externe ID) dieser Metadaten |
OC_Provider |
Wenn vorhanden, benutze den sekundären SRU-Provider aus |
OC_Signature |
Entweder manual ausgewählte/eingegebene Signatur (über VLM) oder beim Import aus |
OC_PhysicalLocation |
Entweder manual ausgewählter/eingegebener Standort (über VLM) oder beim Import aus |
OC_MediaNumber |
Aus |
OC_Owner |
Besitzende Bibliothek |
OC_PageCount |
Aus |
OC_PageCache |
Anzahl der enthaltenen Seiten (wird vom PagecountJob bestimmt. Zählt enthaltene nicht gelöschte Seiten(Knoten vom Datentyp 3)) |
OC_Comment |
Auftrags-Kommentar. Nicht sichtbar im Web-Frontend. |
OC_OrgForm |
Web-Kommentar. Enthält reinen Text (ohne Html-Auszeichnungen). |
OC_WebInfo1 |
HTML-Web-Kommentar. Enthält Html formatierten Text. |
OC_XML |
Bibliographische Metadaten im Mods-Format. |
OC_XmlOrigin |
Herkunft der bibliographischen Metadaten in OC_XML (s. u.) |
OC_SC_OrderDate |
Über VLM gesetzt, OrderDateJob oder Import-Verhalten (Bedeutung: Startdatum des Digitalisierungs-Prozesses) |
OC_SC_DeliveryDate |
Dieses Datum kann automatisch während des Import-Vorganges aus der |
OC_ReleaseDate |
Das Datum, an dem dieses Metadatum zum ersten Mal freigegeben wurde (erstes Mal OT_State auf 1 gesetzt). |
OC_MaxAccessCount |
Lehrbuchsammlung: Anzahl der vorhandenen Kopien. Maximale Anzahl der gleichzeitigen Nutzer, die sich die Seiten dieses Titels in der Lehrbuchanwendung anschauen können. |
OC_PresentationUrl |
url to source presentation for harvested records (extracted from METS in dv:presentation) |
OC_ReferenceUrl |
url to source reference for harvested records (extracted from METS in dv:reference) |
OC_OaiIdent |
OAI-Id, falls der Datensatz über das OAI-Harvesting importiert wurde. |
OC_EMail |
E-Mail Adresse für Hochschulschriften/Open Access Funktionalität |
OC_GenerateIdentifier |
Die Generierung von IDs (URN, DOI, …) einschalten. Wird nur überprüft, wenn |
OC_TextState |
Volltext-Status von Kindknoten (in der Regel nur von Seiten): 0 keine Texte in Kindern enthalten, 1 Kinder enthalten Volltexte oder Wikitexte |
OC_InsertTs |
Zeitpunkt, an dem der Datensatz in die Tabelle eingefügt wurde. |
OC_UpdateTs |
Zeitpunkt, an dem der Datensatz zuletzt geändert wurde. |
OC_XmlOrigin |
Bedeutung |
---|---|
0 |
unspecific |
1 |
online catalogue (vlz) |
2 |
batch catalogue (.mab, .pica, .mods, .marc) |
3 |
oai harvest |
4 |
fs-import (proto record) |
5 |
csv/xls import |
10 |
local manually edited |
11 |
local autocreated from struct |
12 |
simple file upload (SOURCEIDENT.pdf) |
13 |
created via mods forms |
14 |
vlm upload |
15 |
identifier upload |
OPR_Page¶
Die Tabelle OPR_Page
verwaltet die Eigenschaften der Digitalisate der
einzelnen Seiten eines Metadatums. Mit dem Feld OP_OT_ID
wird eine 1:1
Verbindung zu den Daten aus OPR_Tree (mit dem Datentyp 3
)
hergestellt. Gleichbleibende Daten aus dem Scan-Prozess werden
zusammengefasst in OPR_ImageProfile gespeichert.
Struktur von OPR_Page
Feldname |
Beschreibung |
---|---|
OP_ID |
Primärschlüssel |
OP_OT_ID |
1:1 Fremdschlüssel nach OPR_Tree |
OP_Url |
Url auf das Digitalisat im Archiv ( |
OP_State |
Status vom Digitalisat: 0 nicht definiert (z. B. kein Digitalisat vorhanden), 1 existiert (im lokalen Archiv), 2 gelöscht (nach Kapsel-Export), 3 gespiegelt (remote url, not implemented yet), 4 extrahierte PDF Seite |
OP_Comment |
Bemerkung zur Seite |
OP_OIP_ID |
Fremdschlüssel auf die Informationen in OPR_ImageProfile |
OP_Ident |
Import-Identifier |
OP_ImgWidth |
Breite in Pixel |
OP_ImgHeight |
Höhe in Pixel |
OP_ImgDateTime |
Zeitstempel vom Scan-Prozess, aus den Bild-Metadaten (tiff-metadatatags) extrahiert |
OP_ImgPageName |
Seitenname vom Scan-Prozess aus den Bild-Metadaten (tiff-header) |
OP_Filesize |
Dateigröße (in Bytes). Dieser Wert wird nicht verändert, wenn die Datei nach Kapsel-Export gelöscht wird (OP_State = 2) |
OP_ModificationTimestamp |
Zeitpunkt der letzten Änderungen dieser Datei im Archiv |
OP_Webcache |
Kommaseparierte Liste von Webcache Bildbreiten |
OP_WebcacheSize |
Webcachegröße (in Bytes) als Summe über alle verfügbaren Webcache-Bilder |
OP_PaginationCaption |
Textwert der Paginierung (Überschrift) |
OP_PaginationType |
Typ der Paginatierung: siehe paginate.xml |
OP_PaginationValue |
Wert des Paginierungs-Zählers (unabhängig von der Ausgabe/Formatierung des Zähler, der abhängig vom Paginierungstyp ist) |
OP_IccState |
Status der ICC-Profil Anwendung: 0 nicht transformiert, 1 Webcache-Bilder sind in den sRGB-Farbraum transformiert |
OP_Hashsum |
Hashsumme des Digitalisats im Format: |
OP_ChangeTS |
Änderungszeitpunkt für die Felder: OP_Ident, OP_Comment, OP_ModificationTimestamp, OP_PaginationCaption, OP_PaginationType, OP_PaginationValue, OP_Hashsum |
OP_InsertTs |
Zeitpunkt, an dem der Datensatz in die Tabelle eingefügt wurde. |
OP_UpdateTs |
Zeitpunkt, an dem der Datensatz zuletzt geändert wurde. |
API¶
Die VLS-Datenbankschicht ist in vls.db
implementiert. Mit folgendem
Python-Import:
>>> from vls.api import db
steht der wichtigste Namespace im Modul zur Verfügung. Alternativ lässt
sich die API auch über component.vls.db
erreichen.
Ohne Transaktionskontext lässt sich die DB in Python über folgende API erreichen:
>>> sql = 'select first 2 OT_Caption from OPR_Tree where OT_RCV_DataType = :t'
>>> rs = db.selectQuery(sql, t=10)
>>> for r in rs: print(r)
<result set row with OT_CAPTION = domain obvsg>
<result set row with OT_CAPTION = domain ulbtirol>
>>> print('\n'.join(db.selectQuery(sql, t=10, map='OT_Caption')))
domain obvsg
domain ulbtirol
>>> db.updateQuery('delete from TMP_ID')
0
>>> db.batchQuery('insert into TMP_ID (TI_OT_ID) values(1);delete from TMP_ID')
Für Transaktionshandling stehen folgende Idiome zur Verfügung:
conn = db.getConn(autoCommit=False) # from pool or from request-thread
try:
# TMP_ID has only transaction-level visibility
conn.updateQuery('insert into TMP_ID (TI_OT_ID) values(1)')
# still there
affected = conn.updateQuery('delete from TMP_ID')
assert affected == 1
conn.commit()
finally:
db.releaseConn(conn)
Oder in einem Kontextmanager:
with db.conn() as conn:
# do something with conn
# auto-commit() if from pool, do not commit if from request-thread
# rollback on error
with db.conn(request=False) as conn:
# got a non-cp-request conn, got one from the pool
Cursor-basiert lässt sich auf folgender API arbeiten:
with db.selectCursorQuery(sql, **params) as (cur, prep):
for i, r in enumerate(cur.itermap()):
print(i, r)
with db.conn() as conn:
with conn.selectCursorQuery(sql, **params) as (cur, prep):
# ..
# beware! cursor will be closed if prepared
# statement is garbage collected
del prep # there goes the cursor
Wenn man gegen eine (große, >1499, Firebird „in ()“ Limit) Liste von IDs operiert:
sql = '''select OT_ID from OPR_Tree
inner join TMP_ID on TI_OT_ID = OT_ID'''
with db.conn() as conn:
db.fillTempIds(conn, otids, clear=True)
round_tripped = conn.selectQuery(sql, map='OT_ID')
assert set(otids) == set(round_tripped)
Eine Connection, die readonly/non-blocking arbeitet:
readonly = dict(isolationLevel=(db.isc_tpb_read_committed, db.isc_tpb_rec_version),
accessMode=db.isc_tpb_read)
with db.conn(request=False, **readonly) as conn:
# got a readonly non blocking conn from pool