Die Lexer Einstellungen beeinflussen die Art der Speicherung von Texten und den Zugriff auf den Textindex. Stand Datenbank Version 11g gibt es unterschiedliche Lexer-Typen wie zum Beispiel BASIC_LEXER, MULTI_LEXER und WORLD_LEXER. Die Definitionen zu den unterschiedlichen Lexer-Typen sind in Kurzfassung in der Tabelle zu finden.
Wir wollen uns im folgenden Blogeintrag mit dem MULTI_LEXER beschäftigen. Der MULTI_LEXER ist eine Art "Container" für verschiedene Sub-Lexer. Die Nutzung kann in multi-lingualen Umgebungen sinnvoll sein. Im Gegensatz zum WORLD_LEXER erfordert der MULTI_LEXER allerdings eine zusätzliche Language Spalte. Dies bedeutet die Tabellen müssen als zusätzliche Metadateninformation eine Spalte mit der Sprachzugehörigkeit beinhalten.
Nehmen wir als Beispiel eine Tabelle, die nicht nur deutsche sondern auch englische Dokumente abspeichert. Das unterschiedliche Vorkommen wird mit einem Eintrag in einer Language Spalte SPRACHE dokumentiert. Es sollen dabei unterschiedliche Spracheigenschaften wie zum Beispiel Groß-Kleinschreibung und unterschiedliche Stopplisten berücksichtigt werden. Dazu definieren wir folgende Präferenzen für einen deutschen und einen englischen Lexer.
Wir wollen uns im folgenden Blogeintrag mit dem MULTI_LEXER beschäftigen. Der MULTI_LEXER ist eine Art "Container" für verschiedene Sub-Lexer. Die Nutzung kann in multi-lingualen Umgebungen sinnvoll sein. Im Gegensatz zum WORLD_LEXER erfordert der MULTI_LEXER allerdings eine zusätzliche Language Spalte. Dies bedeutet die Tabellen müssen als zusätzliche Metadateninformation eine Spalte mit der Sprachzugehörigkeit beinhalten.
Nehmen wir als Beispiel eine Tabelle, die nicht nur deutsche sondern auch englische Dokumente abspeichert. Das unterschiedliche Vorkommen wird mit einem Eintrag in einer Language Spalte SPRACHE dokumentiert. Es sollen dabei unterschiedliche Spracheigenschaften wie zum Beispiel Groß-Kleinschreibung und unterschiedliche Stopplisten berücksichtigt werden. Dazu definieren wir folgende Präferenzen für einen deutschen und einen englischen Lexer.
execute ctx_ddl.drop_preference('global_lexer'); execute ctx_ddl.drop_preference('english_lexer'); execute ctx_ddl.drop_preference('german_lexer'); begin ctx_ddl.create_preference('english_lexer', 'basic_lexer'); ctx_ddl.set_attribute('english_lexer','SKIPJOINS','_'); ctx_ddl.set_attribute('english_lexer','mixed_case','no'); ctx_ddl.create_preference('german_lexer', 'basic_lexer'); ctx_ddl.set_attribute('german_lexer','mixed_case','yes'); ctx_ddl.set_attribute('german_lexer','composite','german'); ctx_ddl.set_attribute('german_lexer','alternate_spelling','german'); end; /
Zusätzlich werden nun auch sprach-spezifische Stopplisten erzeugt. Beim Anlegen des Index, wird je nach Dokumentsprache, das entsprechende Stoppwort ausgesondert.
execute ctx_ddl.drop_stoplist('mymulti_stoplist'); begin ctx_ddl.create_stoplist('mymulti_stoplist', 'MULTI_STOPLIST'); ctx_ddl.add_stopword('mymulti_stoplist', 'den','german'); ctx_ddl.add_stopword('mymulti_stoplist', 'this','english'); end; /
Nun definieren wir die MULTI_LEXER Präferenz.
execute ctx_ddl.create_preference('global_lexer','multi_lexer');
Im nächsten Schritt müssen die sprach-spezifischen Lexer mit einem ADD_SUB_LEXER Aufruf hinzugefügt werden. Wichtig ist eine Default Sprache zu definieren - in unserem Fall Deutsch.
begin ctx_ddl.add_sub_lexer('global_lexer', 'default', 'german_lexer'); ctx_ddl.add_sub_lexer('global_lexer', 'english','english_lexer', 'eng'); end; /
Nun erzeugen wir die Tabelle mit der zusätzlichen Language Spalte SPRACHE und fügen einige Einträge hinzu.
DROP TABLE globaldoc PURGE; CREATE TABLE globaldoc ( doc_id NUMBER, sprache VARCHAR2(30), text CLOB); INSERT INTO globaldoc values (10,'english','this is not America'); INSERT INTO globaldoc values (12,'english','this is user_110'); INSERT INTO globaldoc values (13,'english','this or that'); INSERT INTO globaldoc values (1,'deutsch','Wer den Pfennig nicht ehrt'); INSERT INTO globaldoc values (2,'deutsch','das ist user_111'); INSERT INTO globaldoc values (5,'deutsch','München'); INSERT INTO globaldoc values (6,'deutsch','Muenchen'); COMMIT;
Nun wird der Index erzeugt.
DROP INDEX globalx FORCE; CREATE INDEX globalx ON globaldoc (text) indextype is ctxsys.context parameters ('lexer global_lexer language column sprache stoplist mymulti_stoplist');
Um mehr über den Index zu erfahren, listen wir die Tokenliste auf. Der Eintrag USER110 ist zu finden, allerdings nicht USER111. Das ist ein Beispiel für die sprach-spezifische Präferenz SKIPJOINS.
SQL> SELECT token_text FROM dr$globalx$i; TOKEN_TEXT ---------------------------------------------------------------- 111 AMERICA IS Muenchen München NOT OR Pfennig THAT USER110 Wer das den ehrt ist nicht user
Zum Abfragezeitpunkt untersucht der MULTI_LEXER die Sessionsprache und nutzt die entsprechende Sub-Lexer Präferenz und die aktive stoplist zum Parsen der Abfrage. Falls die Sprache nicht gesetzt ist, wird der Default Lexer verwendet. Um die Abfrage auf eine bestimmte Sprache einzuschränken, kann eine zusätzliche Abfrage-Erweiterung auf die Language Spalte SPRACHE sinnvoll sein. Folgende Abfragen dokumentieren das Verhalten.
ALTER SESSION SET NLS_LANGUAGE='GERMAN'; -- wegen MIXED_CASE -- SElECT * FROM globaldoc WHERE contains(text, 'Pfennig')>0; -- oder SELECT * FROM globaldoc WHERE contains(text, 'Pfennig')>0 AND sprache='deutsch'; DOC_ID SPRACHE TEXT ---------- --------------- ----------------------------------- 1 deutsch Wer den Pfennig nicht ehrt SELECT * FROM globaldoc WHERE contains(text, 'PFENNIG')>0; no rows selected SELECT * FROM globaldoc WHERE contains(text, 'pfennig')>0; no rows selected -- wegen alternate_spelling SELECT * FROM globaldoc WHERE contains(text, 'Muenchen')>0; DOC_ID SPRACHE TEXT ---------- --------------- ----------------------------------- 5 deutsch München 6 deutsch Muenchen -- nun englische Dokumente ALTER SESSION SET NLS_LANGUAGE=ENGLISH; -- wegen MIXED_CASE SELECT * FROM globaldoc WHERE contains(text, 'AMERICA')>0; DOC_ID SPRACHE TEXT ---------- --------------- ----------------------------------- 10 english this is not America SELECT * FROM globaldoc WHERE contains(text, 'america')>0; DOC_ID SPRACHE TEXT ---------- --------------- ----------------------------------- 10 english this is not America SELECT * FROM globaldoc WHERE contains(text, 'America')>0; DOC_ID SPRACHE TEXT ---------- --------------- ----------------------------------- 10 english this is not America -- wegen SKIPJOINS und stoplist mit 'this' SELECT * FROM globaldoc WHERE contains(text, 'user110 or this')>0 AND sprache='English'; DOC_ID SPRACHE TEXT ---------- --------------- ----------------------------------- 12 english this is user_110 SELECT * FROM globaldoc WHERE contains(text, 'user110 or that')>0 AND sprache='English'; DOC_ID SPRACHE TEXT ---------- --------------- ----------------------------------- 12 english this is user_110 13 english this or that