![]() Home Grundsätzliches Installation Konfiguration Erste Schritte Laufzeitschalter Textdateien Systemfunktionen Tabellenfunktionen Indexfunktionen Volltext-Indizierung Memos und Blobs Semaphoren-Konzept Support Datenbank Einführungskurs |
VolltextindizierungAnders als die normale Indizierung, bei der der Inhalt einzelner Felder als Ganzes aus der Datenbank extrahiert und in einer speziellen, geordneten Struktur abgelegt wird, wird bei der Volltextindizierung der Feldinhalt weiter aufgespalten in einzelne Wörter. Diese Wörter werden in einer eigenen Tabelle abgelegt, und eine Relation aus dieser Tabelle (der Stichwortliste) und der Ausgangstabelle bildet schließlich den Volltextindex.Braucht man das überhaupt?Nun kann man einwenden, daß ein Volltextindex überflüssig ist, schließlich kann man ja den Datenbestand auch sequentiell durchsuchen. Die sequentielle Suche hat sogar noch den Vorteil, daß man nach Wortfragmenten suchen kann...Stimmt! Die sequentielle Suche, bei der jeder Datensatz gelesen und nach den betreffenden Informationen durchforstet wird, ersetzt im Prinzip jede Form der indizierten (oder einer sonstigen trickreichen) Suche. Und wenn das Lesen der Daten und die Verarbeitung der Informationen keine Zeit benötigen würde, könnte man sich in der Tat die Entwicklung anderer Suchfunktionen ersparen. Aber dem ist leider nicht so... Es ist sogar noch viel schlimmer. Denn selbst wenn die Rechnergeschwindigkeit um irgendeinen beliebigen Faktor gesteigert wird, bleibt die sequentielle Suche immer nur auf kleine Informationsbestände beschränkt, wenn das Suchergebnis in einer vorgegebenen Zeit vorliegen muß. Klar doch!Die Volltextindizierung ersetzt, was die Suche nach Wörtern bzw. Wortbestandteilen betrifft, die sequentielle Suche in Datenbanken. Sie macht diese in größeren Datenbeständen sogar erst möglich, den sie liefert ihre Ergebnisse in sehr kurzer Zeit – unabhängig vom Umfang der Datenbank. Beispielsweise wären alle Suchmaschinen im Internet ohne Volltextindizierung überhaupt nicht denkbar.Ich möchte sogar noch einen Schritt weitergehen und behaupten, daß Sie, nachdem Sie die Volltextindizierung zum ersten Mal eingesetzt haben, nicht mehr wissen, wie Sie vorher ohne diese Funktion überhaupt auskommen konnten. Volltextindex mit der tdbengineDer VDP bietet einen Teil der Volltextfunktionen der tdbengine über die Programmoberfläche an. Den gesamten Funktionsumfang kann man sich jedoch nur über die EASY-Programmierung erschließen.Die Voraussetzung für die Volltextindizierung einer Tabelle besteht im Vorliegern zweier Tabellen: Stichworttabelle, die im ersten Feld eine Zeichenkette enthält
und automatisch numeriert wird
Weil diese Tabellen recht häufig benötigt werden, bietet die tdbengine zu deren Erzeugen zwei Sonderfunktion der allgemeinen Funktion MakeTable: GenList erzeugt eine neue Stichworttabelle
GenList(Dateinname,Zeichenzahl,Modus) Damit wird eine neue, leere Tabelle unter dem Dateinamen angelegt. Die Struktur dieser Tabelle ist, wenn kein Modus oder Modus=0 angegeben wurde: [STRUCTURE]
Für den Modus=1 ergibt sich ein kleiner Unterschied: [STRUCTURE]
GenRel(Tabelle1,Tabelle2,Dateiname) legt eine leere Relationstabelle mit folgender Struktur an [STRUCTURE]
Hinweis für Experten: Die Ausgangstabelle muß weder automatisch numeriert werden, noch ein Relationsfeld zur Stichworttabelle enthalten! Beide Tabellen werden normalerweise über die üblichen Tabellenfunktionen weder gelesen noch geschrieben. Denn dafür sind die speziellen Volltextfunktionen der tdbengine zuständig: Volltextindext neu erstellenScanRecs(D,I,R,Fields(Felder),ExtABC,Cut,Kontraindex,Modus,Step,MaskenFeld,DynKontraIndex) : REALNicht erschrecken über dieses Funktionsmonster: Alle Parameter mit Ausnahme der ersten vier sind optional! D Tabellennummer der Ausgangstabelle
Fields ist ist eine Spezialfunktion, über die die Information, welche Felder in den Volltextindex aufgenommen werden, an die Funktion übergeben werden. Die Feldkombination kann sowohl statisch als auch dynamisch erfolgen. Im Falle der CGI-Programmierung ist nur die dynamische Version möglich, weil hier ja zur Übersetzzeit keine Tabellen geöffnet sind. Statische Version: Fields(Feld1,Feld2,Feld3...)
Es sind alle Felder der Tabelle (mit Ausnahme von Blobs) zugelassen. Datums-, Zeit- und Zahlenfelder werden in Zeichenketten umgewandelt. Bei Auswahlfeldern wird die entsprechende Textkonstante in den Volltext übernommen. Bei String-Feldern (Zeichenketten) gibt es eine wichtige Ausnahme: Beginnt ein String-Feld mit dem "#"-Zeichen, so wird der Rest der Zeichenkette als Pfad zu einem externen Textdokument interpretiert und dieses in den Volltextindex aufgenommen. Diese Version der tdbengine unterstützt externe ASCII, ANSI und HTML-Dokumente. Zusätzlich können die Inhalte ADL-verknüpfter Datensätze über die L-Feld-Notation und über die R-Feld-Notation in den Volltextindex aufgenommen werden. Beispiel: Eine einfache Volltextindizierung für eine Adreßtabelle adr.dat VARDEF D,I,R,fc,N : REAL
Dieses Beispiel enthält alle wesentlichen Bestandteile inklusive skizzierter Fehlerbehandlung. Darauf sollten Sie achten:
Zunächst werden sowohl Stichwort- als auch Relationstabelle geleert. Dann werden sämtliche Datensätze der Ausgangstabelle gelesen. Bei jedem Datensatz werden die unter Fields angegebenen Felder gescannt, d.h. in einzelne Zeichenketten zerlegt, die nur aus Buchstaben bestehen. Jeder dieser Wörter wird nun in die Stichworttabelle eingetragen, wenn es dort nicht bereits vorhanden ist. Schließlich erfolgt ein Eintrag in die Relationstabelle mit je einem Verweis auf den Datensatz der Ausgangstabelle und dem zugrhörigen Satz in der Stichworttabelle. Die optionalen Parameter:ExtABCist eine Zeichenkette, die zusätzlich zu den Buchstaben als Wortbestandteile gelten sollen. Wenn beispielsweise auch die Postleitzahl in den Volltextindex aufgenommen werden sollen, so muß hier "0123456789" stehen. Tip: Der Bindestrich sollte nicht als Bestandteil eines Wortes aufgenommen werden (außer in Spezialfällen), denn damit werden die einzelnen Bestandteile eines (mit Bindestrichen) zusammengestzten Wortes nicht mehr (so einfach und schnell) über die Volltextsuche auffindbar. Bei den nächsten beiden Parametern geht es um die Einschränkung der Volltextindex. In vielen Fällen will man nämlich informationsarme Wörter wie "und" "der" u.a. nicht in den Volltextindex aufnehmen. Cut wird hier ein Wert größer 0 angegeben, so werden Wörter, die öfter als Cut vorkommen, nicht in den Volltextindex aufgenommen. Kontraindex ist der Name eine externen Textdatei, in der jene Wörter enthalten sind, die nicht in den Volltextindex aufgenommen werden. Dabei steht jedes Wort in einer eigenen Zeile, eine Sortierung ist nicht notwendig. Der nächste Parameter definiert das grundsätzliche Verhalten der tdbengine bei Aufbauu des Volltextindex. Die einzelnen Modi werden einfach addiert: Modus 0 Die Stichworttabelle wird
neu erstellt
*) Dieser Modus ist nur bedingt kompatibel zum relationalen System der tdbengine. Er ist aber immer dann mit großem Vorteil einzusetzen, wenn der Zugriff auf die Stichworttabelle und die Relationstabelle ausschließlich über die Volltextfunktionen erfolgt. Zudem wird der benötigte Speicherplatz auf weit weniger als 50% reduziert und die Suchgeschwindigkeit signifikant erhöht. Step Dieser Parameter ist nur dann auf einen Wert ungleich 0 zu setzen, wenn wirklich sehr große Datenmengen bearbeitet werden. Es handelt sich dabei um den Speicherplatz, den die tdbengine währen des Indizierens vom Betriebssystem anfordert. Viel Speicherplatz bringt in diesem Fall eine erhöhte Bearbeitungsgeschwindigkeit. Allerdings kann eine zu hohe Speicheranforderung dazu führen, daß ein Teil davon vom Betriebssystem ausgelagert wird, was dann zu einem massiven Performance-Einbruch führt! Aber angenommen, sie haben ein 256 Mbyte-System, auf dem nur wenige speicherintensive Prozesse laufen so können Sie beispielsweise hier den Wert 100000000 (=100 Millionen) eingeben. Tip: Verändern Sie diesen Wert erst dann, wenn die Indizierungszeiten wirklich aus dem Rahmen fallen. Maskenfeld Hierbei handelt es sich um die Nummer eines Feldes der Ausgangsdatei, das eine 16-Bit-Integerzahl speichert (NUMBER,2). Der Inhalt dieses Feldes wird in den Volltextindex, genauer in die Relationstabelle (noch genauer: in den IN2 der Relationstabelle) aufgenommen und kann bei der Volltextsuche berücksichtigt werden. Ein Sonderfall besteht, wenn hier –1 angegeben wird, denn dann können die Maskenwerte direkt (durch Doppelpunkt abgetrennt) in der Feldkombination bei Fields angegeben werden. Die Auswertung bei der Volltextsuche geschieht dann dermaßen, daß hier eine Zahl angegeben wird und diese mit einem binären AND mit der im Volltextindex gespeicherten Maske verknüpft wird. Es werden dann nur solche Verknüpfungen gefunden, bei denen das binäre AND einen Wert ungleich 0 ergibt. Das klingt kompliziert und ist auch kompliziert, eröffnet aber sehr schöne Möglichkeiten. Dazu ein Beispiel. Angenommen Sie haben eine Adreßdatenbank, in der unterschiedliche Arten von Adressen gespeichert sind: Privatadressen, Firmenadressen, Behördern etc. Die Art der Adressen speichern Sie als Kennung in einem Integer-Feld ab: 1 Privat
Bei Adressen, die in mehrere Kathegiorien fallen, addieren Sie einfach diese Werte. Wenn Sie nun die Feldnummer dieses Feldes bei der Volltextindizierung mit angeben, so können Sie bereits bei der Suche jede beliebige Einschränkung bezüglich der Kathegorien machen, ohne daß hierzu ein einziger Datensatz gelesen werden muß! Alternativ zur Auswertung eines Maskenfeldes können Sie auch direkt bei Fields Maskenzahlen vergeben. In diesem Fall hat die Maskenfeldnummer den Wert –1, und die Maskenzahlen werden direkt nach den Feldnamen angegeben. Dazu wieder ein Beispiel: ScanRecs(D,I,R,Fields("Vorname:1, Name:2, Strasse:4, Land:8, PLZ:8, Ort:8, Bemerkung:16"),"",0,"",0,0,-1) Auch hier wird ein Volltextindex aufgebaut. In die Relationstabelle wird jedoch, wenn es sich bei dem gerade untersuchten Feld um Vorname handelt, zu jedem Eintrag die Maskenzahl 1 aufgenommen, für das Feld Name die Zahl 2 usw. Bei der Volltextsuche können Sie dann gezielt in einzelnen Feldern oder Feldkombinationen suchen (oder natürlich auch in allen Feldern gleichzeitig)! DynKontraIndex Hierbei handelt es sich um einen dynamischen, feldbezogenen Kontraindex, der wiederum über die Funktion Fields tranportiert wird. Das bedeutet, daß bei jedem Datensatz zunächst die hier stehenen Felder gelesen werden und daraus ein Kontraindex gebildet wird. Dieser Kontraindex gilt allerdings nur für diesen Datensatz, beim nächsten beginnt das Spiel von vorne. FehlerbehandlungDie Parameter erlauben ein sehr flexibles Volltextindizieren beliebiger Datenbestände. Allerdings handelt es sich bei ScanRecs um eine Tabellenfunktion, mit der gleich zwei Tabellen gleichzeitig verändert werden, und entsprechend pingelig ist das System bei der Einhaltung der richtigen Syntax. Die Funktion liefert, falls kein Fehler auftritt, die Anzahl der Einträge in die Relationstabelle. Andernfalls wird ein Laufzeitfehler ausgelöst, den Sie mit .EC 1 abfangen können.Die häufigsten Fehler: 50: (Unbekannter Bezeichner) In Fields steht mindestens ein falsches
Datenfeld
Ist das Funktionsergebnis negativ, wird zwar kein Laufzeitfehler ausgelöst, aber die Funktion dennoch abgebrochen -115 Zu wenig Rechte für die Funktion (Die Relationsdatei
konnte nicht geleert werden)
Einzelne Datensätze indizierenDie Funktions ScanRecs indiziert eine kompette Tabelle. Es ist aber auch möglich einen einzelnen Datensatz in einen bestehenden Volltextindex aufzunehmen. ScanRec indiziert den aktuellen Datensatz einer Tabelle.ScanRec(D,I,R,Fields(Felder),ExtABC,Cut,Kontraindex,Modus,Step,MaskenFeld,DynKontraIndex) : REAL Die Parameter sind genau dieselben wie bei ScanRecs. Allerdings werden nur folgende Modi unterstützt: 2 Es werden keine neuen Wörter in die Stichworttabelle
aufgenommen
Wichtig: Der Modus 4 kann nur verwendet werden, wenn der Volltextindex mit ScanRecs in diesem Modus erzeugt wurde. Das Funktionsergebnis liefert die Anzahl der neu hinzugekommenen Verknüpfungen bzw. einen der folgenden Fehlercodes: -56 In Fields(...) fehlt (mindestens) ein Komma
Den Volltextindex für einzelne Datensätze löschenEs ist auch möglich, einzelne Datensätze aus einem bestehenden Volltextindex herauszunehmen. Damit ist eine vollständig dynamische Verwaltung des Volltextindex möglich.UnScanRec(D,I,R,Fields(Felder),ExtABC,Cut,Kontraindex,Modus,Step,MaskenFeld,DynKontraIndex) : REAL Die Parameter sind exakt dieselben wie bei ScanRec. Das Funktionsergebnis
liefert die Anzahl der gelöschten Verknüpfungen (falls positiv)
bzw. den Fehlercode.
Die Suche im VolltextindexZur Auswertung eines Volltextindex gibt es zwei Funktionen: MarkTable und MarkBits. Sie haben nahezu die gleichen Parameter.MarkTable(D,I,Suchstring,ExtABC,Ops,MaskenZahl,R1,R2,...) : REAL
D
Nummer der Ausgangstabelle
Das Funktionergebnis ist die Anzahl der gefundenen Datensätze (falls positiv) bzw einer der Fehlercodes: -64 ungültige Stichworttabelle
Die letzten beiden Fehler können mit TDB_ErrorMsg und TDB_ErrorOfs näher bestimmt werden. Beim Suchstring handelt es sich um eine Zeichenkette mit folgendem Aufbau: Suchstring ::= Ausdruck
Ein Suchwort ist eine Zeichenkette aus Buchstaben, den Zeichen aus ExtABC sowie den Sonderzeichen "?" und "*". Dabei steht "?" als Platzalter für genau ein beliebiges Zeichen, "*" für beliebig viele Zeichen. Folgende Operatoren sind vordefiniert (wenn in Ops ein Leerstring übergeben wird): Oder-Operator + (Vereinigungsmenge,
logisches ODER)
Beispiele:Meier findet alle Datensätze die das Wort Meier in beliebiger Groß- oder Kleinschreibung enthalten.Mei*er findet die Datensätze, die Wörter enthalten, die mit Mei beginnen und mit er enden. Hans, Meier im Datensatz muß sowohl das Wort Hans als auch Meier enthalten sein. Hans+Meier findet Datensätze in denen Hans oder Meier oder beides vorkommt. Hans-Meier findet Datensätze, in denen zwar Hans vorkommt, Meier aber nicht. OpsIm Parameter Ops können die Operatoren umdefiniert werden. Dazu muß hier eine Zeichenkette mit genau drei Zeichen übergeben werden. Das erste Zeichen ist dann der Operator für logisches UND, das zweite entspricht dem logischen ODER, deas dritte schließliche dem logischen UND NICHT.MaskenzahlDie Angabe einer Maskenzahl ist nur dann sinnvoll, wenn bereits bei der Volltextindizierung mit einem Maskenfeld oder mit Maskenkonstanten gearbeitet wurde. Die hier angegebene Zahl und die in der Verknüpfung gespeicherten Zahl werden mit einem binären AND verknüpft. Es werden nur solche Datensätze gefunden, bei denen das binäre AND einen Wert ungleich 0 ergibt.Sonderfall: Die Maskenzahl 0 liefert alle Verknüpfungen. Beispiel: Die Volltextindizierung erfolgte mit folgender Indexbeschreibung: Fields(Name:1,Vorname:2,Straße:4) Die Suche nach Hans liefert dann je nach Maskenzahl 0 In (wenigstens) einem der Felder ist das Wort enthalten
BitfeldIn diesem Parameter unterscheiden sich die beiden Suchfunktionen. Bei MarkTable werden die Treffer direkt in die Markierungsliste der Tabelle eingetragen. Bei MarkBits hingegen werden die Treffer in ein Bitfeld übertragen. Das ist dann von Vorteil, wenn mehrfach gesucht wird und die einzelnen Suchergebnisse zu einem Gesamtergebnis zusammengestzt werden.Beispiel: VARDEF Treffer, Temp : TBITS[1000000] MarkBits(...,Treffer,...)
Hinweis: Da EASY (derzeit) keine dynamische Dimensionierung von Feldern zuläßt, muß das Bitfeld ausreichend groß dimensioniert werden. Andererseits sollten die Felder auch nicht extrem überdimensioniert werden, da eine schnelle Berabeitung nur erfolgt, wenn die Felder in den Hauptspeicher passen (und nicht vom Betriebssystem ausgelagert werden). Der Speicherbedarf in Bytes kann leicht berechnet werden, indem man die Feldgröße duch 8 teilt. Ein Bitfeld für eine Tabelle mit 1000000 (=1 Million) Datsätzen belegt etwa 120 KByte. Expertenhinweis: Ein Bitfeld, wie wir es hier einsetzen, kann auch als charakteristische Mengenfunktion aufgefaßt werden, also eine Abbildung der Menge aller Datensätze einer Tabelle in die Menge {0,1}. Es handelt sich dabei um eine sehr effiziente Implementierung von (Teil-)Mengen, da die Mengenfunktionen dann direkt (als fundamentale Maschinenoperationen) ausgeführt werden. Auch MarkTable arbeitet intern mit einem Bitfeld und bildet erst am Ende der Funktion die gefundene Menge auf die Markierungsliste ab. Um auf die Treffer einer Suche zuzugreifen, gibt es mehrere Möglichkeiten: Abbildung auf die Markierungsliste Nach PutMarks(D,Treffer) stehen die Treffer in der Markierungsliste und können wie üblich weiterverarbeitet werden. Direkter Zugriff auf das Bitfeld i:=0
Die RelationstabellenBeide Suchfunktionen können (gleichzeitig) in mehreren Relationen (zwischen der Ausgangstabelle und der Stichworttabelle) suchen. Im Normalfall wird jedoch nur eine Relationstabelle angegeben.Ein Szenario für zwei Relationstabellen könnte etwa folgendermaßen
aussehen: Eine Tabelle für ein Textarchiv enthält eine Reihe
von Informationen in gewöhnlichen Datenfeldern und einen Verweis auf
ein Textdokument, das im HTML-Format vorliegt. In diesem Fall kann es sinnvoll
sein, für den Volltextindex über die externen Textreferenzen
eine eigene Relationstabelle zu erstellen.
Beispiel: DokumentenarchivWir gehen von folgender (vereinfachter) Struktur einer Tabelle aus[STRUCTURE]
Im Feld Dokument sind Referenzen wie #/home/kern/doc/beispiel_001.html abgelegt. Über diese Tabelle wurde ein Volltextindex erzeugt mit ScanRecs(...,Fields(Autor:1,Titel:2,Kurzinhalt:4,Dokument:8),"",0,"",4+8) Modus 4 (nur benötigte Dateien) + 8 (Unterstützung von externen HTML-Dateien) Unser Suchformular hat folgenden Aufbau: <form action=... method="post">
Damit erlauben wir dem Anwender die Eingabe eines Suchbegriffs und geben ihm die Auswahl, in welchen Bereichen der Begriff gesucht werden soll. PROCEDURE Dokument-Suche
|