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.

SearchResult Diagram

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.

Technorati-Tags:  |  |  | 
Wenn ihnen der Artikel gefallen hat oder er für sie hilfreich war, bitte "kicken" sie ihn.
kick it on dotnet-kicks.de