Nach dem der Katalog nun erstellt und mit den Inhalten der Seiten des Webs gefüllt wurde, steht der Benutzung desselben nun nichts mehr im Weg.
Für den Anfang habe ich eine einfache lineare Wortsuche mit einem sehr einfachen Bewertungssystem gewählt. Um Binde- und Füllwörter udgl. von der Suche auszuschließen, verwende ich eine sogenannte Stoppwort-Liste die als einfache Textdatei vorliegt. So kann diese sehr einfach bearbeitet werden. Diese Textdatei wird in ein string-Array geladen und sortiert. Die Abfrage des Array erfolgt mit der statischen Methode BinarySearch der Array-Klasse, was eine sehr schnelle Lösung darstellen dürfte.
Um die Ergebnisse in einem Objekt abzubilden wird die gleiche Struktur SiteEntry verwendet, wie auch schon zur Abbildung einer Inhaltseite im Katalog. Ich habe bereits in Teil 3 auf die mehrfache Verwendung der SiteEntry Struktur hingewiesen.
Um die einzelnen Ergebnisse aufzunehmen, wird noch ein Container dafür benötigt.
Dieser Container, ich habe ihn SearchResult genannt, muss mehrere Voraussetzungen erfüllen. Er soll zum einen leicht über Code zu bearbeiten sein, also Elemente hinzufügen, löschen, sortieren udgl., aber auch an Datensteuerelemente gebunden werden können. Herausgekommen ist dabei diese Klasse.

Wie schon Eingangs erwähnt, verwende ich erst einmal eine einfache Wortsuche, die sich sequentiell durch den Katalog arbeitet und dabei die Wörter aus der Stoppwort-Liste übersprungen werden. Die Methode, welche die eigentliche Suche ausführt, befindet sich in der Singleton-Instanz der Klasse SiteSearch die ich in diesem Artikel kurz vorgestellt habe, und gibt eine Instanz der SearchResult-Klasse zurück.
Um die Relevanz des Ergebnis zu werten habe ich mir ein einfaches Punktesystem überlegt, dass noch ausgebaut werden kann. Für den Moment wird jeder Treffer im Titel mit 10 und im Text mit 5 Punkten gewertet.
Wenn als z.B. in einer Seite mit dem Titel “Shared Memory in verwaltetem Code” nach sharedmemory gesucht wird und im Text auch noch beide Wörter vorkommen, erhält dieses Suchergebnis 30 Punkte. Jeweils 10 für beide Suchwörter im Titel und nochmals jeweils 5 für die Treffer im Text. Dabei spielt die Anzahl des Vorkommen eines Worts im Text keine Rolle, sondern nur das es vorhanden ist. Würde die Anzahl gewertet werden und ein Wort währe sehr oft im Text enthalten, entstünde ein falsches Bild der Relevanz da eine andere Seite die mehrere Suchwörter enthält schlechter gewertet würde.
public SearchResult ExecuteSearch(string term)
{
// wenn noch eine SearchResult-Instanz aktiv ist
if (this.searchResult != null)
{
// Instanz zerstören
this.searchResult.Dispose();
this.searchResult = null;
}
// Zeichenfolge in ein Array splitten
string[] searchTerms = term.Split(new char[] { ' ' },StringSplitOptions.RemoveEmptyEntries);
// Wenn keine Wörter enthalten
if (searchTerms.Length < 1)
{
// eine neue leere instanz zurückgeben
return new SearchResult();
}
// neue Instanz erzeugen
this.searchResult = new SearchResult();
// durch den Volltextkatalog iterieren
foreach (var entry in SiteSearch.GetInstance().fulltextCatalog)
{
// neue Instanz der SiteEntry-Struktur erzeugen
SiteEntry resultEntry = new SiteEntry();
// durch die einzelnen Wörter der Suche iterieren
foreach (var word in searchTerms)
{
// prüfen ob das Wort in der Stopword-Liste enthalten ist
if (Array.BinarySearch(SiteSearch.stopwords, word) > 0)
{// suchwort in der Stoppwortliste enthalten,// weiter mit dem nächsten Suchwortcontinue;
}
// prüfen ob das Suchwort im Titel vorhanden ist
if (entry.Title.ToUpperInvariant().Contains(word.ToUpperInvariant()))
{resultEntry.Title = entry.Title;
// Suchwort im Titel enthalten; 10 PunkteresultEntry.Rank += 10;
}
// prüfen ob das Suchwort im Inhalt enthalten ist
if (entry.Content.ToUpperInvariant().Contains(word.ToUpperInvariant()))
{// wenn noch kein Content dem Ergebnis hinzugefügt wurdeif (string.IsNullOrEmpty(resultEntry.Content)){ resultEntry.Content = SiteSearch.GetFragment(entry.Content);}
// Wort enthalten; 5 PunkteresultEntry.Rank += 5;
// prüfen ob der Titel noch leer istif (string.IsNullOrEmpty(resultEntry.Title)){ // Titel hinzufügen resultEntry.Title = entry.Title;}
// prüfen ob die Url und das Datum schon gespeichert wurden.if (resultEntry.Url == null){ // Datum und Url speichern resultEntry.PubDate = entry.PubDate; resultEntry.Url = entry.Url;}
}
}
// Wenn der Titel nicht leer ist
if (!string.IsNullOrEmpty(resultEntry.Title))
{
// dem Ergebnis hinzufügen
this.searchResult.Add(resultEntry);
}
}
// Ergebnisse nach Relevanz sortieren
this.searchResult.Sort();
// Ergebnis zurückgeben
return this.searchResult;
}Im Moment ist die Suche noch sehr naiv. Es werden keine Wortvarianten, wie etwa außer JS auch JavaScript und JScript oder ähnliches, erkannt. Auch werden keine Tippfehler berücksichtigt. Doch um ein Konzept zu zeigen, dürfte die Ausführung allemal ausreichen.
Was noch fehlt, ist die Ausgabe des Ergebnis auf einer ASP.NET-Seite, doch das sollte kein Problem darstellen. Das zu bindende Objekt ist vorhanden und der Rest sollte Routine sein.
Ich lasse diese Artikelreihe hier so offen stehen, denn vieleicht folgt ja noch die ein oder andere Ergänzung wie etwa die Verwendung eines Thesaurus oder ähnlichem.
Wenn ihnen der Artikel gefallen hat oder er für sie hilfreich war, bitte "kicken" sie ihn.
