Eine der interessanten Eigenschaften des Oracle TEXT-Index ist, dass die zu indizierenden
Dokumente nicht nur einfach aus einer Tabellenspalte, sondern aus unterschiedlichen
Data Stores kommen können.
- DIRECT_DATASTORE: der "Normalfall"
- MULTICOLUMN_DATASTORE: Mehrere Tabellenspalten (dazu hatten wir schon Blog-Postings
- FILE_DATASTORE: Die Dokumente liegen im Dateisysten; die Tabelle enthält für jedes Dokument einen vollständigen, absoluten Pfad
- URL_DATASTORE: Die Dokumente liegen im "Netzwerk"; die Tabelle enthält für jedes Dokument einen vollständigen, absoluten URL (FTP oder HTTP)
- DETAIL_DATASTORE: Die Dokumente liegen in einer anderen Tabelle, welche mit der zu indizierenden in einer Master-Detail-Beziehung steht
- NESTED_DATASTORE: Die Dokumente liegen in nicht direkt in der zu indizierenden Tabelle, sondern in einer Nested Table (dürfte selten vorkommen).
Und schließlich gibt es den USER_DATA_STORE, der quasi "alles kann". Und um den soll es in diesem Posting auch gehen. Als Ausgangspunkt haben wir eine Tabelle mit Dokumenten und diese sollen nicht direkt indiziert werden, sondern über einen USER_DATA_STORE. Warum? Weil wir über eine zusätzliche Spalte (INDEX_DOCUMENT) kontrollieren möchten, ob die Dokumente indiziert werden sollen oder nicht. Beginnen wir mit dem Tabellenaufbau ...
SQL> desc dokumente Name Null? Typ ----------------------------------------- -------- --------------------- ID NUMBER FILENAME VARCHAR2(2000) DOCUMENT BLOB INDEX_DOCUMENT CHAR(1)
Nun geht es ans Erstellen des Textindex mit dem User Datastore. Ein User Datastore bedeutet nichts
weiter als dass der Index seine Dokumente von einer PL/SQL-Prozedur zugewiesen bekommt und diese
eben nicht direkt aus der Tabelle holt. Da man in der PL/SQL-Prozedur programmieren kann, was man
möchte, ist das Verhalten des User Datastore auch sehr individuell. Dieses Beispiel soll (wie gesagt),
das Dokument normal indizieren, wenn die Spalte INDEX_DOCUMENT auf "Y" steht und gar nicht indizieren,
wenn die Spalte auf "N" steht. Beginnen wir mit der PL/SQL-Prozedur:
create or replace procedure dokument_uds_proc ( rid in rowid, tlob in out NOCOPY blob ) is begin begin select document into tlob from dokumente where rowid = rid and index_document = 'Y'; exception when NO_DATA_FOUND then tlob := null; end; end; /
Wichtig an dieser Prozedur ist die Signatur, diese muss genau so aussehen,
wie in diesem Beispiel vorgegeben. Die zu indizierende ROWID wird in die Prozedur
hineingegeben, das zu indizierende Dokument kommt als OUT-Parameter wieder zurück.
Man sieht, dass man das Dokument selbst nun beliebig zusammensetzen kann. Die
Inhalte können sehr wohl aus verschiedensten Tabellen oder anderen Datenquellen
kommen.
Aber bis jetzt ist die Prozedur nur eine normale PL/SQL-Prozedur. Damit sie für etwas
ORACLE Text etwas bewirken kann, muss sie zunächst in Oracle TEXT bekannt gemacht werden; die
folgenden PL/SQL-Blöcke richten den User Datastore im Dictionary von Oracle TEXT ein.
begin ctx_ddl.drop_preference('DOKUMENT_UDS'); end; / sho err begin ctx_ddl.create_preference('DOKUMENT_UDS','user_datastore'); ctx_ddl.set_attribute('DOKUMENT_UDS','procedure','dokument_uds_proc'); ctx_ddl.set_attribute('DOKUMENT_UDS','output_type','blob_loc'); end; / sho err
Bevor der Index nun erzeugt wird, fehlt noch eine Kleinigkeit: Der Index muss
in zwei Fällen aktualisiert werden: Erstens wenn sich das Dokument ändert
und zweitens wenn die Spale INDEX_DOCUMENT verändert wird. Wenn der Index
also auf die Spalte INDEX_DOCUMENT gelegt wird, benötigen wir noch einen Trigger,
der UPDATE-Operationen in DOCUMENT auf die Spalte INDEX_DOCUMENT überträgt:
create or replace trigger trg_uds_document before update on dokumente for each row begin :new.index_document := :new.index_document; end; /
Nun kann der Index erstellt werden. Wie schon beschrieben, wird
der Index auf die Spalte INDEX_DOCUMENT gelegt:
create index idx_text_dokumente on dokumente (index_document) indextype is ctxsys.context parameters ('filter ctxsys.auto_filter datastore dokument_uds memory 200M transactional') /
Wenn alle Einträge ein "N" in der Spalte INDEX_DOCUMENT haben, ist der Index
nach Erstellung immer noch leer. Setzt man eine oder mehrere Zeilen auf "Y",
so können diese anschließend dank des TRANSACTIONAL-Parameters (Posting!) gefunden werden,
das dauert aber sehr lange. Kein Wunder, denn der Index ist noch nicht synchronisiert;
die Dokumente in der Pending-Tabelle werden also on-the-fly durchsucht und
dabei muss natürlich auch die PL/SQL-Prozedur des User Datastore durchlaufen werden.
Nach einem CTX_DDL.SYNC_INDEX('IDX_TEXT_DOKUMENTE') befinden sich die Einträge
denn auch im Textindex.
Dieses einfache Beispiel zeigt, wie man einen User Datastore nutzen kann. Wichtig ist, dass
hier keine Grenzen existieren; die PL/SQL-Prozedur des UDS ist für Oracle TEXT
eine "Black Box"; man kann dort hineinprogrammieren, was man möchte ...