HowTo:
Indizierte
Textsuche
|
|
Home
Workshop-Seite
|
Unter den
vielen neuen Lingo-Funktionen der Director Version 7 findet sich
sowohl was die Volltextsuche als auch Datenbankanbindungen
angeht leider nichts Neues. Der Entwickler ist nach wie vor
entweder auf den Einsatz von Xtras oder die Programmierung der benötigten
Funktionen in Lingo angewiesen. Die Realisierung einer Volltextsuche
in Lingo galt vor einigen Jahren (und Director-Versionen) als kaum
realisierbar.
In der Zwischenzeit
sind die Hardware und auch Director deutlich schneller geworden
und es lohnt, die Grenzen neu abzustecken.
|


 |
|
|
Der Weg zu einer Lingo-Volltextsuche
mit akzeptablen Suchzeiten führt über eine Indizierung
der zu durchsuchenden Texte während des Authorings. Der Index
enthält für jedes Wort die Information, in welchem Textfeld
der Anwendung es vorkommt. Die Daten des Index können deutlich
schneller als Textfelder durchsucht werden, da sie sortiert sind,
jedes vorkommende Wort nur einmal enthalten und überflüssige
Wörter (z.B. "der", "die", "das",
"oder", "und", ...) ausgelassen werden können.
Der generierte Index wird in einer Liste gespeichert, da Director
auf Listen deutlich schneller zugreifen kann als beispielsweise
auf in Textfeldern gespeicherte Zeichenketten.
Für die Realisierung
eines Index wird zuerst eine Liste aller zu indizierenden Wörter
benötigt. Diese Liste wird mit dem folgenden Skript generiert,
für dessen kompletten Durchlauf Sie abhängig von
Anzahl und Umfang Ihrer Texte und natürlich der Performance
Ihres Rechners einiges an Zeit einplanen sollten. In den
meisten Fällen ist mit Zeiten im zweistelligen Minutenbereich
zu rechnen - eine Indizierung zur Laufzeit scheidet damit aus.
on makeWordList
starttimer
set gAllWordsL = []
set notThisL = exclude()
repeat with m = 1 to (the number of castmembers of castlib
"text" -1)
put m
repeat with w = 1 to the number of words
of field m of castlib "text"
set wrd = stripSpecChars (word
w of field m of castlib "text")
--nur, wenn wrd nicht in der
ausschlussliste steht...
if getPos(notThisL, wrd) = 0
then
add(gAllWordsL,
wrd)
end if
end repeat
end repeat
sort (gAllWordsL)
put count(gAllWordsL)
put "needed ticks: " && the timer
-- in externer Datei speichern
saveIndex string(gAllWordsL)
end makeWordList
|


 |
|
|
Um die Indizierung von
Trivialwörtern zu vermeiden, werden diese in einer eigenen
Liste gespeichert, die im Beispiel von der Prozedur exclude()
geliefert wird. (Diese besteht lediglich aus der Listendefinition
und einer return-Anweisung.)
In Director 7 kann die
so erzeugte Liste in einem Feld- oder Textdarsteller gespeichert
werden, da die in früheren Versionen vorhandene Beschränkung
auf 32 kB Text hier nicht mehr besteht. In Director 6 wird die Liste
mit Hilfe des im Lieferumfang von Director enthaltenen Xtras "FileIO"
in einer externen ASCII-Datei gespeichert. (Sofern Sie mehr als
4096 Elemente in der Liste speichern möchten, ist es ratsam,
die Liste in mehrere Teile zu zerlegen, da ein Bug in der Funktion
value() in Director 6 für die Verfälschung der Listenelemente
jenseits der Listenposition 4096 sorgt.
Die hier nicht im Detail
vorgestellte Prozedur stripSpecChars()
sorgt für die Filterung von Satzzeichen und Klammern, die Director
normalerweise als Bestandteil eines Wortes interpretiert.
Im zweiten Schritt wird die soeben generierte Wortliste indiziert.
Das heißt, für jedes Wort (also jedes Listenelement)
wird ermittelt, in welchem Textfeld es vorkommt.
|


 |
|
|
on makeIndex
set gMasterIndexList = [:]
set mx = count(gAllWordsL)
repeat with m = 1 to mx
set prop = getAt(gAllWordsL, m)
set val = []
addProp(gMasterIndexList, prop, val)
end repeat
--
set cmax = the number of members of castLib "text"
repeat with i = 1 count(gMasterIndexList)
set idxwrd = getPropAt(gMasterIndexList,
i)
set wrdlist = getAt(gMasterIndexList, i)
put idxwrd
repeat with fld = 1 to cmax
set afld = field fld of castlib
"text"
set wmax = the number of words
of afld
repeat with wrd = 1 to wmax
set actwrd = stripSpecChars
(word wrd of afld)
if idxwrd = actwrd
then
if getPos(wrdList,
fld) = 0 then add(wrdList, afld)
end if
end repeat
setAt(gMasterIndexList, i, wrdList)
end repeat
end repeat
sort (gMasterIndexList)
end makeIndex
Die Prozedur makeIndex
durchläuft zuerst alle Elemente der oben erzeugten Wortliste
und schreibt das aktuelle Wort als Property in die Indexliste. Als
zugehöriger Wert wird zunächst eine leere, lineare Liste
eingetragen. Darauf folgen drei verschachtelte Repeat-Schleifen,
die bewirken, daß für jedes Wort der Indexliste (Schleife
1) in jedem Feld der castlib
"text"
(Schleife 2) geprüft wird, ob jedes Wort des aktuellen Feldes
(Schleife 3) dem aktuellen Wort der Indexliste entspricht. Im Fall
der Übereinstimmung und sofern das Word noch nicht in der Liste
enthalten ist, wird die Nummer des aktuellen Darstellers in die
zum Darsteller gehörende lineare Liste eingetragen.
|


 |
|
|
Die zeitaufwendige Indizierung
läßt sich durch die Verwendung des Xtras "Textcruncher"
beschleunigen. Da desses Funktion
findall()
eine zu durchsuchende Zeichenkette als Parameter erwartet, müssen
erst alle Textfelder zu einer gemeinsamen Zeichenkette verknüpft
werden (Prozedur buildbigstring()),
wobei ein Trennzeichen zwischen den einzelnen Feldinhalten definiert
werden muß (hier "#").
on makeIndexTX
-- benštigt Xtra "TextCruncher"
set tm = the ticks
set bigString = buildbigstring()
set gMasterIndexList = [:]
set mx = count(gAllWordsL)
repeat with m = 1 to mx
set prop = getAt(gAllWordsL, m)
set val = []
addProp(gMasterIndexList, prop, val)
end repeat
repeat with i = 1 to count(gMasterIndexList)
set idxwrd = getPropAt(gMasterIndexList,
i)
set wrdlist = getAt(gMasterIndexList, i)
set charPos = findall(bigString, idxwrd)
if voidp(charPos) = 0 then
repeat with pos in charPos
-- 35 = code fuer "#"
set fldNum = getItemOfCharPosition(gBigList,
pos, 35)
if getPos(wrdList,
fldNum) = 0 then add(wrdList, fldNum)
end repeat
end if
setAt(gMasterIndexList, i, wrdList)
-- Ausgabe im Nachrichtenfenster
put idxwrd && i &" / " && mx && " / " &&
the timer & "for " & string(wrdList)
end repeat
sort (gMasterIndexList)
put "time: " && the ticks - tm
end makeIndexTX
|


 |
|
|
on buildbigstring
set theString = ""
repeat with m = 1 to the number of members of castLib
"text"
put field m of castLib "text" & "#" after
theString
end repeat
put length(theString)
return theString
end buildbigstring
Da
der Index jetzt als sortierte Propertyliste vorliegt, können
die Such-Prozeduren neben der Funktion getaprop()
auch die Funktion findPosNear()
verwenden, die das Finden von Listeneinträgen ermöglicht,
die dem Suchkriterium nur teilweise entsprechen.
Gerd
Gillmaier
|


 |
|