Heute geht es nochmals um Oracle12c: Oracle TEXT hat ein hochinteressantes neues Feature
dazubekommen: Und zwar die Unterstützung für XQuery Full Text. Damit bietet Oracle TEXT
nun wirklich eine vollständige Unterstützung für XML. Aber Moment mal: Konnte Oracle TEXT nicht
schon immer mit XML umgehen? Im Prinzip ja - aber einige XML-Besonderheiten wurden von Oracle TEXT
nicht unterstützt - zum Beispiel XML Namespaces. Dazu ein kleines Beispiel: Zunächst füllen
wir eine Tabelle mit ein paar XML-Dokumenten:
create table tab_xml (id number, docs xmltype) / /* * Das Dokument enthält ein Tag "tag" aus dem Namespace "http://mynamespaces.com/ns1" */ insert into tab_xml values ( 1, '<ns1:dokument xmlns:ns1="http://mynamespaces.com/ns1" xmlns:ns2="http://mynamespaces.com/ns2"> <ns1:tag att="attr1">Das ist ein Text</ns1:tag> </ns1:dokument>' ) / /* * Das Dokument enthält ein Tag "tag" aus dem Namespace "http://mynamespaces.com/ns2" * Also ein anderer Namespace als Dokument 1 */ insert into tab_xml values ( 2, '<ns1:dokument xmlns:ns1="http://mynamespaces.com/ns1" xmlns:ns2="http://mynamespaces.com/ns2"> <ns2:tag att="attr1">Das ist ein Text</ns2:tag> </ns1:dokument>' ) / /* * Das Dokument enthält ein Tag "tag" aus dem Namespace "http://mynamespaces.com/ns2" * Also gleicher Namespace wie Dokument 2 - ABER: anderer Präfix! */ insert into tab_xml values ( 3, '<ns_1:dokument xmlns:ns_1="http://mynamespaces.com/ns1" xmlns:ns_2="http://mynamespaces.com/ns2"> <ns_2:tag att="attr1">Das ist ein Text</ns_2:tag> </ns_1:dokument>' ) / commit /
Das Besondere an diesen Dokumenten sind die XML Namespaces. Die XML-Tags enthalten ein
sogenanntes Namespace-Präfix. Das ist aber noch nicht der Namespace selbst - denn oben im
ersten Tag wird dieser Präfix mit dem Attribut xmlns auf den tatsächlichen Namespace
abgebildet. Das erste Dokument definiert also zwei Namespaces: http://mynamespaces.com/ns1
und http://mynamespaces.com/ns2. Das XML-Tag names tag trägt den Präfix ns1,
gehört also zum Namespace http://mynamespaces.com/ns1. Im zweiten Dokument werden die
gleichen Namespaxes und auch die gleichen Namespace-Präfixe verwendet - nur gehört das Tag
tag hier zum anderen Namespace http://mynamespaces.com/ns2.
Im dritten Dokument kommt es nun: Auch hier werden die gleichen Namespaces verwendet,
aber mit anderen Präfixen. Das XML Tag tag trägt nun den Präfix ns_2 -
dieser ist aber auf den Namespace http://mynamespaces.com/ns2 gemappt - es ist
also tatsächlich das gleiche Tag wie in Dokument 2.
In XML kann der gleiche Namespace tatsächlich mit unterschiedlichen Präfixen angesprochen werden; wenn
man wissen möchte, ob zwei XML Tags identisch sind, reicht es also nicht aus, nur auf die
Präfixe zu schauen; man muss nachsehen, auf welche Namespaces diese Präfixe gemappt wurden. Und
genau das tut Oracle TEXT bis einschließlich Oracle11g nicht. Legen wir zunächst einen "klassischen"
Oracle TEXT-Index mit "XML-Unterstützung" an.
create index ft_tabxml on tab_xml (docs) indextype is ctxsys.context parameters ('section group ctxsys.path_section_group') /
Ein mit der PATH_SECTION_GROUP erzeugter Volltextindex eröffnet die Möglichkeit, mit
den CONTAINS-Operatoren INPATH und HASPATH zu arbeiten. Diese unterstützen XML auch ganz wunderbar;
man kann ganz ähnlich zu XPath arbeiten und auch Attribute werden unterstützt. Nur ... mit den
Eigenheiten der XML-Namespaces kann die PATH_SECTION_GROUP in Oracle11g nicht umgehen ...
select id from tab_xml where contains(docs, 'text INPATH(/ns1:dokument/ns2:tag)') > 0 / ID ---------- 2 1 Zeile wurde ausgewählt.
Es gibt von der Syntax her keine Möglichkeit, einen Namespace "richtig" anzugeben. Man kann nur
den Namespace-Präfix angeben (und der wird so behandelt, als wäre er Teil des Tag-Namens). Aber Oracle TEXT kann in 11g nicht nachsehen, auf welchen Namespace er
gemappt wurde und ob der gleiche Namespace noch von anderen Präfixen verwendet wird. Dokument 3 verwendet
einen anderen Präfix, daher wird es nicht gefunden - es müsste aber gefunden werden, denn es ist das
gleiche Tag. Doch Oracle12c schafft Abhilfe - zunächst muss der Index etwas anders erzeugt werden ...
begin ctx_ddl.create_section_group('my_sg_xquery', 'PATH_SECTION_GROUP'); ctx_ddl.set_sec_grp_attr('my_sg_xquery', 'xml_enable', 'true'); end; / sho err create index ft_tabxml on tab_xml (docs) indextype is ctxsys.context parameters ('section group my_sg_xquery') /
Vor dem CREATE INDEX muss eine eigene Section Group vom Typ PATH_SECTION_GROUP erstellt werden.
Das muss sein, denn bei dieser muss das Attribut XML_AWARE auf TRUE gestellt sein. Danach kann der
Index normal erzeugt werden.
Nun liegt ein Textindex vor, der auch XML-Namespaces vollständig unterstützt. Nicht erweitert wurde
dagegen die Syntax des CONTAINS-Operators - und das hat einen guten Grund: Denn für die Volltextsuche
in XML-Dokumenten gibt es inzwischen einen Standard - XQuery Full Text - und diesen sollte man
auch verwenden. Es machte also keinen Sinn, mit einer Erweiterung für CONTAINS neue, proprietäre Syntax
zu schaffen, man entschied sich, auf den Standard zu setzen. Und der sieht so aus:
SELECT id FROM tab_xml WHERE XMLExists('declare namespace ns="http://mynamespaces.com/ns2"; //ns:tag[. contains text "text"]' PASSING docs) /
Achtung: Wenn Ihr mit SQL*Plus arbeitet, kann euch das Semikolon in der dritten Zeile in die
Query kommen - SQL*Plus betrachtet die Abfrage als dort zu Ende. Das kann man mit einem set sqlterminator #
umstellen. Diese Abfrage findet nun alles, was gefunden werden soll ...
ID ---------- 2 3 2 Zeilen ausgewählt.
Schauen wir nun mal in die Indexstrukturen hinein. Wie immer bei Oracle TEXT, ist der Index
tatsächlich in den DR$-Tabellen abgelegt.
SQL> select tname from tab where tname like 'DR$%'
2 /
TNAME
-------------------------------------------------------
DR$FT_TABXML$R
DR$FT_TABXML$N
DR$FT_TABXML$K
DR$FT_TABXML$I
DR$FT_TABXML$E
DR$FT_TABXML$D
6 Zeilen ausgewählt.
Die XML-Unterstützung ist nun in den $D- und $E-Tabellen enthalten. Die $E-Tabelle kann
man lesen: Wie die folgende Abfrage zeigt, speichert Oracle TEXT dort alle XML-Tags mitsamt
Ihren Namespaces ab.
SQL> select * from DR$FT_TABXML$E / ID NAMESPACE LNAME NS ------------ ---------------------------- ------------ ---------------------------- _-_1 http://mynamespaces.com/ns1 dokument http://mynamespaces.com/ns1 _-_2 http://mynamespaces.com/ns1 tag http://mynamespaces.com/ns1 _-_3 http://mynamespaces.com/ns2 tag http://mynamespaces.com/ns2 3 Zeilen ausgewählt.
Die $D-Tabelle gehört eigentlich zum Save Copy Feature von Oracle TEXT, welches
hier gar nicht explizit angesprochen wurde. In dieser Tabelle speichert Oracle TEXT, aktiviert man
das Feature, gefilterte Versionen von Binärdokumenten ab, um Snippet- oder Markup-Operationen
schneller durchführen zu können (auch dazu gibt es noch ein Blog Posting). Ein "XML-Aware"
Textindex nutzt diese Tabellen aber ebenfalls für seine interne Verarbeitung. Die Spalte
DOC der $D-Tabelle enthält eine nochmals aufbereitete Variante des XML-Dokumentes.
Zusammenfassend kann man also sagen, dass Oracle TEXT in der Version 12c eine umfangreiche
und wirklich komplette Volltextsuche für XML anbietet - mit der Unterstützung für XQuery Full Text.
Weitere Informationen zum Thema findet Ihr im XML DB Developers' Guide: Indexing XML Data for Full-Text Queries. Viel Spaß beim Ausprobieren.