D7WS HOMEPAGE WORKSHOP NEUE XTRAS GALERIE WORKSHOP Automatic Translation LESERSERVICE BUCH NEWS

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




 

 

 

 





Directorworkshop.de ist © Joachim Gola & Gerd Gillmaier 1998-2002. Alle Rechte vorbehalten.